diff --git a/src/adapters/GmmxxAdapter.h b/src/adapters/GmmxxAdapter.h
index ff35800b2..b1d2206a5 100644
--- a/src/adapters/GmmxxAdapter.h
+++ b/src/adapters/GmmxxAdapter.h
@@ -51,8 +51,8 @@ public:
         columns.reserve(matrix.getEntryCount());
         
         for (auto const& entry : matrix) {
-            columns.emplace_back(entry.first);
-            values.emplace_back(entry.second);
+            columns.emplace_back(entry.getColumn());
+            values.emplace_back(entry.getValue());
         }
         
         std::swap(result->ir, columns);
diff --git a/src/counterexamples/MILPMinimalLabelSetGenerator.h b/src/counterexamples/MILPMinimalLabelSetGenerator.h
index 197db76ab..ad581e616 100644
--- a/src/counterexamples/MILPMinimalLabelSetGenerator.h
+++ b/src/counterexamples/MILPMinimalLabelSetGenerator.h
@@ -65,13 +65,13 @@ namespace storm {
              * A helper struct capturing information about the variables of the MILP formulation.
              */
             struct VariableInformation {
-                std::unordered_map<uint_fast64_t, uint_fast64_t> labelToVariableIndexMap;
-                std::unordered_map<uint_fast64_t, std::list<uint_fast64_t>> stateToChoiceVariablesIndexMap;
-                std::unordered_map<uint_fast64_t, uint_fast64_t> initialStateToChoiceVariableIndexMap;
-                std::unordered_map<uint_fast64_t, uint_fast64_t> stateToProbabilityVariableIndexMap;
-                uint_fast64_t virtualInitialStateVariableIndex;
-                std::unordered_map<uint_fast64_t, uint_fast64_t> problematicStateToVariableIndexMap;
-                std::unordered_map<std::pair<uint_fast64_t, uint_fast64_t>, uint_fast64_t, PairHash> problematicTransitionToVariableIndexMap;
+                std::unordered_map<uint_fast64_t, std::string> labelToVariableMap;
+                std::unordered_map<uint_fast64_t, std::list<std::string>> stateToChoiceVariablesMap;
+                std::unordered_map<uint_fast64_t, std::string> initialStateToChoiceVariableMap;
+                std::unordered_map<uint_fast64_t, std::string> stateToProbabilityVariableMap;
+                std::string virtualInitialStateVariable;
+                std::unordered_map<uint_fast64_t, std::string> problematicStateToVariableMap;
+                std::unordered_map<std::pair<uint_fast64_t, uint_fast64_t>, std::string, PairHash> problematicTransitionToVariableMap;
                 uint_fast64_t numberOfVariables;
 
 				VariableInformation() : numberOfVariables(0) {}
@@ -130,7 +130,7 @@ namespace storm {
                         bool allSuccessorsProblematic = true;
                         for (auto const& successorEntry : transitionMatrix.getRow(row)) {
                             // If there is a relevant successor, we need to add the labels of the current choice.
-                            if (stateInformation.relevantStates.get(successorEntry.first) || psiStates.get(successorEntry.first)) {
+                            if (stateInformation.relevantStates.get(successorEntry.getColumn()) || psiStates.get(successorEntry.getColumn())) {
                                 for (auto const& label : choiceLabeling[row]) {
                                     result.allRelevantLabels.insert(label);
                                 }
@@ -139,7 +139,7 @@ namespace storm {
                                     result.relevantChoicesForRelevantStates[state].push_back(row);
                                 }
                             }
-                            if (!stateInformation.problematicStates.get(successorEntry.first)) {
+                            if (!stateInformation.problematicStates.get(successorEntry.getColumn())) {
                                 allSuccessorsProblematic = false;
                             }
                         }
@@ -167,14 +167,15 @@ namespace storm {
              * @param relevantLabels The set of relevant labels of the model.
              * @return A mapping from labels to variable indices.
              */
-            static std::pair<std::unordered_map<uint_fast64_t, uint_fast64_t>, uint_fast64_t> createLabelVariables(storm::solver::LpSolver& solver, boost::container::flat_set<uint_fast64_t> const& relevantLabels) {
+            static std::pair<std::unordered_map<uint_fast64_t, std::string>, uint_fast64_t> createLabelVariables(storm::solver::LpSolver& solver, boost::container::flat_set<uint_fast64_t> const& relevantLabels) {
                 std::stringstream variableNameBuffer;
-                std::unordered_map<uint_fast64_t, uint_fast64_t> resultingMap;
+                std::unordered_map<uint_fast64_t, std::string> resultingMap;
                 for (auto const& label : relevantLabels) {
                     variableNameBuffer.str("");
                     variableNameBuffer.clear();
                     variableNameBuffer << "label" << label;
-                    resultingMap[label] = solver.createBinaryVariable(variableNameBuffer.str(), 1);
+                    resultingMap[label] = variableNameBuffer.str();
+                    solver.addBinaryVariable(resultingMap[label], 1);
                 }
                 return std::make_pair(resultingMap, relevantLabels.size());
             }
@@ -187,19 +188,20 @@ namespace storm {
              * @param choiceInformation The information about the choices of the model.
              * @return A mapping from states to a list of choice variable indices.
              */
-            static std::pair<std::unordered_map<uint_fast64_t, std::list<uint_fast64_t>>, uint_fast64_t> createSchedulerVariables(storm::solver::LpSolver& solver, StateInformation const& stateInformation, ChoiceInformation const& choiceInformation) {
+            static std::pair<std::unordered_map<uint_fast64_t, std::list<std::string>>, uint_fast64_t> createSchedulerVariables(storm::solver::LpSolver& solver, StateInformation const& stateInformation, ChoiceInformation const& choiceInformation) {
                 std::stringstream variableNameBuffer;
                 uint_fast64_t numberOfVariablesCreated = 0;
-                std::unordered_map<uint_fast64_t, std::list<uint_fast64_t>> resultingMap;
+                std::unordered_map<uint_fast64_t, std::list<std::string>> resultingMap;
                 
                 for (auto state : stateInformation.relevantStates) {
-                    resultingMap.emplace(state, std::list<uint_fast64_t>());
+                    resultingMap.emplace(state, std::list<std::string>());
                     std::list<uint_fast64_t> const& relevantChoicesForState = choiceInformation.relevantChoicesForRelevantStates.at(state);
                     for (uint_fast64_t row : relevantChoicesForState) {
                         variableNameBuffer.str("");
                         variableNameBuffer.clear();
                         variableNameBuffer << "choice" << row << "in" << state;
-                        resultingMap[state].push_back(solver.createBinaryVariable(variableNameBuffer.str(), 0));
+                        resultingMap[state].push_back(variableNameBuffer.str());
+                        solver.addBinaryVariable(resultingMap[state].back());
                         ++numberOfVariablesCreated;
                     }
                 }
@@ -215,10 +217,10 @@ namespace storm {
              * @param stateInformation The information about the states of the model.
              * @return A mapping from initial states to choice variable indices.
              */
-            static std::pair<std::unordered_map<uint_fast64_t, uint_fast64_t>, uint_fast64_t> createInitialChoiceVariables(storm::solver::LpSolver& solver, storm::models::Mdp<T> const& labeledMdp, StateInformation const& stateInformation) {
+            static std::pair<std::unordered_map<uint_fast64_t, std::string>, uint_fast64_t> createInitialChoiceVariables(storm::solver::LpSolver& solver, storm::models::Mdp<T> const& labeledMdp, StateInformation const& stateInformation) {
                 std::stringstream variableNameBuffer;
                 uint_fast64_t numberOfVariablesCreated = 0;
-                std::unordered_map<uint_fast64_t, uint_fast64_t> resultingMap;
+                std::unordered_map<uint_fast64_t, std::string> resultingMap;
                 
                 for (auto initialState : labeledMdp.getLabeledStates("init")) {
                     // Only consider this initial state if it is relevant.
@@ -226,7 +228,8 @@ namespace storm {
                         variableNameBuffer.str("");
                         variableNameBuffer.clear();
                         variableNameBuffer << "init" << initialState;
-                        resultingMap[initialState] = solver.createBinaryVariable(variableNameBuffer.str(), 0);
+                        resultingMap[initialState] = variableNameBuffer.str();
+                        solver.addBinaryVariable(resultingMap[initialState]);
                         ++numberOfVariablesCreated;
                     }
                 }
@@ -240,34 +243,36 @@ namespace storm {
              * @param stateInformation The information about the states in the model.
              * @return A mapping from states to the index of the corresponding probability variables.
              */
-            static std::pair<std::unordered_map<uint_fast64_t, uint_fast64_t>, uint_fast64_t> createProbabilityVariables(storm::solver::LpSolver& solver, StateInformation const& stateInformation) {
+            static std::pair<std::unordered_map<uint_fast64_t, std::string>, uint_fast64_t> createProbabilityVariables(storm::solver::LpSolver& solver, StateInformation const& stateInformation) {
                 std::stringstream variableNameBuffer;
                 uint_fast64_t numberOfVariablesCreated = 0;
-                std::unordered_map<uint_fast64_t, uint_fast64_t> resultingMap;
+                std::unordered_map<uint_fast64_t, std::string> resultingMap;
                 
                 for (auto state : stateInformation.relevantStates) {
                     variableNameBuffer.str("");
                     variableNameBuffer.clear();
                     variableNameBuffer << "p" << state;
-                    resultingMap[state] = solver.createContinuousVariable(variableNameBuffer.str(), storm::solver::LpSolver::BOUNDED, 0, 1, 0);
+                    resultingMap[state] = variableNameBuffer.str();
+                    solver.addBoundedContinuousVariable(resultingMap[state], 0, 1);
                     ++numberOfVariablesCreated;
                 }
                 return std::make_pair(resultingMap, numberOfVariablesCreated);
             }
             
             /*!
-             * Creates the variables for the probabilities in the model.
+             * Creates the variable for the probability of the virtual initial state.
              *
              * @param solver The MILP solver.
              * @param maximizeProbability If set to true, the objective function is constructed in a way that a
              * label-minimal subsystem of maximal probability is computed.
              * @return The index of the variable for the probability of the virtual initial state.
              */
-            static std::pair<uint_fast64_t, uint_fast64_t> createVirtualInitialStateVariable(storm::solver::LpSolver& solver, bool maximizeProbability = false) {
+            static std::pair<std::string, uint_fast64_t> createVirtualInitialStateVariable(storm::solver::LpSolver& solver, bool maximizeProbability = false) {
                 std::stringstream variableNameBuffer;
                 variableNameBuffer << "pinit";
-                
-                return std::make_pair(solver.createContinuousVariable(variableNameBuffer.str(), storm::solver::LpSolver::BOUNDED, 0, 1, 0), 1);
+                std::string variableName = variableNameBuffer.str();
+                solver.addBoundedContinuousVariable(variableName, 0, 1);
+                return std::make_pair(variableName, 1);
             }
             
             /*!
@@ -278,10 +283,10 @@ namespace storm {
              * @param stateInformation The information about the states in the model.
              * @return A mapping from problematic states to the index of the corresponding variables.
              */
-            static std::pair<std::unordered_map<uint_fast64_t, uint_fast64_t>, uint_fast64_t> createProblematicStateVariables(storm::solver::LpSolver& solver, storm::models::Mdp<T> const& labeledMdp, StateInformation const& stateInformation, ChoiceInformation const& choiceInformation) {
+            static std::pair<std::unordered_map<uint_fast64_t, std::string>, uint_fast64_t> createProblematicStateVariables(storm::solver::LpSolver& solver, storm::models::Mdp<T> const& labeledMdp, StateInformation const& stateInformation, ChoiceInformation const& choiceInformation) {
                 std::stringstream variableNameBuffer;
                 uint_fast64_t numberOfVariablesCreated = 0;
-                std::unordered_map<uint_fast64_t, uint_fast64_t> resultingMap;
+                std::unordered_map<uint_fast64_t, std::string> resultingMap;
                 
                 for (auto state : stateInformation.problematicStates) {
                     // First check whether there is not already a variable for this state and advance to the next state
@@ -290,19 +295,21 @@ namespace storm {
                         variableNameBuffer.str("");
                         variableNameBuffer.clear();
                         variableNameBuffer << "r" << state;
-                        resultingMap[state] = solver.createContinuousVariable(variableNameBuffer.str(), storm::solver::LpSolver::BOUNDED, 0, 1, 0);
+                        resultingMap[state] = variableNameBuffer.str();
+                        solver.addBoundedContinuousVariable(resultingMap[state], 0, 1);
                         ++numberOfVariablesCreated;
                     }
                     
                     std::list<uint_fast64_t> const& relevantChoicesForState = choiceInformation.relevantChoicesForRelevantStates.at(state);
                     for (uint_fast64_t row : relevantChoicesForState) {
                         for (auto const& successorEntry : labeledMdp.getTransitionMatrix().getRow(row)) {
-                            if (stateInformation.relevantStates.get(successorEntry.first)) {
-                                if (resultingMap.find(successorEntry.first) == resultingMap.end()) {
+                            if (stateInformation.relevantStates.get(successorEntry.getColumn())) {
+                                if (resultingMap.find(successorEntry.getColumn()) == resultingMap.end()) {
                                     variableNameBuffer.str("");
                                     variableNameBuffer.clear();
-                                    variableNameBuffer << "r" << successorEntry.first;
-                                    resultingMap[state] = solver.createContinuousVariable(variableNameBuffer.str(), storm::solver::LpSolver::BOUNDED, 0, 1, 0);
+                                    variableNameBuffer << "r" << successorEntry.getColumn();
+                                    resultingMap[successorEntry.getColumn()] = variableNameBuffer.str();
+                                    solver.addBoundedContinuousVariable(resultingMap[successorEntry.getColumn()], 0, 1);
                                     ++numberOfVariablesCreated;
                                 }
                             }
@@ -321,20 +328,21 @@ namespace storm {
              * @param choiceInformation The information about the choices in the model.
              * @return A mapping from problematic choices to the index of the corresponding variables.
              */
-            static std::pair<std::unordered_map<std::pair<uint_fast64_t, uint_fast64_t>, uint_fast64_t, PairHash>, uint_fast64_t> createProblematicChoiceVariables(storm::solver::LpSolver& solver, storm::models::Mdp<T> const& labeledMdp, StateInformation const& stateInformation, ChoiceInformation const& choiceInformation) {
+            static std::pair<std::unordered_map<std::pair<uint_fast64_t, uint_fast64_t>, std::string, PairHash>, uint_fast64_t> createProblematicChoiceVariables(storm::solver::LpSolver& solver, storm::models::Mdp<T> const& labeledMdp, StateInformation const& stateInformation, ChoiceInformation const& choiceInformation) {
                 std::stringstream variableNameBuffer;
                 uint_fast64_t numberOfVariablesCreated = 0;
-                std::unordered_map<std::pair<uint_fast64_t, uint_fast64_t>, uint_fast64_t, PairHash> resultingMap;
+                std::unordered_map<std::pair<uint_fast64_t, uint_fast64_t>, std::string, PairHash> resultingMap;
                 
                 for (auto state : stateInformation.problematicStates) {
                     std::list<uint_fast64_t> const& relevantChoicesForState = choiceInformation.relevantChoicesForRelevantStates.at(state);
                     for (uint_fast64_t row : relevantChoicesForState) {
                         for (auto const& successorEntry : labeledMdp.getTransitionMatrix().getRow(row)) {
-                            if (stateInformation.relevantStates.get(successorEntry.first)) {
+                            if (stateInformation.relevantStates.get(successorEntry.getColumn())) {
                                 variableNameBuffer.str("");
                                 variableNameBuffer.clear();
-                                variableNameBuffer << "t" << state << "to" << successorEntry.first;
-                                resultingMap[std::make_pair(state, successorEntry.first)] = solver.createBinaryVariable(variableNameBuffer.str(), 0);
+                                variableNameBuffer << "t" << state << "to" << successorEntry.getColumn();
+                                resultingMap[std::make_pair(state, successorEntry.getColumn())] = variableNameBuffer.str();
+                                solver.addBinaryVariable(resultingMap[std::make_pair(state, successorEntry.getColumn())]);
                                 ++numberOfVariablesCreated;
                             }
                         }
@@ -358,44 +366,44 @@ namespace storm {
                 VariableInformation result;
                 
                 // Create variables for involved labels.
-                std::pair<std::unordered_map<uint_fast64_t, uint_fast64_t>, uint_fast64_t> labelVariableResult = createLabelVariables(solver, choiceInformation.allRelevantLabels);
-                result.labelToVariableIndexMap = std::move(labelVariableResult.first);
+                std::pair<std::unordered_map<uint_fast64_t, std::string>, uint_fast64_t> labelVariableResult = createLabelVariables(solver, choiceInformation.allRelevantLabels);
+                result.labelToVariableMap = std::move(labelVariableResult.first);
                 result.numberOfVariables += labelVariableResult.second;
                 LOG4CPLUS_DEBUG(logger, "Created variables for labels.");
                 
                 // Create scheduler variables for relevant states and their actions.
-                std::pair<std::unordered_map<uint_fast64_t, std::list<uint_fast64_t>>, uint_fast64_t> schedulerVariableResult = createSchedulerVariables(solver, stateInformation, choiceInformation);
-                result.stateToChoiceVariablesIndexMap = std::move(schedulerVariableResult.first);
+                std::pair<std::unordered_map<uint_fast64_t, std::list<std::string>>, uint_fast64_t> schedulerVariableResult = createSchedulerVariables(solver, stateInformation, choiceInformation);
+                result.stateToChoiceVariablesMap = std::move(schedulerVariableResult.first);
                 result.numberOfVariables += schedulerVariableResult.second;
                 LOG4CPLUS_DEBUG(logger, "Created variables for nondeterministic choices.");
 
                 // Create scheduler variables for nondeterministically choosing an initial state.
-                std::pair<std::unordered_map<uint_fast64_t, uint_fast64_t>, uint_fast64_t> initialChoiceVariableResult = createInitialChoiceVariables(solver, labeledMdp, stateInformation);
-                result.initialStateToChoiceVariableIndexMap = std::move(initialChoiceVariableResult.first);
+                std::pair<std::unordered_map<uint_fast64_t, std::string>, uint_fast64_t> initialChoiceVariableResult = createInitialChoiceVariables(solver, labeledMdp, stateInformation);
+                result.initialStateToChoiceVariableMap = std::move(initialChoiceVariableResult.first);
                 result.numberOfVariables += initialChoiceVariableResult.second;
                 LOG4CPLUS_DEBUG(logger, "Created variables for the nondeterministic choice of the initial state.");
                 
                 // Create variables for probabilities for all relevant states.
-                std::pair<std::unordered_map<uint_fast64_t, uint_fast64_t>, uint_fast64_t> probabilityVariableResult = createProbabilityVariables(solver, stateInformation);
-                result.stateToProbabilityVariableIndexMap = std::move(probabilityVariableResult.first);
+                std::pair<std::unordered_map<uint_fast64_t, std::string>, uint_fast64_t> probabilityVariableResult = createProbabilityVariables(solver, stateInformation);
+                result.stateToProbabilityVariableMap = std::move(probabilityVariableResult.first);
                 result.numberOfVariables += probabilityVariableResult.second;
                 LOG4CPLUS_DEBUG(logger, "Created variables for the reachability probabilities.");
 
                 // Create a probability variable for a virtual initial state that nondeterministically chooses one of the system's real initial states as its target state.
-                std::pair<uint_fast64_t, uint_fast64_t> virtualInitialStateVariableResult = createVirtualInitialStateVariable(solver);
-                result.virtualInitialStateVariableIndex = virtualInitialStateVariableResult.first;
+                std::pair<std::string, uint_fast64_t> virtualInitialStateVariableResult = createVirtualInitialStateVariable(solver);
+                result.virtualInitialStateVariable = virtualInitialStateVariableResult.first;
                 result.numberOfVariables += virtualInitialStateVariableResult.second;
                 LOG4CPLUS_DEBUG(logger, "Created variables for the virtual initial state.");
 
                 // Create variables for problematic states.
-                std::pair<std::unordered_map<uint_fast64_t, uint_fast64_t>, uint_fast64_t> problematicStateVariableResult = createProblematicStateVariables(solver, labeledMdp, stateInformation, choiceInformation);
-                result.problematicStateToVariableIndexMap = std::move(problematicStateVariableResult.first);
+                std::pair<std::unordered_map<uint_fast64_t, std::string>, uint_fast64_t> problematicStateVariableResult = createProblematicStateVariables(solver, labeledMdp, stateInformation, choiceInformation);
+                result.problematicStateToVariableMap = std::move(problematicStateVariableResult.first);
                 result.numberOfVariables += problematicStateVariableResult.second;
                 LOG4CPLUS_DEBUG(logger, "Created variables for the problematic states.");
 
                 // Create variables for problematic choices.
-                std::pair<std::unordered_map<std::pair<uint_fast64_t, uint_fast64_t>, uint_fast64_t, PairHash>, uint_fast64_t> problematicTransitionVariableResult = createProblematicChoiceVariables(solver, labeledMdp, stateInformation, choiceInformation);
-                result.problematicTransitionToVariableIndexMap = problematicTransitionVariableResult.first;
+                std::pair<std::unordered_map<std::pair<uint_fast64_t, uint_fast64_t>, std::string, PairHash>, uint_fast64_t> problematicTransitionVariableResult = createProblematicChoiceVariables(solver, labeledMdp, stateInformation, choiceInformation);
+                result.problematicTransitionToVariableMap = problematicTransitionVariableResult.first;
                 result.numberOfVariables += problematicTransitionVariableResult.second;
                 LOG4CPLUS_DEBUG(logger, "Created variables for the problematic choices.");
 
@@ -419,7 +427,13 @@ namespace storm {
              * @return The total number of constraints that were created.
              */
             static uint_fast64_t assertProbabilityGreaterThanThreshold(storm::solver::LpSolver& solver, storm::models::Mdp<T> const& labeledMdp, VariableInformation const& variableInformation, double probabilityThreshold, bool strictBound) {
-                solver.addConstraint("ProbGreaterThreshold", {variableInformation.virtualInitialStateVariableIndex}, {1}, strictBound ? storm::solver::LpSolver::GREATER : storm::solver::LpSolver::GREATER_EQUAL, probabilityThreshold);
+                storm::expressions::Expression constraint;
+                if (strictBound) {
+                    constraint = storm::expressions::Expression::createDoubleVariable(variableInformation.virtualInitialStateVariable) > storm::expressions::Expression::createDoubleLiteral(probabilityThreshold);
+                } else {
+                    constraint = storm::expressions::Expression::createDoubleVariable(variableInformation.virtualInitialStateVariable) >= storm::expressions::Expression::createDoubleLiteral(probabilityThreshold);
+                }
+                solver.addConstraint("ProbGreaterThreshold", constraint);
                 return 1;
             }
             
@@ -435,28 +449,28 @@ namespace storm {
                 // Assert that the policy chooses at most one action in each state of the system.
                 uint_fast64_t numberOfConstraintsCreated = 0;
                 for (auto state : stateInformation.relevantStates) {
-                    std::list<uint_fast64_t> const& choiceVariableIndices = variableInformation.stateToChoiceVariablesIndexMap.at(state);
-                    std::vector<uint_fast64_t> variables;
-                    std::vector<double> coefficients(choiceVariableIndices.size(), 1);
-                    variables.reserve(choiceVariableIndices.size());
-                    for (auto choiceVariableIndex : choiceVariableIndices) {
-                        variables.push_back(choiceVariableIndex);
+                    std::list<std::string> const& choiceVariableIndices = variableInformation.stateToChoiceVariablesMap.at(state);
+                    storm::expressions::Expression constraint = storm::expressions::Expression::createDoubleLiteral(0);
+
+                    for (auto const& choiceVariable : choiceVariableIndices) {
+                        constraint = constraint + storm::expressions::Expression::createIntegerVariable(choiceVariable);
                     }
                     
-                    solver.addConstraint("ValidPolicy" + std::to_string(numberOfConstraintsCreated), variables, coefficients, storm::solver::LpSolver::LESS_EQUAL, 1);
+                    constraint = constraint <= storm::expressions::Expression::createDoubleLiteral(1);
+                    
+                    solver.addConstraint("ValidPolicy" + std::to_string(numberOfConstraintsCreated), constraint);
                     ++numberOfConstraintsCreated;
                 }
                 
                 // Now assert that the virtual initial state picks exactly one initial state from the system as its
                 // successor state.
-                std::vector<uint_fast64_t> variables;
-                variables.reserve(variableInformation.initialStateToChoiceVariableIndexMap.size());
-                std::vector<double> coefficients(variableInformation.initialStateToChoiceVariableIndexMap.size(), 1);
-                for (auto initialStateVariableIndexPair : variableInformation.initialStateToChoiceVariableIndexMap) {
-                    variables.push_back(initialStateVariableIndexPair.second);
+                storm::expressions::Expression constraint = storm::expressions::Expression::createDoubleLiteral(0);
+                for (auto const& initialStateVariablePair : variableInformation.initialStateToChoiceVariableMap) {
+                    constraint = constraint + storm::expressions::Expression::createIntegerVariable(initialStateVariablePair.second);
                 }
+                constraint = constraint == storm::expressions::Expression::createDoubleLiteral(1);
                 
-                solver.addConstraint("VirtualInitialStateChoosesOneInitialState", variables, coefficients, storm::solver::LpSolver::EQUAL, 1);
+                solver.addConstraint("VirtualInitialStateChoosesOneInitialState", constraint);
                 ++numberOfConstraintsCreated;
                 
                 return numberOfConstraintsCreated;
@@ -478,13 +492,14 @@ namespace storm {
 
                 std::vector<boost::container::flat_set<uint_fast64_t>> const& choiceLabeling = labeledMdp.getChoiceLabeling();
                 for (auto state : stateInformation.relevantStates) {
-                    std::list<uint_fast64_t>::const_iterator choiceVariableIndicesIterator = variableInformation.stateToChoiceVariablesIndexMap.at(state).begin();
+                    std::list<std::string>::const_iterator choiceVariableIterator = variableInformation.stateToChoiceVariablesMap.at(state).begin();
                     for (auto choice : choiceInformation.relevantChoicesForRelevantStates.at(state)) {
                         for (auto label : choiceLabeling[choice]) {
-                            solver.addConstraint("ChoicesImplyLabels" + std::to_string(numberOfConstraintsCreated), {variableInformation.labelToVariableIndexMap.at(label), *choiceVariableIndicesIterator}, {1, -1}, storm::solver::LpSolver::GREATER_EQUAL, 0);
+                            storm::expressions::Expression constraint = storm::expressions::Expression::createIntegerVariable(variableInformation.labelToVariableMap.at(label)) - storm::expressions::Expression::createIntegerVariable(*choiceVariableIterator) >= storm::expressions::Expression::createDoubleLiteral(0);
+                            solver.addConstraint("ChoicesImplyLabels" + std::to_string(numberOfConstraintsCreated), constraint);
                             ++numberOfConstraintsCreated;
                         }
-                        ++choiceVariableIndicesIterator;
+                        ++choiceVariableIterator;
                     }
                 }
                 return numberOfConstraintsCreated;
@@ -503,16 +518,12 @@ namespace storm {
             static uint_fast64_t assertZeroProbabilityWithoutChoice(storm::solver::LpSolver& solver, StateInformation const& stateInformation, ChoiceInformation const& choiceInformation, VariableInformation const& variableInformation) {
                 uint_fast64_t numberOfConstraintsCreated = 0;
                 for (auto state : stateInformation.relevantStates) {
-                    std::list<uint_fast64_t> const& choiceVariableIndices = variableInformation.stateToChoiceVariablesIndexMap.at(state);
-
-                    std::vector<double> coefficients(choiceVariableIndices.size() + 1, -1);
-                    coefficients[0] = 1;
-                    std::vector<uint_fast64_t> variables;
-                    variables.reserve(variableInformation.stateToChoiceVariablesIndexMap.at(state).size() + 1);
-                    variables.push_back(variableInformation.stateToProbabilityVariableIndexMap.at(state));
-                    variables.insert(variables.end(), choiceVariableIndices.begin(), choiceVariableIndices.end());
-                    
-                    solver.addConstraint("ProbabilityIsZeroIfNoAction" + std::to_string(numberOfConstraintsCreated), variables, coefficients, storm::solver::LpSolver::LESS_EQUAL, 0);
+                    storm::expressions::Expression constraint = storm::expressions::Expression::createDoubleVariable(variableInformation.stateToProbabilityVariableMap.at(state));
+                    for (auto const& choiceVariable : variableInformation.stateToChoiceVariablesMap.at(state)) {
+                        constraint = constraint - storm::expressions::Expression::createIntegerVariable(choiceVariable);
+                    }
+                    constraint = constraint <= storm::expressions::Expression::createDoubleLiteral(0);
+                    solver.addConstraint("ProbabilityIsZeroIfNoAction" + std::to_string(numberOfConstraintsCreated), constraint);
                     ++numberOfConstraintsCreated;
                 }
                 return numberOfConstraintsCreated;
@@ -532,40 +543,32 @@ namespace storm {
             static uint_fast64_t assertReachabilityProbabilities(storm::solver::LpSolver& solver, storm::models::Mdp<T> const& labeledMdp, storm::storage::BitVector const& psiStates, StateInformation const& stateInformation, ChoiceInformation const& choiceInformation, VariableInformation const& variableInformation) {
                 uint_fast64_t numberOfConstraintsCreated = 0;
                 for (auto state : stateInformation.relevantStates) {
-                    std::vector<double> coefficients;
-                    std::vector<uint_fast64_t> variables;
-                    
-                    std::list<uint_fast64_t>::const_iterator choiceVariableIndicesIterator = variableInformation.stateToChoiceVariablesIndexMap.at(state).begin();
+                    std::list<std::string>::const_iterator choiceVariableIterator = variableInformation.stateToChoiceVariablesMap.at(state).begin();
                     for (auto choice : choiceInformation.relevantChoicesForRelevantStates.at(state)) {
-                        variables.clear();
-                        coefficients.clear();
-                        variables.push_back(variableInformation.stateToProbabilityVariableIndexMap.at(state));
-                        coefficients.push_back(1.0);
+                        storm::expressions::Expression constraint = storm::expressions::Expression::createDoubleVariable(variableInformation.stateToProbabilityVariableMap.at(state));
                         
                         double rightHandSide = 1;
                         for (auto const& successorEntry : labeledMdp.getTransitionMatrix().getRow(choice)) {
-                            if (stateInformation.relevantStates.get(successorEntry.first)) {
-                                variables.push_back(static_cast<int>(variableInformation.stateToProbabilityVariableIndexMap.at(successorEntry.first)));
-                                coefficients.push_back(-successorEntry.second);
-                            } else if (psiStates.get(successorEntry.first)) {
-                                rightHandSide += successorEntry.second;
+                            if (stateInformation.relevantStates.get(successorEntry.getColumn())) {
+                                constraint = constraint - storm::expressions::Expression::createDoubleLiteral(successorEntry.getValue()) * storm::expressions::Expression::createDoubleVariable(variableInformation.stateToProbabilityVariableMap.at(successorEntry.getColumn()));
+                            } else if (psiStates.get(successorEntry.getColumn())) {
+                                rightHandSide += successorEntry.getValue();
                             }
                         }
                         
-                        coefficients.push_back(1);
-                        variables.push_back(*choiceVariableIndicesIterator);
-                        
-                        solver.addConstraint("ReachabilityProbabilities" + std::to_string(numberOfConstraintsCreated), variables, coefficients, storm::solver::LpSolver::LESS_EQUAL, rightHandSide);
+                        constraint = constraint + storm::expressions::Expression::createIntegerVariable(*choiceVariableIterator) <= storm::expressions::Expression::createDoubleLiteral(rightHandSide);
+                        solver.addConstraint("ReachabilityProbabilities" + std::to_string(numberOfConstraintsCreated), constraint);
                         
                         ++numberOfConstraintsCreated;
-                        ++choiceVariableIndicesIterator;
+                        ++choiceVariableIterator;
                     }
                 }
                 
                 // Make sure that the virtual initial state is being assigned the probability from the initial state
                 // that it selected as a successor state.
-                for (auto initialStateVariableIndexPair : variableInformation.initialStateToChoiceVariableIndexMap) {
-                    solver.addConstraint("VirtualInitialStateHasCorrectProbability" + std::to_string(numberOfConstraintsCreated), {variableInformation.virtualInitialStateVariableIndex, variableInformation.stateToProbabilityVariableIndexMap.at(initialStateVariableIndexPair.first), initialStateVariableIndexPair.second}, {1, -1, 1}, storm::solver::LpSolver::LESS_EQUAL, 1);
+                for (auto const& initialStateVariablePair : variableInformation.initialStateToChoiceVariableMap) {
+                    storm::expressions::Expression constraint = storm::expressions::Expression::createDoubleVariable(variableInformation.virtualInitialStateVariable) - storm::expressions::Expression::createDoubleVariable(variableInformation.stateToProbabilityVariableMap.at(initialStateVariablePair.first)) + storm::expressions::Expression::createDoubleVariable(initialStateVariablePair.second) <= storm::expressions::Expression::createDoubleLiteral(1);
+                    solver.addConstraint("VirtualInitialStateHasCorrectProbability" + std::to_string(numberOfConstraintsCreated), constraint);
                     ++numberOfConstraintsCreated;
                 }
                 
@@ -587,44 +590,34 @@ namespace storm {
 
                 for (auto stateListPair : choiceInformation.problematicChoicesForProblematicStates) {
                     for (auto problematicChoice : stateListPair.second) {
-                        std::list<uint_fast64_t>::const_iterator choiceVariableIndicesIterator = variableInformation.stateToChoiceVariablesIndexMap.at(stateListPair.first).begin();
+                        std::list<std::string>::const_iterator choiceVariableIterator = variableInformation.stateToChoiceVariablesMap.at(stateListPair.first).begin();
                         for (auto relevantChoice : choiceInformation.relevantChoicesForRelevantStates.at(stateListPair.first)) {
                             if (relevantChoice == problematicChoice) {
                                 break;
                             }
-                            ++choiceVariableIndicesIterator;
+                            ++choiceVariableIterator;
                         }
                         
-                        std::vector<uint_fast64_t> variables;
-                        std::vector<double> coefficients;
-                        
-                        variables.push_back(*choiceVariableIndicesIterator);
-                        coefficients.push_back(1);
-                        
+                        storm::expressions::Expression constraint = storm::expressions::Expression::createDoubleVariable(*choiceVariableIterator);
                         for (auto const& successorEntry : labeledMdp.getTransitionMatrix().getRow(problematicChoice)) {
-                            variables.push_back(variableInformation.problematicTransitionToVariableIndexMap.at(std::make_pair(stateListPair.first, successorEntry.first)));
-                            coefficients.push_back(-1);
+                            constraint = constraint - storm::expressions::Expression::createDoubleVariable(variableInformation.problematicTransitionToVariableMap.at(std::make_pair(stateListPair.first, successorEntry.getColumn())));
                         }
+                        constraint = constraint <= storm::expressions::Expression::createDoubleLiteral(0);
                         
-                        solver.addConstraint("UnproblematicStateReachable" + std::to_string(numberOfConstraintsCreated), variables, coefficients, storm::solver::LpSolver::LESS_EQUAL, 0);
+                        solver.addConstraint("UnproblematicStateReachable" + std::to_string(numberOfConstraintsCreated), constraint);
                         ++numberOfConstraintsCreated;
                     }
                 }
                 
                 for (auto state : stateInformation.problematicStates) {
                     for (auto problematicChoice : choiceInformation.problematicChoicesForProblematicStates.at(state)) {
-                        for (auto const& successorEntry : labeledMdp.getTransitionMatrix().getRow(state)) {
-                            std::vector<uint_fast64_t> variables;
-                            std::vector<double> coefficients;
-                            
-                            variables.push_back(variableInformation.problematicStateToVariableIndexMap.at(state));
-                            coefficients.push_back(1);
-                            variables.push_back(variableInformation.problematicStateToVariableIndexMap.at(successorEntry.first));
-                            coefficients.push_back(-1);
-                            variables.push_back(variableInformation.problematicTransitionToVariableIndexMap.at(std::make_pair(state, successorEntry.first)));
-                            coefficients.push_back(1);
+                        for (auto const& successorEntry : labeledMdp.getTransitionMatrix().getRow(problematicChoice)) {
+                            storm::expressions::Expression constraint = storm::expressions::Expression::createDoubleVariable(variableInformation.problematicStateToVariableMap.at(state));
+                            constraint = constraint - storm::expressions::Expression::createDoubleVariable(variableInformation.problematicStateToVariableMap.at(successorEntry.getColumn()));
+                            constraint = constraint + storm::expressions::Expression::createDoubleVariable(variableInformation.problematicTransitionToVariableMap.at(std::make_pair(state, successorEntry.getColumn())));
+                            constraint = constraint < storm::expressions::Expression::createDoubleLiteral(1);
                             
-                            solver.addConstraint("UnproblematicStateReachable" + std::to_string(numberOfConstraintsCreated), variables, coefficients, storm::solver::LpSolver::LESS, 1);
+                            solver.addConstraint("UnproblematicStateReachable" + std::to_string(numberOfConstraintsCreated), constraint);
                             ++numberOfConstraintsCreated;
                         }
                     }
@@ -646,7 +639,8 @@ namespace storm {
                 uint_fast64_t numberOfConstraintsCreated = 0;
 
                 for (auto label : choiceInformation.knownLabels) {
-                    solver.addConstraint("KnownLabels" + std::to_string(numberOfConstraintsCreated), {variableInformation.labelToVariableIndexMap.at(label)}, {1}, storm::solver::LpSolver::EQUAL, 1);
+                    storm::expressions::Expression constraint = storm::expressions::Expression::createDoubleVariable(variableInformation.labelToVariableMap.at(label)) == storm::expressions::Expression::createDoubleLiteral(1);
+                    solver.addConstraint("KnownLabels" + std::to_string(numberOfConstraintsCreated), constraint);
                     ++numberOfConstraintsCreated;
                 }
 
@@ -667,61 +661,52 @@ namespace storm {
             static uint_fast64_t assertSchedulerCuts(storm::solver::LpSolver& solver, storm::models::Mdp<T> const& labeledMdp, storm::storage::BitVector const& psiStates, StateInformation const& stateInformation, ChoiceInformation const& choiceInformation, VariableInformation const& variableInformation) {
                 storm::storage::SparseMatrix<T> backwardTransitions = labeledMdp.getBackwardTransitions();
                 uint_fast64_t numberOfConstraintsCreated = 0;
-                std::vector<uint_fast64_t> variables;
-                std::vector<double> coefficients;
                 
                 for (auto state : stateInformation.relevantStates) {
                     // Assert that all states, that select an action, this action either has a non-zero probability to
                     // go to a psi state directly, or in the successor states, at least one action is selected as well.
-                    std::list<uint_fast64_t>::const_iterator choiceVariableIndicesIterator = variableInformation.stateToChoiceVariablesIndexMap.at(state).begin();
+                    std::list<std::string>::const_iterator choiceVariableIterator = variableInformation.stateToChoiceVariablesMap.at(state).begin();
                     for (auto choice : choiceInformation.relevantChoicesForRelevantStates.at(state)) {
                         bool psiStateReachableInOneStep = false;
                         for (auto const& successorEntry : labeledMdp.getTransitionMatrix().getRow(choice)) {
-                            if (psiStates.get(successorEntry.first)) {
+                            if (psiStates.get(successorEntry.getColumn())) {
                                 psiStateReachableInOneStep = true;
                             }
                         }
                         
                         if (!psiStateReachableInOneStep) {
-                            variables.clear();
-                            coefficients.clear();
-                            
-                            variables.push_back(static_cast<int>(*choiceVariableIndicesIterator));
-                            coefficients.push_back(1);
-                            
+                            storm::expressions::Expression constraint = storm::expressions::Expression::createDoubleVariable(*choiceVariableIterator);
                             for (auto const& successorEntry : labeledMdp.getTransitionMatrix().getRow(choice)) {
-                                if (state != successorEntry.first && stateInformation.relevantStates.get(successorEntry.first)) {
-                                    std::list<uint_fast64_t> const& successorChoiceVariableIndices = variableInformation.stateToChoiceVariablesIndexMap.at(successorEntry.first);
+                                if (state != successorEntry.getColumn() && stateInformation.relevantStates.get(successorEntry.getColumn())) {
+                                    std::list<std::string> const& successorChoiceVariableIndices = variableInformation.stateToChoiceVariablesMap.at(successorEntry.getColumn());
                                     
-                                    for (auto choiceVariableIndex : successorChoiceVariableIndices) {
-                                        variables.push_back(choiceVariableIndex);
-                                        coefficients.push_back(-1);
+                                    for (auto const& choiceVariable : successorChoiceVariableIndices) {
+                                        constraint = constraint - storm::expressions::Expression::createDoubleVariable(choiceVariable);
                                     }
                                 }
                             }
+                            constraint = constraint <= storm::expressions::Expression::createDoubleLiteral(1);
                             
-                            solver.addConstraint("SchedulerCuts" + std::to_string(numberOfConstraintsCreated), variables, coefficients, storm::solver::LpSolver::LESS_EQUAL, 1);
+                            solver.addConstraint("SchedulerCuts" + std::to_string(numberOfConstraintsCreated), constraint);
                             ++numberOfConstraintsCreated;
                         }
                         
-                        ++choiceVariableIndicesIterator;
+                        ++choiceVariableIterator;
                     }
                     
                     // For all states assert that there is either a selected incoming transition in the subsystem or the
                     // state is the chosen initial state if there is one selected action in the current state.
-                    variables.clear();
-                    coefficients.clear();
-                    
-                    for (auto choiceVariableIndex : variableInformation.stateToChoiceVariablesIndexMap.at(state)) {
-                        variables.push_back(choiceVariableIndex);
-                        coefficients.push_back(1);
+                    storm::expressions::Expression constraint = storm::expressions::Expression::createDoubleLiteral(0);
+
+                    for (auto const& choiceVariable : variableInformation.stateToChoiceVariablesMap.at(state)) {
+                        constraint = constraint + storm::expressions::Expression::createDoubleVariable(choiceVariable);
                     }
                     
                     // Compute the set of predecessors.
                     std::unordered_set<uint_fast64_t> predecessors;
                     for (auto const& predecessorEntry : backwardTransitions.getRow(state)) {
-                        if (state != predecessorEntry.first) {
-                            predecessors.insert(predecessorEntry.first);
+                        if (state != predecessorEntry.getColumn()) {
+                            predecessors.insert(predecessorEntry.getColumn());
                         }
                     }
                     
@@ -731,13 +716,13 @@ namespace storm {
                             continue;
                         }
                         
-                        std::list<uint_fast64_t>::const_iterator choiceVariableIndicesIterator = variableInformation.stateToChoiceVariablesIndexMap.at(predecessor).begin();
+                        std::list<std::string>::const_iterator choiceVariableIterator = variableInformation.stateToChoiceVariablesMap.at(predecessor).begin();
                         for (auto relevantChoice : choiceInformation.relevantChoicesForRelevantStates.at(predecessor)) {
                             bool choiceTargetsCurrentState = false;
                             
                             // Check if the current choice targets the current state.
                             for (auto const& successorEntry : labeledMdp.getTransitionMatrix().getRow(relevantChoice)) {
-                                if (state == successorEntry.first) {
+                                if (state == successorEntry.getColumn()) {
                                     choiceTargetsCurrentState = true;
                                     break;
                                 }
@@ -745,45 +730,42 @@ namespace storm {
                             
                             // If it does, we can add the choice to the sum.
                             if (choiceTargetsCurrentState) {
-                                variables.push_back(static_cast<int>(*choiceVariableIndicesIterator));
-                                coefficients.push_back(-1);
+                                constraint = constraint - storm::expressions::Expression::createDoubleVariable(*choiceVariableIterator);
                             }
-                            ++choiceVariableIndicesIterator;
+                            ++choiceVariableIterator;
                         }
                     }
                     
                     // If the current state is an initial state and is selected as a successor state by the virtual
                     // initial state, then this also justifies making a choice in the current state.
                     if (labeledMdp.getLabeledStates("init").get(state)) {
-                        variables.push_back(variableInformation.initialStateToChoiceVariableIndexMap.at(state));
-                        coefficients.push_back(-1);
+                        constraint = constraint - storm::expressions::Expression::createDoubleVariable(variableInformation.initialStateToChoiceVariableMap.at(state));
                     }
+                    constraint = constraint <= storm::expressions::Expression::createDoubleLiteral(0);
                     
-                    solver.addConstraint("SchedulerCuts" + std::to_string(numberOfConstraintsCreated), variables, coefficients, storm::solver::LpSolver::LESS_EQUAL, 0);
+                    solver.addConstraint("SchedulerCuts" + std::to_string(numberOfConstraintsCreated), constraint);
                     ++numberOfConstraintsCreated;
                 }
                 
                 // Assert that at least one initial state selects at least one action.
-                variables.clear();
-                coefficients.clear();
+                storm::expressions::Expression constraint = storm::expressions::Expression::createDoubleLiteral(0);
                 for (auto initialState : labeledMdp.getLabeledStates("init")) {
-                    for (auto choiceVariableIndex : variableInformation.stateToChoiceVariablesIndexMap.at(initialState)) {
-                        variables.push_back(choiceVariableIndex);
-                        coefficients.push_back(1);
+                    for (auto const& choiceVariable : variableInformation.stateToChoiceVariablesMap.at(initialState)) {
+                        constraint = constraint + storm::expressions::Expression::createDoubleVariable(choiceVariable);
                     }
                 }
-                solver.addConstraint("SchedulerCuts" + std::to_string(numberOfConstraintsCreated), variables, coefficients, storm::solver::LpSolver::GREATER_EQUAL, 1);
+                constraint = constraint >= storm::expressions::Expression::createDoubleLiteral(1);
+                solver.addConstraint("SchedulerCuts" + std::to_string(numberOfConstraintsCreated), constraint);
                 ++numberOfConstraintsCreated;
                 
                 // Add constraints that ensure at least one choice is selected that targets a psi state.
-                variables.clear();
-                coefficients.clear();
+                constraint = storm::expressions::Expression::createDoubleLiteral(0);
                 std::unordered_set<uint_fast64_t> predecessors;
                 for (auto psiState : psiStates) {
                     // Compute the set of predecessors.
                     for (auto const& predecessorEntry : backwardTransitions.getRow(psiState)) {
-                        if (psiState != predecessorEntry.first) {
-                            predecessors.insert(predecessorEntry.first);
+                        if (psiState != predecessorEntry.getColumn()) {
+                            predecessors.insert(predecessorEntry.getColumn());
                         }
                     }
                 }
@@ -794,13 +776,13 @@ namespace storm {
                         continue;
                     }
                     
-                    std::list<uint_fast64_t>::const_iterator choiceVariableIndicesIterator = variableInformation.stateToChoiceVariablesIndexMap.at(predecessor).begin();
+                    std::list<std::string>::const_iterator choiceVariableIterator = variableInformation.stateToChoiceVariablesMap.at(predecessor).begin();
                     for (auto relevantChoice : choiceInformation.relevantChoicesForRelevantStates.at(predecessor)) {
                         bool choiceTargetsPsiState = false;
                         
                         // Check if the current choice targets the current state.
                         for (auto const& successorEntry : labeledMdp.getTransitionMatrix().getRow(relevantChoice)) {
-                            if (psiStates.get(successorEntry.first)) {
+                            if (psiStates.get(successorEntry.getColumn())) {
                                 choiceTargetsPsiState = true;
                                 break;
                             }
@@ -808,14 +790,14 @@ namespace storm {
                         
                         // If it does, we can add the choice to the sum.
                         if (choiceTargetsPsiState) {
-                            variables.push_back(*choiceVariableIndicesIterator);
-                            coefficients.push_back(1);
+                            constraint = constraint + storm::expressions::Expression::createDoubleVariable(*choiceVariableIterator);
                         }
-                        ++choiceVariableIndicesIterator;
+                        ++choiceVariableIterator;
                     }
                 }
+                constraint = constraint >= storm::expressions::Expression::createDoubleLiteral(1);
                 
-                solver.addConstraint("SchedulerCuts" + std::to_string(numberOfConstraintsCreated), variables, coefficients, storm::solver::LpSolver::GREATER_EQUAL, 1);
+                solver.addConstraint("SchedulerCuts" + std::to_string(numberOfConstraintsCreated), constraint);
                 ++numberOfConstraintsCreated;
                 
                 return numberOfConstraintsCreated;
@@ -872,6 +854,9 @@ namespace storm {
                     LOG4CPLUS_DEBUG(logger, "Asserted scheduler cuts.");
                 }
                 
+                // Finally, we can tell the solver to incorporate the latest changes.
+                solver.update();
+                
                 LOG4CPLUS_INFO(logger, "Successfully created " << numberOfConstraints << " MILP constraints.");
             }
             
@@ -884,7 +869,7 @@ namespace storm {
             static boost::container::flat_set<uint_fast64_t> getUsedLabelsInSolution(storm::solver::LpSolver const& solver, VariableInformation const& variableInformation) {
                 boost::container::flat_set<uint_fast64_t> result;
 
-                for (auto labelVariablePair : variableInformation.labelToVariableIndexMap) {
+                for (auto const& labelVariablePair : variableInformation.labelToVariableMap) {
                     bool labelTaken = solver.getBinaryValue(labelVariablePair.second);
                     
                     if (labelTaken) {
@@ -908,10 +893,10 @@ namespace storm {
                 std::map<uint_fast64_t, uint_fast64_t> result;
                 
                 for (auto state : stateInformation.relevantStates) {
-                    std::list<uint_fast64_t>::const_iterator choiceVariableIndicesIterator = variableInformation.stateToChoiceVariablesIndexMap.at(state).begin();
+                    std::list<std::string>::const_iterator choiceVariableIterator = variableInformation.stateToChoiceVariablesIndexMap.at(state).begin();
                     for (auto choice : choiceInformation.relevantChoicesForRelevantStates.at(state)) {
-                        bool choiceTaken = solver.getBinaryValue(*choiceVariableIndicesIterator);
-                        ++choiceVariableIndicesIterator;
+                        bool choiceTaken = solver.getBinaryValue(*choiceVariableIterator);
+                        ++choiceVariableIterator;
                         if (choiceTaken) {
                             result.emplace_hint(result.end(), state, choice);
                         }
@@ -930,20 +915,20 @@ namespace storm {
              */
             static std::pair<uint_fast64_t, double> getReachabilityProbability(storm::solver::LpSolver const& solver, storm::models::Mdp<T> const& labeledMdp, VariableInformation const& variableInformation) {
                 uint_fast64_t selectedInitialState = 0;
-                for (auto initialStateVariableIndexPair : variableInformation.initialStateToChoiceVariableIndexMap) {
-                    bool initialStateChosen = solver.getBinaryValue(initialStateVariableIndexPair.second);
+                for (auto const& initialStateVariablePair : variableInformation.initialStateToChoiceVariableMap) {
+                    bool initialStateChosen = solver.getBinaryValue(initialStateVariablePair.second);
                     if (initialStateChosen) {
-                        selectedInitialState = initialStateVariableIndexPair.first;
+                        selectedInitialState = initialStateVariablePair.first;
                         break;
                     }
                 }
                 
-                double reachabilityProbability = solver.getContinuousValue(variableInformation.virtualInitialStateVariableIndex);
+                double reachabilityProbability = solver.getContinuousValue(variableInformation.virtualInitialStateVariable);
                 return std::make_pair(selectedInitialState, reachabilityProbability);
             }
                 
         public:
-            
+
             static boost::container::flat_set<uint_fast64_t> getMinimalLabelSet(storm::models::Mdp<T> const& labeledMdp, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, double probabilityThreshold, bool strictBound, bool checkThresholdFeasible = false, bool includeSchedulerCuts = false) {
                 // (0) Check whether the MDP is indeed labeled.
                 if (!labeledMdp.hasChoiceLabeling()) {
diff --git a/src/counterexamples/PathBasedSubsystemGenerator.h b/src/counterexamples/PathBasedSubsystemGenerator.h
index 11e24d00d..2325eebe8 100644
--- a/src/counterexamples/PathBasedSubsystemGenerator.h
+++ b/src/counterexamples/PathBasedSubsystemGenerator.h
@@ -69,31 +69,31 @@ public:
 
 				for(auto const& trans : transMat.getRow(init)) {
 					//save transition only if it's no 'virtual transition of prob 0 and it doesn't go from init state to init state.
-					if(trans.second != (T) 0 && !subSysStates.get(trans.first)) {
+					if(trans.getValue() != (T) 0 && !subSysStates.get(trans.getColumn())) {
 						//new state?
-						if(distances[trans.first].second == (T) -1) {
-							distances[trans.first].first = init;
-							distances[trans.first].second = trans.second;
+						if(distances[trans.getColumn()].second == (T) -1) {
+							distances[trans.getColumn()].first = init;
+							distances[trans.getColumn()].second = trans.getValue();
 
-							activeSet.insert(std::pair<uint_fast64_t, T>(trans.first, distances[trans.first].second));
+							activeSet.insert(std::pair<uint_fast64_t, T>(trans.getColumn(), distances[trans.getColumn()].second));
 						}
-						else if(distances[trans.first].second < trans.second){
+						else if(distances[trans.getColumn()].second < trans.getValue()){
 							//This state has already been discovered
 							//And the distance can be improved by using this transition.
 
 							//find state in set, remove it, reenter it with new and correct values.
-							auto range = activeSet.equal_range(std::pair<uint_fast64_t, T>(trans.first, distances[trans.first].second));
+							auto range = activeSet.equal_range(std::pair<uint_fast64_t, T>(trans.getColumn(), distances[trans.getColumn()].second));
 							for(;range.first != range.second; range.first++) {
-								if(trans.first == range.first->first) {
+								if(trans.getColumn() == range.first->first) {
 									activeSet.erase(range.first);
 									break;
 								}
 							}
 
-							distances[trans.first].first = init;
-							distances[trans.first].second = trans.second;
+							distances[trans.getColumn()].first = init;
+							distances[trans.getColumn()].second = trans.getValue();
 
-							activeSet.insert(std::pair<uint_fast64_t, T>(trans.first, trans.second));
+							activeSet.insert(std::pair<uint_fast64_t, T>(trans.getColumn(), trans.getValue()));
 						}
 					}
 				}
@@ -115,36 +115,36 @@ public:
 				// Look at all neighbors
 				for(auto const& trans : transMat.getRow(activeState.first)) {
 					// Only consider the transition if it's not virtual
-					if(trans.second != (T) 0) {
+					if(trans.getValue() != (T) 0) {
 
-						T distance = activeState.second * trans.second;
+						T distance = activeState.second * trans.getValue();
 
 						//not discovered or initial terminal state
-						if(distances[trans.first].second == (T)-1) {
+						if(distances[trans.getColumn()].second == (T)-1) {
 							//New state discovered -> save it
-							distances[trans.first].first = activeState.first;
-							distances[trans.first].second = distance;
+							distances[trans.getColumn()].first = activeState.first;
+							distances[trans.getColumn()].second = distance;
 
 							// push newly discovered state into activeSet
-							activeSet.insert(std::pair<uint_fast64_t, T>(trans.first, distance));
+							activeSet.insert(std::pair<uint_fast64_t, T>(trans.getColumn(), distance));
 						}
-						else if(distances[trans.first].second < distance ){
+						else if(distances[trans.getColumn()].second < distance) {
 							//This state has already been discovered
 							//And the distance can be improved by using this transition.
 
 							//find state in set, remove it, reenter it with new and correct values.
 
-							auto range = activeSet.equal_range(std::pair<uint_fast64_t, T>(trans.first, distances[trans.first].second));
+							auto range = activeSet.equal_range(std::pair<uint_fast64_t, T>(trans.getColumn(), distances[trans.getColumn()].second));
 							for(;range.first != range.second; range.first++) {
-								if(trans.first == range.first->first) {
+								if(trans.getColumn() == range.first->first) {
 									activeSet.erase(range.first);
 									break;
 								}
 							}
 
-							distances[trans.first].first = activeState.first;
-							distances[trans.first].second = distance;
-							activeSet.insert(std::pair<uint_fast64_t, T>(trans.first, distance));
+							distances[trans.getColumn()].first = activeState.first;
+							distances[trans.getColumn()].second = distance;
+							activeSet.insert(std::pair<uint_fast64_t, T>(trans.getColumn(), distance));
 						}
 					}
 				}
@@ -182,33 +182,33 @@ public:
 
 				for(auto const& trans : transMat.getRow(init)) {
 					//save transition only if it's no 'virtual transition of prob 0 and it doesn't go from init state to init state.
-					if(trans.second != (T) 0 && !subSysStates.get(trans.first)) {
+					if(trans.getValue() != (T) 0 && !subSysStates.get(trans.getColumn())) {
 						//new state?
-						if(distances[trans.first].second == (T) -1) {
+						if(distances[trans.getColumn()].second == (T) -1) {
 							//for initialization of subsys -> subsys search use prob (init -> subsys state -> found state) instead of prob(subsys state -> found state)
-							distances[trans.first].first = init;
-							distances[trans.first].second = trans.second * (itDistances[init].second == -1 ? 1 : itDistances[init].second);
+							distances[trans.getColumn()].first = init;
+							distances[trans.getColumn()].second = trans.getValue() * (itDistances[init].second == -1 ? 1 : itDistances[init].second);
 
-							activeSet.insert(std::pair<uint_fast64_t, T>(trans.first, distances[trans.first].second));
+							activeSet.insert(std::pair<uint_fast64_t, T>(trans.getColumn(), distances[trans.getColumn()].second));
 						}
-						else if(distances[trans.first].second < trans.second * itDistances[init].second){
+						else if(distances[trans.getColumn()].second < trans.getValue() * itDistances[init].second){
 							//This state has already been discovered
 							//And the distance can be improved by using this transition.
 
 							//find state in set, remove it, reenter it with new and correct values.
-							auto range = activeSet.equal_range(std::pair<uint_fast64_t, T>(trans.first, distances[trans.first].second));
+							auto range = activeSet.equal_range(std::pair<uint_fast64_t, T>(trans.getColumn(), distances[trans.getColumn()].second));
 							for(;range.first != range.second; range.first++) {
-								if(trans.first == range.first->first) {
+								if(trans.getColumn() == range.first->first) {
 									activeSet.erase(range.first);
 									break;
 								}
 							}
 
 							//for initialization of subsys -> subsys search use prob (init -> subsys state -> found state) instead of prob(subsys state -> found state)
-							distances[trans.first].first = init;
-							distances[trans.first].second = trans.second * (itDistances[init].second == -1 ? 1 : itDistances[init].second);
+							distances[trans.getColumn()].first = init;
+							distances[trans.getColumn()].second = trans.getValue() * (itDistances[init].second == -1 ? 1 : itDistances[init].second);
 
-							activeSet.insert(std::pair<uint_fast64_t, T>(trans.first, trans.second));
+							activeSet.insert(std::pair<uint_fast64_t, T>(trans.getColumn(), trans.getValue()));
 						}
 					}
 				}
@@ -225,7 +225,7 @@ public:
 			activeSet.erase(--activeSet.end());
 
 			// Always stop at first target/terminal state
-			//if(terminalStates.get(activeState.first) || subSysStates.get(activeState.first)) break;
+			//if(terminalStates.get(activeState.getColumn()) || subSysStates.get(activeState.getColumn())) break;
 
 			// If this is an initial state, do not consider its outgoing transitions, since all relevant ones have already been considered
 			// Same goes for forbidden states since they may not be used on a path, except as last node.
@@ -233,36 +233,36 @@ public:
 				// Look at all neighbors
 				for(auto const& trans : transMat.getRow(activeState.first)) {
 					// Only consider the transition if it's not virtual
-					if(trans.second != (T) 0) {
+					if(trans.getValue() != (T) 0) {
 
-						T distance = activeState.second * trans.second;
+						T distance = activeState.second * trans.getValue();
 
 						//not discovered or initial terminal state
-						if(distances[trans.first].second == (T)-1) {
+						if(distances[trans.getColumn()].second == (T)-1) {
 							//New state discovered -> save it
-							distances[trans.first].first = activeState.first;
-							distances[trans.first].second = distance;
+							distances[trans.getColumn()].first = activeState.first;
+							distances[trans.getColumn()].second = distance;
 
 							// push newly discovered state into activeSet
-							activeSet.insert(std::pair<uint_fast64_t, T>(trans.first, distance));
+							activeSet.insert(std::pair<uint_fast64_t, T>(trans.getColumn(), distance));
 						}
-						else if(distances[trans.first].second < distance ){
+						else if(distances[trans.getColumn()].second < distance) {
 							//This state has already been discovered
 							//And the distance can be improved by using this transition.
 
 							//find state in set, remove it, reenter it with new and correct values.
 
-							auto range = activeSet.equal_range(std::pair<uint_fast64_t, T>(trans.first, distances[trans.first].second));
+							auto range = activeSet.equal_range(std::pair<uint_fast64_t, T>(trans.getColumn(), distances[trans.getColumn()].second));
 							for(;range.first != range.second; range.first++) {
-								if(trans.first == range.first->first) {
+								if(trans.getColumn() == range.first->first) {
 									activeSet.erase(range.first);
 									break;
 								}
 							}
 
-							distances[trans.first].first = activeState.first;
-							distances[trans.first].second = distance;
-							activeSet.insert(std::pair<uint_fast64_t, T>(trans.first, distance));
+							distances[trans.getColumn()].first = activeState.first;
+							distances[trans.getColumn()].second = distance;
+							activeSet.insert(std::pair<uint_fast64_t, T>(trans.getColumn(), distance));
 						}
 					}
 				}
@@ -292,8 +292,8 @@ public:
 
 			// if there is a terminal state that is an initial state then prob == 1 and return
 			if(initStates.get(*target)){
-				distances[*target].first = *target;
-				distances[*target].second = (T) 1;
+				distances[*target].getColumn() = *target;
+				distances[*target].getValue() = (T) 1;
 				return;
 			}
 
@@ -302,19 +302,19 @@ public:
 				//only use if allowed and not in subsys and not terminal
 				if(allowedStates.get(*iter) && !subSysStates.get(*iter) && !terminalStates.get(*iter)) {
 					//new state?
-					if(distances[*iter].second == (T) -1) {
+					if(distances[*iter].getValue() == (T) -1) {
 						// save as discovered and push into active set
-						distances[*iter].first = *target;												//successor
-						distances[*iter].second = transMat.getValue(*iter, *target);					//prob of shortest path
+						distances[*iter].getColumn() = *target;												//successor
+						distances[*iter].getValue() = transMat.getValue(*iter, *target);					//prob of shortest path
 
 						activeSet.insert(std::pair<uint_fast64_t, T>(*iter, probabilities[*iter]));		//prob of reaching some terminal state from pred.
 					}
 					else {
 						// state was already discovered
 						// is this the better transition?
-						if(distances[*iter].second > transMat.getValue(*iter, *target)) {
-							distances[*iter].first = *target;
-							distances[*iter].second = transMat.getValue(*iter, *target);
+						if(distances[*iter].getValue() > transMat.getValue(*iter, *target)) {
+							distances[*iter].getColumn() = *target;
+							distances[*iter].getValue() = transMat.getValue(*iter, *target);
 						}
 					}
 				}
@@ -328,19 +328,19 @@ public:
 				//only use if allowed and not in subsys and not terminal
 				if(allowedStates.get(*iter) && !subSysStates.get(*iter) && !terminalStates.get(*iter)) {
 					//new state?
-					if(distances[*iter].second == (T) -1) {
+					if(distances[*iter].getValue() == (T) -1) {
 						// save as discovered and push into active set
-						distances[*iter].first = *sysState;												//successor
-						distances[*iter].second = transMat.getValue(*iter, *sysState);					//prob of shortest path
+						distances[*iter].getColumn() = *sysState;												//successor
+						distances[*iter].getValue() = transMat.getValue(*iter, *sysState);					//prob of shortest path
 
 						activeSet.insert(std::pair<uint_fast64_t, T>(*iter, probabilities[*iter]));		//prob of reaching some terminal state from pred.
 					}
 					else {
 						// state was already discovered
 						// is this the better transition?
-						if(distances[*iter].second > transMat.getValue(*iter, *sysState)) {
-							distances[*iter].first = *sysState;
-							distances[*iter].second = transMat.getValue(*iter, *sysState);
+						if(distances[*iter].getValue() > transMat.getValue(*iter, *sysState)) {
+							distances[*iter].getColumn() = *sysState;
+							distances[*iter].getValue() = transMat.getValue(*iter, *sysState);
 						}
 					}
 				}
@@ -355,7 +355,7 @@ public:
 		while(!activeSet.empty()) {
 			// copy here since using a reference leads to segfault
 			state = *(--activeSet.end());
-			activeState = state.first;
+			activeState = state.getColumn();
 			activeSet.erase(--activeSet.end());
 
 			//stop on the first subsys/init state
@@ -368,19 +368,19 @@ public:
 					//only if transition is not "virtual" and no selfloop
 					if(*iter != activeState && transMat.getValue(*iter, activeState) != (T) 0) {
 						//new state?
-						if(distances[*iter].second == (T) -1) {
+						if(distances[*iter].getValue() == (T) -1) {
 							// save as discovered and push into active set
-							distances[*iter].first = activeState;
-							distances[*iter].second = transMat.getValue(*iter, activeState) * distances[activeState].second;
+							distances[*iter].getColumn() = activeState;
+							distances[*iter].getValue() = transMat.getValue(*iter, activeState) * distances[activeState].getValue();
 
 							activeSet.insert(std::pair<uint_fast64_t, T>(*iter, probabilities[*iter]));
 						}
 						else {
 							// state was already discovered
 							// is this the better transition?
-							if(distances[*iter].second < transMat.getValue(*iter, activeState) * distances[activeState].second) {
-								distances[*iter].first = activeState;
-								distances[*iter].second = transMat.getValue(*iter, activeState) * distances[activeState].second;
+							if(distances[*iter].getValue() < transMat.getValue(*iter, activeState) * distances[activeState].getValue()) {
+								distances[*iter].getColumn() = activeState;
+								distances[*iter].getValue() = transMat.getValue(*iter, activeState) * distances[activeState].getValue();
 							}
 						}
 					}
@@ -389,15 +389,15 @@ public:
 		}
 
 		//get path probability
-		probability = distances[activeState].second;
+		probability = distances[activeState].getValue();
 		if(probability == (T) -1) probability = 1;
 
 		// iterate over the successors until reaching the end of the finite path
 		shortestPath.push_back(activeState);
-		activeState = distances[activeState].first;
+		activeState = distances[activeState].getColumn();
 		while(!terminalStates.get(activeState) && !subSysStates.get(activeState)) {
 			shortestPath.push_back(activeState);
-			activeState = distances[activeState].first;
+			activeState = distances[activeState].getColumn();
 		}
 		shortestPath.push_back(activeState);
 	}
@@ -482,7 +482,7 @@ public:
 		shortestPath.push_back(bestIndex);
 
 		//At last compensate for the distance between init and source state
-		probability = itSearch ? probability : probability / itDistances[bestIndex].second;
+		probability = itSearch ? probability : probability / itDistances[bestIndex].first;
 	}
 
 private:
diff --git a/src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.cpp b/src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.cpp
index 82590e31e..b5fa4bad4 100644
--- a/src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.cpp
+++ b/src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.cpp
@@ -5,4 +5,4 @@ bool SparseMarkovAutomatonCslModelCheckerOptionsRegistered = storm::settings::Se
 	instance->addOption(storm::settings::OptionBuilder("GmmxxLinearEquationSolver", "digiprecision", "", "Precision used for iterative solving of linear equation systems").addArgument(storm::settings::ArgumentBuilder::createDoubleArgument("precision value", "Precision").setDefaultValueDouble(1e-4).addValidationFunctionDouble(storm::settings::ArgumentValidators::doubleRangeValidatorExcluding(0.0, 1.0)).build()).build());
     
 	return true;
-});
\ No newline at end of file
+});
diff --git a/src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.h b/src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.h
index a4cd0844a..dbcf4d8b1 100644
--- a/src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.h
+++ b/src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.h
@@ -103,10 +103,10 @@ namespace storm {
                     for (auto state : markovianNonGoalStates) {
                         for (auto& element : aMarkovian.getRow(rowIndex)) {
                             ValueType eTerm = std::exp(-exitRates[state] * delta);
-                            if (element.first == rowIndex) {
-                                element.second = (storm::utility::constantOne<ValueType>() - eTerm) * element.second + eTerm;
+                            if (element.getColumn() == rowIndex) {
+                                element.getValue() = (storm::utility::constantOne<ValueType>() - eTerm) * element.getValue() + eTerm;
                             } else {
-                                element.second = (storm::utility::constantOne<ValueType>() - eTerm) * element.second;
+                                element.getValue() = (storm::utility::constantOne<ValueType>() - eTerm) * element.getValue();
                             }
                         }
                         ++rowIndex;
@@ -116,7 +116,7 @@ namespace storm {
                     rowIndex = 0;
                     for (auto state : markovianNonGoalStates) {
                         for (auto& element : aMarkovianToProbabilistic.getRow(rowIndex)) {
-                            element.second = (1 - std::exp(-exitRates[state] * delta)) * element.second;
+                            element.getValue() = (1 - std::exp(-exitRates[state] * delta)) * element.getValue();
                         }
                         ++rowIndex;
                     }
@@ -133,8 +133,8 @@ namespace storm {
                         bMarkovianFixed.push_back(storm::utility::constantZero<ValueType>());
                         
                         for (auto& element : transitionMatrix.getRowGroup(state)) {
-                            if (goalStates.get(element.first)) {
-                                bMarkovianFixed.back() += (1 - std::exp(-exitRates[state] * delta)) * element.second;
+                            if (goalStates.get(element.getColumn())) {
+                                bMarkovianFixed.back() += (1 - std::exp(-exitRates[state] * delta)) * element.getValue();
                             }
                         }
                     }
@@ -314,13 +314,13 @@ namespace storm {
                             b.push_back(storm::utility::constantZero<ValueType>());
                             
                             for (auto element : transitionMatrix.getRow(choice)) {
-                                if (statesNotContainedInAnyMec.get(element.first)) {
+                                if (statesNotContainedInAnyMec.get(element.getColumn())) {
                                     // If the target state is not contained in an MEC, we can copy over the entry.
-                                    sspMatrixBuilder.addNextValue(currentChoice, statesNotInMecsBeforeIndex[element.first], element.second);
+                                    sspMatrixBuilder.addNextValue(currentChoice, statesNotInMecsBeforeIndex[element.getColumn()], element.getValue());
                                 } else {
                                     // If the target state is contained in MEC i, we need to add the probability to the corresponding field in the vector
                                     // so that we are able to write the cumulative probability to the MEC into the matrix.
-                                    auxiliaryStateToProbabilityMap[stateToMecIndexMap[element.first]] += element.second;
+                                    auxiliaryStateToProbabilityMap[stateToMecIndexMap[element.getColumn()]] += element.getValue();
                                 }
                             }
                             
@@ -350,13 +350,13 @@ namespace storm {
                                     b.push_back(storm::utility::constantZero<ValueType>());
 
                                     for (auto element : transitionMatrix.getRow(choice)) {
-                                        if (statesNotContainedInAnyMec.get(element.first)) {
+                                        if (statesNotContainedInAnyMec.get(element.getColumn())) {
                                             // If the target state is not contained in an MEC, we can copy over the entry.
-                                            sspMatrixBuilder.addNextValue(currentChoice, statesNotInMecsBeforeIndex[element.first], element.second);
+                                            sspMatrixBuilder.addNextValue(currentChoice, statesNotInMecsBeforeIndex[element.getColumn()], element.getValue());
                                         } else {
                                             // If the target state is contained in MEC i, we need to add the probability to the corresponding field in the vector
                                             // so that we are able to write the cumulative probability to the MEC into the matrix.
-                                            auxiliaryStateToProbabilityMap[stateToMecIndexMap[element.first]] += element.second;
+                                            auxiliaryStateToProbabilityMap[stateToMecIndexMap[element.getColumn()]] += element.getValue();
                                         }
                                     }
                                     
@@ -428,61 +428,61 @@ namespace storm {
                  */
                 static ValueType computeLraForMaximalEndComponent(bool min, storm::storage::SparseMatrix<ValueType> const& transitionMatrix, std::vector<uint_fast64_t> const& nondeterministicChoiceIndices, storm::storage::BitVector const& markovianStates, std::vector<ValueType> const& exitRates, storm::storage::BitVector const& goalStates, storm::storage::MaximalEndComponent const& mec, uint_fast64_t mecIndex = 0) {
                     std::shared_ptr<storm::solver::LpSolver> solver = storm::utility::solver::getLpSolver("LRA for MEC");
-                    solver->setModelSense(min ? storm::solver::LpSolver::MAXIMIZE : storm::solver::LpSolver::MINIMIZE);
+                    solver->setModelSense(min ? storm::solver::LpSolver::ModelSense::Maximize : storm::solver::LpSolver::ModelSense::Minimize);
                     
                     // First, we need to create the variables for the problem.
-                    std::map<uint_fast64_t, uint_fast64_t> stateToVariableIndexMap;
+                    std::map<uint_fast64_t, std::string> stateToVariableNameMap;
                     for (auto const& stateChoicesPair : mec) {
-                        stateToVariableIndexMap[stateChoicesPair.first] = solver->createContinuousVariable("x" + std::to_string(stateChoicesPair.first), storm::solver::LpSolver::UNBOUNDED, 0, 0, 0);
+                        std::string variableName = "x" + std::to_string(stateChoicesPair.first);
+                        stateToVariableNameMap[stateChoicesPair.first] = variableName;
+                        solver->addUnboundedContinuousVariable(variableName);
                     }
-                    uint_fast64_t lraValueVariableIndex = solver->createContinuousVariable("k", storm::solver::LpSolver::UNBOUNDED, 0, 0, 1);
+                    solver->addUnboundedContinuousVariable("k", 1);
                     solver->update();
                     
                     // Now we encode the problem as constraints.
-                    std::vector<uint_fast64_t> variables;
-                    std::vector<double> coefficients;
                     for (auto const& stateChoicesPair : mec) {
                         uint_fast64_t state = stateChoicesPair.first;
                         
                         // Now, based on the type of the state, create a suitable constraint.
                         if (markovianStates.get(state)) {
-                            variables.clear();
-                            coefficients.clear();
-                            
-                            variables.push_back(stateToVariableIndexMap.at(state));
-                            coefficients.push_back(1);
+                            storm::expressions::Expression constraint = storm::expressions::Expression::createDoubleVariable(stateToVariableNameMap.at(state));
                             
                             for (auto element : transitionMatrix.getRow(nondeterministicChoiceIndices[state])) {
-                                variables.push_back(stateToVariableIndexMap.at(element.first));
-                                coefficients.push_back(-element.second);
+                                constraint = constraint - storm::expressions::Expression::createDoubleVariable(stateToVariableNameMap.at(element.getColumn()));
                             }
                             
-                            variables.push_back(lraValueVariableIndex);
-                            coefficients.push_back(storm::utility::constantOne<ValueType>() / exitRates[state]);
-                            
-                            solver->addConstraint("state" + std::to_string(state), variables, coefficients, min ? storm::solver::LpSolver::LESS_EQUAL : storm::solver::LpSolver::GREATER_EQUAL, goalStates.get(state) ? storm::utility::constantOne<ValueType>() / exitRates[state] : storm::utility::constantZero<ValueType>());
+                            constraint = constraint + storm::expressions::Expression::createDoubleLiteral(storm::utility::constantOne<ValueType>() / exitRates[state]) * storm::expressions::Expression::createDoubleVariable("k");
+                            storm::expressions::Expression rightHandSide = goalStates.get(state) ? storm::expressions::Expression::createDoubleLiteral(storm::utility::constantOne<ValueType>() / exitRates[state]) : storm::expressions::Expression::createDoubleLiteral(storm::utility::constantZero<ValueType>());
+                            if (min) {
+                                constraint = constraint <= rightHandSide;
+                            } else {
+                                constraint = constraint >= rightHandSide;
+                            }
+                            solver->addConstraint("state" + std::to_string(state), constraint);
                         } else {
                             // For probabilistic states, we want to add the constraint x_s <= sum P(s, a, s') * x_s' where a is the current action
                             // and the sum ranges over all states s'.
                             for (auto choice : stateChoicesPair.second) {
-                                variables.clear();
-                                coefficients.clear();
-                                
-                                variables.push_back(stateToVariableIndexMap.at(state));
-                                coefficients.push_back(1);
-                                
+                                storm::expressions::Expression constraint = storm::expressions::Expression::createDoubleVariable(stateToVariableNameMap.at(state));
+
                                 for (auto element : transitionMatrix.getRow(choice)) {
-                                    variables.push_back(stateToVariableIndexMap.at(element.first));
-                                    coefficients.push_back(-element.second);
+                                    constraint = constraint - storm::expressions::Expression::createDoubleVariable(stateToVariableNameMap.at(element.getColumn()));
                                 }
                                 
-                                solver->addConstraint("state" + std::to_string(state), variables, coefficients, min ? storm::solver::LpSolver::LESS_EQUAL : storm::solver::LpSolver::GREATER_EQUAL, storm::utility::constantZero<ValueType>());
+                                storm::expressions::Expression rightHandSide = storm::expressions::Expression::createDoubleLiteral(storm::utility::constantZero<ValueType>());
+                                if (min) {
+                                    constraint = constraint <= rightHandSide;
+                                } else {
+                                    constraint = constraint >= rightHandSide;
+                                }
+                                solver->addConstraint("state" + std::to_string(state), constraint);
                             }
                         }
                     }
                     
                     solver->optimize();
-                    return solver->getContinuousValue(lraValueVariableIndex);
+                    return solver->getContinuousValue("k");
                 }
                 
                 /*!
diff --git a/src/models/AbstractDeterministicModel.h b/src/models/AbstractDeterministicModel.h
index c72b914de..88e0d1006 100644
--- a/src/models/AbstractDeterministicModel.h
+++ b/src/models/AbstractDeterministicModel.h
@@ -87,9 +87,9 @@ class AbstractDeterministicModel: public AbstractModel<T> {
             for (uint_fast64_t i = 0; i < this->transitionMatrix.getRowCount(); ++i, ++rowIt) {
                 typename storm::storage::SparseMatrix<T>::const_rows row = this->transitionMatrix.getRow(i);
                 for (auto const& transition : row) {
-                    if (transition.second != storm::utility::constantZero<T>()) {
-                        if (subsystem == nullptr || subsystem->get(transition.first)) {
-                            outStream << "\t" << i << " -> " << transition.first << " [ label= \"" << transition.second << "\" ];" << std::endl;
+                    if (transition.getValue() != storm::utility::constantZero<T>()) {
+                        if (subsystem == nullptr || subsystem->get(transition.getColumn())) {
+                            outStream << "\t" << i << " -> " << transition.getColumn() << " [ label= \"" << transition.getValue() << "\" ];" << std::endl;
                         }
                     }
                 }
diff --git a/src/models/AbstractModel.h b/src/models/AbstractModel.h
index 301c806d3..05296c584 100644
--- a/src/models/AbstractModel.h
+++ b/src/models/AbstractModel.h
@@ -220,7 +220,7 @@ class AbstractModel: public std::enable_shared_from_this<AbstractModel<T>> {
 		 * @return The number of (non-zero) transitions of the model.
 		 */
 		virtual uint_fast64_t getNumberOfTransitions() const {
-			return this->getTransitionMatrix().getEntryCount();
+			return this->getTransitionMatrix().getNonzeroEntryCount();
 		}
 
         /*!
diff --git a/src/models/AbstractNondeterministicModel.h b/src/models/AbstractNondeterministicModel.h
index 0058377a6..3a738cbe6 100644
--- a/src/models/AbstractNondeterministicModel.h
+++ b/src/models/AbstractNondeterministicModel.h
@@ -189,8 +189,8 @@ namespace storm {
             
                         // Now draw all probabilitic arcs that belong to this nondeterminstic choice.
                         for (auto const& transition : row) {
-                            if (subsystem == nullptr || subsystem->get(transition.first)) {
-                                outStream << "\t\"" << state << "c" << choice << "\" -> " << transition.first << " [ label= \"" << transition.second << "\" ]";
+                            if (subsystem == nullptr || subsystem->get(transition.getColumn())) {
+                                outStream << "\t\"" << state << "c" << choice << "\" -> " << transition.getColumn() << " [ label= \"" << transition.getValue() << "\" ]";
                                 
                                 // If we were given a scheduler to highlight, we do so now.
                                 if (scheduler != nullptr) {
diff --git a/src/models/Dtmc.h b/src/models/Dtmc.h
index b599774f9..eb0e669dd 100644
--- a/src/models/Dtmc.h
+++ b/src/models/Dtmc.h
@@ -170,7 +170,7 @@ public:
 		for(uint_fast64_t row = 0; row < origMat.getRowCount(); ++row) {
 			if(subSysStates.get(row)){
 				for(auto const& entry : origMat.getRow(row)) {
-					if(subSysStates.get(entry.first)) {
+					if(subSysStates.get(entry.getColumn())) {
 						subSysTransitionCount++;	
 					} 
 				}
@@ -198,10 +198,10 @@ public:
 			if(subSysStates.get(row)){
 				// Transfer transitions
 				for(auto& entry : origMat.getRow(row)) {
-					if(subSysStates.get(entry.first)) {
-						newMatBuilder.addNextValue(newRow, stateMapping[entry.first], entry.second);
+					if(subSysStates.get(entry.getColumn())) {
+						newMatBuilder.addNextValue(newRow, stateMapping[entry.getColumn()], entry.getValue());
 					} else {
-						rest += entry.second;
+						rest += entry.getValue();
 					}
 				}
 
@@ -251,8 +251,8 @@ public:
 				if(subSysStates.get(row)){
 					// Transfer transition rewards
 					for(auto& entry : this->getTransitionRewardMatrix().getRow(row)) {
-						if(subSysStates.get(entry.first)) {
-							newTransRewardsBuilder.addNextValue(newRow, stateMapping[entry.first], entry.second);
+						if(subSysStates.get(entry.getColumn())) {
+							newTransRewardsBuilder.addNextValue(newRow, stateMapping[entry.getColumn()], entry.getValue());
 						}
 					}
 
diff --git a/src/models/MarkovAutomaton.h b/src/models/MarkovAutomaton.h
index 22b6ab13f..89ce7ccee 100644
--- a/src/models/MarkovAutomaton.h
+++ b/src/models/MarkovAutomaton.h
@@ -147,7 +147,7 @@ namespace storm {
 
                         for (uint_fast64_t row = this->getTransitionMatrix().getRowGroupIndices()[state] + (this->isHybridState(state) ? 1 : 0); row < this->getTransitionMatrix().getRowGroupIndices()[state + 1]; ++row) {
                             for (auto const& entry : this->transitionMatrix.getRow(row)) {
-                                newTransitionMatrixBuilder.addNextValue(currentChoice, entry.first, entry.second);
+                                newTransitionMatrixBuilder.addNextValue(currentChoice, entry.getColumn(), entry.getValue());
                             }
                             ++currentChoice;
                         }
@@ -220,8 +220,8 @@ namespace storm {
                     
                     // Now draw all probabilitic arcs that belong to this nondeterminstic choice.
                     for (auto const& transition : row) {
-                        if (subsystem == nullptr || subsystem->get(transition.first)) {
-                            outStream << "\t\"" << state << "c" << choice << "\" -> " << transition.first << " [ label= \"" << transition.second << "\" ]";
+                        if (subsystem == nullptr || subsystem->get(transition.getColumn())) {
+                            outStream << "\t\"" << state << "c" << choice << "\" -> " << transition.getColumn() << " [ label= \"" << transition.getValue() << "\" ]";
                             
                             // If we were given a scheduler to highlight, we do so now.
                             if (scheduler != nullptr) {
@@ -237,8 +237,8 @@ namespace storm {
                     } else {
                         // In this case we are emitting a Markovian choice, so draw the arrows directly to the target states.
                         for (auto const& transition : row) {
-                            if (subsystem == nullptr || subsystem->get(transition.first)) {
-                                outStream << "\t\"" << state << "\" -> " << transition.first << " [ label= \"" << transition.second << " (" << this->exitRates[state] << ")\" ]";
+                            if (subsystem == nullptr || subsystem->get(transition.getColumn())) {
+                                outStream << "\t\"" << state << "\" -> " << transition.getColumn() << " [ label= \"" << transition.getValue() << " (" << this->exitRates[state] << ")\" ]";
                             }
                         }
                     }
@@ -259,7 +259,7 @@ namespace storm {
             void turnRatesToProbabilities() {
                 for (auto state : this->markovianStates) {
                     for (auto& transition : this->transitionMatrix.getRowGroup(state)) {
-                        transition.second /= this->exitRates[state];
+                        transition.getValue() /= this->exitRates[state];
                     }
                 }
             }
diff --git a/src/solver/GlpkLpSolver.cpp b/src/solver/GlpkLpSolver.cpp
index e122fcb16..2a9eec7f6 100644
--- a/src/solver/GlpkLpSolver.cpp
+++ b/src/solver/GlpkLpSolver.cpp
@@ -4,13 +4,12 @@
 
 #include <iostream>
 
-#include "src/exceptions/InvalidStateException.h"
-#include "src/settings/Settings.h"
-
-#include "log4cplus/logger.h"
-#include "log4cplus/loggingmacros.h"
+#include "src/storage/expressions/LinearCoefficientVisitor.h"
 
-extern log4cplus::Logger logger;
+#include "src/settings/Settings.h"
+#include "src/exceptions/ExceptionMacros.h"
+#include "src/exceptions/InvalidAccessException.h"
+#include "src/exceptions/InvalidStateException.h"
 
 bool GlpkLpSolverOptionsRegistered = storm::settings::Settings::registerNewModule([] (storm::settings::Settings* instance) -> bool {
 	instance->addOption(storm::settings::OptionBuilder("GlpkLpSolver", "glpkoutput", "", "If set, the glpk output will be printed to the command line.").build());
@@ -22,7 +21,7 @@ bool GlpkLpSolverOptionsRegistered = storm::settings::Settings::registerNewModul
 
 namespace storm {
     namespace solver {
-        GlpkLpSolver::GlpkLpSolver(std::string const& name, ModelSense const& modelSense) : LpSolver(modelSense), lp(nullptr), nextVariableIndex(1), nextConstraintIndex(1), modelContainsIntegerVariables(false), isInfeasibleFlag(false), isUnboundedFlag(false), rowIndices(), columnIndices(), coefficientValues() {
+        GlpkLpSolver::GlpkLpSolver(std::string const& name, ModelSense const& modelSense) : LpSolver(modelSense), lp(nullptr), variableNameToIndexMap(), nextVariableIndex(1), nextConstraintIndex(1), modelContainsIntegerVariables(false), isInfeasibleFlag(false), isUnboundedFlag(false), rowIndices(), columnIndices(), coefficientValues() {
             // Create the LP problem for glpk.
             lp = glp_create_prob();
             
@@ -38,11 +37,11 @@ namespace storm {
             coefficientValues.push_back(0);
         }
         
-        GlpkLpSolver::GlpkLpSolver(std::string const& name) : GlpkLpSolver(name, MINIMIZE) {
+        GlpkLpSolver::GlpkLpSolver(std::string const& name) : GlpkLpSolver(name, ModelSense::Minimize) {
             // Intentionally left empty.
         }
         
-        GlpkLpSolver::GlpkLpSolver() : GlpkLpSolver("", MINIMIZE) {
+        GlpkLpSolver::GlpkLpSolver() : GlpkLpSolver("", ModelSense::Minimize) {
             // Intentionally left empty.
         }
         
@@ -56,76 +55,120 @@ namespace storm {
             glp_free_env();
         }
         
-        uint_fast64_t GlpkLpSolver::createContinuousVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) {
-            glp_add_cols(this->lp, 1);
-            glp_set_col_name(this->lp, nextVariableIndex, name.c_str());
-            switch (variableType) {
-                case LpSolver::BOUNDED:
-                    glp_set_col_bnds(lp, nextVariableIndex, GLP_DB, lowerBound, upperBound);
-                    break;
-                case LpSolver::UNBOUNDED:
-                    glp_set_col_bnds(lp, nextVariableIndex, GLP_FR, 0, 0);
-                    break;
-                case LpSolver::UPPER_BOUND:
-                    glp_set_col_bnds(lp, nextVariableIndex, GLP_UP, 0, upperBound);
-                    break;
-                case LpSolver::LOWER_BOUND:
-                    glp_set_col_bnds(lp, nextVariableIndex, GLP_LO, lowerBound, 0);
-                    break;
-            }
-            glp_set_col_kind(this->lp, nextVariableIndex, GLP_CV);
-            glp_set_obj_coef(this->lp, nextVariableIndex, objectiveFunctionCoefficient);
-            ++nextVariableIndex;
-            
-            this->currentModelHasBeenOptimized = false;
-            return nextVariableIndex - 1;
+        void GlpkLpSolver::addBoundedContinuousVariable(std::string const& name, double lowerBound, double upperBound, double objectiveFunctionCoefficient) {
+            this->addVariable(name, GLP_CV, GLP_DB, lowerBound, upperBound, objectiveFunctionCoefficient);
+        }
+        
+        void GlpkLpSolver::addLowerBoundedContinuousVariable(std::string const& name, double lowerBound, double objectiveFunctionCoefficient) {
+            this->addVariable(name, GLP_CV, GLP_LO, lowerBound, 0, objectiveFunctionCoefficient);
+        }
+        
+        void GlpkLpSolver::addUpperBoundedContinuousVariable(std::string const& name, double upperBound, double objectiveFunctionCoefficient) {
+            this->addVariable(name, GLP_CV, GLP_UP, 0, upperBound, objectiveFunctionCoefficient);
         }
         
-        uint_fast64_t GlpkLpSolver::createIntegerVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) {
-            uint_fast64_t index = this->createContinuousVariable(name, variableType, lowerBound, upperBound, objectiveFunctionCoefficient);
-            glp_set_col_kind(this->lp, index, GLP_IV);
+        void GlpkLpSolver::addUnboundedContinuousVariable(std::string const& name, double objectiveFunctionCoefficient) {
+            this->addVariable(name, GLP_CV, GLP_FR, 0, 0, objectiveFunctionCoefficient);
+        }
+        
+        void GlpkLpSolver::addBoundedIntegerVariable(std::string const& name, double lowerBound, double upperBound, double objectiveFunctionCoefficient) {
+            this->addVariable(name, GLP_IV, GLP_DB, lowerBound, upperBound, objectiveFunctionCoefficient);
+            this->modelContainsIntegerVariables = true;
+        }
+        
+        void GlpkLpSolver::addLowerBoundedIntegerVariable(std::string const& name, double lowerBound, double objectiveFunctionCoefficient) {
+            this->addVariable(name, GLP_IV, GLP_LO, lowerBound, 0, objectiveFunctionCoefficient);
+            this->modelContainsIntegerVariables = true;
+        }
+
+        void GlpkLpSolver::addUpperBoundedIntegerVariable(std::string const& name, double upperBound, double objectiveFunctionCoefficient) {
+            this->addVariable(name, GLP_IV, GLP_UP, 0, upperBound, objectiveFunctionCoefficient);
+            this->modelContainsIntegerVariables = true;
+        }
+        
+        void GlpkLpSolver::addUnboundedIntegerVariable(std::string const& name, double objectiveFunctionCoefficient) {
+            this->addVariable(name, GLP_IV, GLP_FR, 0, 0, objectiveFunctionCoefficient);
             this->modelContainsIntegerVariables = true;
-            return index;
         }
         
-        uint_fast64_t GlpkLpSolver::createBinaryVariable(std::string const& name, double objectiveFunctionCoefficient) {
-            uint_fast64_t index = this->createContinuousVariable(name, UNBOUNDED, 0, 1, objectiveFunctionCoefficient);
-            glp_set_col_kind(this->lp, index, GLP_BV);
+        void GlpkLpSolver::addBinaryVariable(std::string const& name, double objectiveFunctionCoefficient) {
+            this->addVariable(name, GLP_BV, GLP_FR, 0, 0, objectiveFunctionCoefficient);
             this->modelContainsIntegerVariables = true;
-            return index;
+        }
+        
+        void GlpkLpSolver::addVariable(std::string const& name, int variableType, int boundType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) {
+            // Check whether variable already exists.
+            auto nameIndexPair = this->variableNameToIndexMap.find(name);
+            LOG_THROW(nameIndexPair == this->variableNameToIndexMap.end(), storm::exceptions::InvalidArgumentException, "Variable '" << nameIndexPair->first << "' already exists.");
+            
+            // Check for valid variable type.
+            LOG_ASSERT(variableType == GLP_CV || variableType == GLP_IV || variableType == GLP_BV, "Illegal type '" << variableType << "' for glpk variable.");
+            
+            // Check for valid bound type.
+            LOG_ASSERT(boundType == GLP_FR || boundType == GLP_UP || boundType == GLP_LO || boundType == GLP_DB, "Illegal bound type for variable '" << name << "'.");
+            
+            // Finally, create the actual variable.
+            glp_add_cols(this->lp, 1);
+            glp_set_col_name(this->lp, nextVariableIndex, name.c_str());
+            glp_set_col_bnds(lp, nextVariableIndex, boundType, lowerBound, upperBound);
+            glp_set_col_kind(this->lp, nextVariableIndex, variableType);
+            glp_set_obj_coef(this->lp, nextVariableIndex, objectiveFunctionCoefficient);
+            this->variableNameToIndexMap.emplace(name, this->nextVariableIndex);
+            ++this->nextVariableIndex;
         }
         
         void GlpkLpSolver::update() const {
             // Intentionally left empty.
         }
         
-        void GlpkLpSolver::addConstraint(std::string const& name, std::vector<uint_fast64_t> const& variables, std::vector<double> const& coefficients, BoundType const& boundType, double rightHandSideValue) {
-            if (variables.size() != coefficients.size()) {
-                LOG4CPLUS_ERROR(logger, "Sizes of variable indices vector and coefficients vector do not match.");
-                throw storm::exceptions::InvalidStateException() << "Sizes of variable indices vector and coefficients vector do not match.";
-            }
-
+        void GlpkLpSolver::addConstraint(std::string const& name, storm::expressions::Expression const& constraint) {
             // Add the row that will represent this constraint.
             glp_add_rows(this->lp, 1);
             glp_set_row_name(this->lp, nextConstraintIndex, name.c_str());
             
+            LOG_THROW(constraint.isRelationalExpression(), storm::exceptions::InvalidArgumentException, "Illegal constraint is not a relational expression.");
+            LOG_THROW(constraint.getOperator() != storm::expressions::OperatorType::NotEqual, storm::exceptions::InvalidArgumentException, "Illegal constraint uses inequality operator.");
+            
+            std::pair<storm::expressions::SimpleValuation, double> leftCoefficients = storm::expressions::LinearCoefficientVisitor().getLinearCoefficients(constraint.getOperand(0));
+            std::pair<storm::expressions::SimpleValuation, double> rightCoefficients = storm::expressions::LinearCoefficientVisitor().getLinearCoefficients(constraint.getOperand(1));
+            for (auto const& identifier : rightCoefficients.first.getDoubleIdentifiers()) {
+                if (leftCoefficients.first.containsDoubleIdentifier(identifier)) {
+                    leftCoefficients.first.setDoubleValue(identifier, leftCoefficients.first.getDoubleValue(identifier) - rightCoefficients.first.getDoubleValue(identifier));
+                } else {
+                    leftCoefficients.first.addDoubleIdentifier(identifier, -rightCoefficients.first.getDoubleValue(identifier));
+                }
+            }
+            rightCoefficients.second -= leftCoefficients.second;
+            
+            // Now we need to transform the coefficients to the vector representation.
+            std::vector<int> variables;
+            std::vector<double> coefficients;
+            for (auto const& identifier : leftCoefficients.first.getDoubleIdentifiers()) {
+                auto identifierIndexPair = this->variableNameToIndexMap.find(identifier);
+                LOG_THROW(identifierIndexPair != this->variableNameToIndexMap.end(), storm::exceptions::InvalidArgumentException, "Constraint contains illegal identifier '" << identifier << "'.");
+                variables.push_back(identifierIndexPair->second);
+                coefficients.push_back(leftCoefficients.first.getDoubleValue(identifier));
+            }
+            
             // Determine the type of the constraint and add it properly.
-            switch (boundType) {
-                case LESS:
-                    glp_set_row_bnds(this->lp, nextConstraintIndex, GLP_UP, 0, rightHandSideValue - storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble());
+            switch (constraint.getOperator()) {
+                case storm::expressions::OperatorType::Less:
+                    glp_set_row_bnds(this->lp, nextConstraintIndex, GLP_UP, 0, rightCoefficients.second - storm::settings::Settings::getInstance()->getOptionByLongName("glpkinttol").getArgument(0).getValueAsDouble());
                     break;
-                case LESS_EQUAL:
-                    glp_set_row_bnds(this->lp, nextConstraintIndex, GLP_UP, 0, rightHandSideValue);
+                case storm::expressions::OperatorType::LessOrEqual:
+                    glp_set_row_bnds(this->lp, nextConstraintIndex, GLP_UP, 0, rightCoefficients.second);
                     break;
-                case GREATER:
-                    glp_set_row_bnds(this->lp, nextConstraintIndex, GLP_LO, rightHandSideValue + storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble(), 0);
+                case storm::expressions::OperatorType::Greater:
+                    glp_set_row_bnds(this->lp, nextConstraintIndex, GLP_LO, rightCoefficients.second + storm::settings::Settings::getInstance()->getOptionByLongName("glpkinttol").getArgument(0).getValueAsDouble(), 0);
                     break;
-                case GREATER_EQUAL:
-                    glp_set_row_bnds(this->lp, nextConstraintIndex, GLP_LO, rightHandSideValue, 0);
+                case storm::expressions::OperatorType::GreaterOrEqual:
+                    glp_set_row_bnds(this->lp, nextConstraintIndex, GLP_LO, rightCoefficients.second, 0);
                     break;
-                case EQUAL:
-                    glp_set_row_bnds(this->lp, nextConstraintIndex, GLP_FX, rightHandSideValue, rightHandSideValue);
+                case storm::expressions::OperatorType::Equal:
+                    glp_set_row_bnds(this->lp, nextConstraintIndex, GLP_FX, rightCoefficients.second, rightCoefficients.second);
                     break;
+                default:
+                    LOG_ASSERT(false, "Illegal operator in LP solver constraint.");
             }
             
             // Record the variables and coefficients in the coefficient matrix.
@@ -143,13 +186,13 @@ namespace storm {
             this->isUnboundedFlag = false;
             
             // Start by setting the model sense.
-            glp_set_obj_dir(this->lp, this->getModelSense() == MINIMIZE ? GLP_MIN : GLP_MAX);
+            glp_set_obj_dir(this->lp, this->getModelSense() == LpSolver::ModelSense::Minimize ? GLP_MIN : GLP_MAX);
             
             glp_load_matrix(this->lp, rowIndices.size() - 1, rowIndices.data(), columnIndices.data(), coefficientValues.data());
             
             int error = 0;
             if (this->modelContainsIntegerVariables) {
-                glp_iocp* parameters = new glp_iocp;
+                glp_iocp* parameters = new glp_iocp();
                 glp_init_iocp(parameters);
                 parameters->presolve = GLP_ON;
                 parameters->tol_int = storm::settings::Settings::getInstance()->getOptionByLongName("glpkinttol").getArgument(0).getValueAsDouble();
@@ -171,11 +214,7 @@ namespace storm {
                 error = glp_simplex(this->lp, nullptr);
             }
             
-            if (error != 0) {
-                LOG4CPLUS_ERROR(logger, "Unable to optimize glpk model (" << error << ").");
-                throw storm::exceptions::InvalidStateException() << "Unable to optimize glpk model (" << error << ").";
-            }
-            
+            LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to optimize glpk model (" << error << ").");
             this->currentModelHasBeenOptimized = true;
         }
         
@@ -217,103 +256,75 @@ namespace storm {
             return status == GLP_OPT;
         }
         
-        int_fast64_t GlpkLpSolver::getIntegerValue(uint_fast64_t variableIndex) const {
+        double GlpkLpSolver::getContinuousValue(std::string const& name) const {
             if (!this->isOptimal()) {
-                if (this->isInfeasible()) {
-                    LOG4CPLUS_ERROR(logger, "Unable to get glpk solution from infeasible model.");
-                    throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from infeasible model.";
-                } else if (this->isUnbounded()) {
-                    LOG4CPLUS_ERROR(logger, "Unable to get glpk solution from unbounded model.");
-                    throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unbounded model.";
-                } else {
-                    LOG4CPLUS_ERROR(logger, "Unable to get glpk solution from unoptimized model.");
-                    throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unoptimized model.";
-                }
+                LOG_THROW(!this->isInfeasible(), storm::exceptions::InvalidAccessException, "Unable to get glpk solution from infeasible model.");
+                LOG_THROW(!this->isUnbounded(),  storm::exceptions::InvalidAccessException, "Unable to get glpk solution from unbounded model.");
+                LOG_THROW(false, storm::exceptions::InvalidAccessException, "Unable to get glpk solution from unoptimized model.");
             }
-           
+            
+            auto variableIndexPair = this->variableNameToIndexMap.find(name);
+            LOG_THROW(variableIndexPair != this->variableNameToIndexMap.end(), storm::exceptions::InvalidAccessException, "Accessing value of unknown variable '" << name << "'.");
+            
             double value = 0;
             if (this->modelContainsIntegerVariables) {
-                value = glp_mip_col_val(this->lp, static_cast<int>(variableIndex));
+                value = glp_mip_col_val(this->lp, static_cast<int>(variableIndexPair->second));
             } else {
-                value = glp_get_col_prim(this->lp, static_cast<int>(variableIndex));
-            }
-
-            if (std::abs(value - static_cast<int>(value)) <= storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()) {
-                // Nothing to do in this case.
-            } else if (std::abs(value) > storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()) {
-                LOG4CPLUS_ERROR(logger, "Illegal value for integer variable in glpk solution (" << value << ").");
-                throw storm::exceptions::InvalidStateException() << "Illegal value for integer variable in glpk solution (" << value << ").";
+                value = glp_get_col_prim(this->lp, static_cast<int>(variableIndexPair->second));
             }
-            
-            return static_cast<int_fast64_t>(value);
+            return value;
         }
         
-        bool GlpkLpSolver::getBinaryValue(uint_fast64_t variableIndex) const {
+        int_fast64_t GlpkLpSolver::getIntegerValue(std::string const& name) const {
             if (!this->isOptimal()) {
-                if (this->isInfeasible()) {
-                    LOG4CPLUS_ERROR(logger, "Unable to get glpk solution from infeasible model.");
-                    throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from infeasible model.";
-                } else if (this->isUnbounded()) {
-                    LOG4CPLUS_ERROR(logger, "Unable to get glpk solution from unbounded model.");
-                    throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unbounded model.";
-                } else {
-                    LOG4CPLUS_ERROR(logger, "Unable to get glpk solution from unoptimized model.");
-                    throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unoptimized model.";
-                }
+                LOG_THROW(!this->isInfeasible(), storm::exceptions::InvalidAccessException, "Unable to get glpk solution from infeasible model.");
+                LOG_THROW(!this->isUnbounded(),  storm::exceptions::InvalidAccessException, "Unable to get glpk solution from unbounded model.");
+                LOG_THROW(false, storm::exceptions::InvalidAccessException, "Unable to get glpk solution from unoptimized model.");
             }
+           
+            auto variableIndexPair = this->variableNameToIndexMap.find(name);
+            LOG_THROW(variableIndexPair != this->variableNameToIndexMap.end(), storm::exceptions::InvalidAccessException, "Accessing value of unknown variable '" << name << "'.");
 
             double value = 0;
             if (this->modelContainsIntegerVariables) {
-                value = glp_mip_col_val(this->lp, static_cast<int>(variableIndex));
+                value = glp_mip_col_val(this->lp, static_cast<int>(variableIndexPair->second));
             } else {
-                value = glp_get_col_prim(this->lp, static_cast<int>(variableIndex));
+                value = glp_get_col_prim(this->lp, static_cast<int>(variableIndexPair->second));
             }
 
-            if (std::abs(value - 1) <= storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()) {
-                // Nothing to do in this case.
-            } else if (std::abs(value) > storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()) {
-                LOG4CPLUS_ERROR(logger, "Illegal value for binary variable in Gurobi solution (" << value << ").");
-                throw storm::exceptions::InvalidStateException() << "Illegal value for binary variable in Gurobi solution (" << value << ").";
-            }
+            // Now check the desired precision was actually achieved.
+            LOG_THROW(std::abs(static_cast<int>(value) - value) <= storm::settings::Settings::getInstance()->getOptionByLongName("glpkinttol").getArgument(0).getValueAsDouble(), storm::exceptions::InvalidStateException, "Illegal value for integer variable in glpk solution (" << value << ").");
             
-            return static_cast<bool>(value);
+            return static_cast<int_fast64_t>(value);
         }
         
-        double GlpkLpSolver::getContinuousValue(uint_fast64_t variableIndex) const {
+        bool GlpkLpSolver::getBinaryValue(std::string const& name) const {
             if (!this->isOptimal()) {
-                if (this->isInfeasible()) {
-                    LOG4CPLUS_ERROR(logger, "Unable to get glpk solution from infeasible model.");
-                    throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from infeasible model.";
-                } else if (this->isUnbounded()) {
-                    LOG4CPLUS_ERROR(logger, "Unable to get glpk solution from unbounded model.");
-                    throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unbounded model.";
-                } else {
-                    LOG4CPLUS_ERROR(logger, "Unable to get glpk solution from unoptimized model.");
-                    throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unoptimized model.";
-                }
+                LOG_THROW(!this->isInfeasible(), storm::exceptions::InvalidAccessException, "Unable to get glpk solution from infeasible model.");
+                LOG_THROW(!this->isUnbounded(),  storm::exceptions::InvalidAccessException, "Unable to get glpk solution from unbounded model.");
+                LOG_THROW(false, storm::exceptions::InvalidAccessException, "Unable to get glpk solution from unoptimized model.");
             }
 
+            auto variableIndexPair = this->variableNameToIndexMap.find(name);
+            LOG_THROW(variableIndexPair != this->variableNameToIndexMap.end(), storm::exceptions::InvalidAccessException, "Accessing value of unknown variable '" << name << "'.");
+            
             double value = 0;
             if (this->modelContainsIntegerVariables) {
-                value = glp_mip_col_val(this->lp, static_cast<int>(variableIndex));
+                value = glp_mip_col_val(this->lp, static_cast<int>(variableIndexPair->second));
             } else {
-                value = glp_get_col_prim(this->lp, static_cast<int>(variableIndex));
+                value = glp_get_col_prim(this->lp, static_cast<int>(variableIndexPair->second));
             }
-            return value;
+
+            LOG_THROW(std::abs(static_cast<int>(value) - value) <= storm::settings::Settings::getInstance()->getOptionByLongName("glpkinttol").getArgument(0).getValueAsDouble(), storm::exceptions::InvalidStateException, "Illegal value for binary variable in glpk solution (" << value << ").");
+            
+            return static_cast<bool>(value);
         }
         
         double GlpkLpSolver::getObjectiveValue() const {
             if (!this->isOptimal()) {
-                if (this->isInfeasible()) {
-                    LOG4CPLUS_ERROR(logger, "Unable to get glpk solution from infeasible model.");
-                    throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from infeasible model.";
-                } else if (this->isUnbounded()) {
-                    LOG4CPLUS_ERROR(logger, "Unable to get glpk solution from unbounded model.");
-                    throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unbounded model.";
-                } else {
-                    LOG4CPLUS_ERROR(logger, "Unable to get glpk solution from unoptimized model.");
-                    throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unoptimized model.";
-                }
+                LOG_THROW(!this->isInfeasible(), storm::exceptions::InvalidAccessException, "Unable to get glpk solution from infeasible model.");
+                LOG_THROW(!this->isUnbounded(),  storm::exceptions::InvalidAccessException, "Unable to get glpk solution from unbounded model.");
+                LOG_THROW(false, storm::exceptions::InvalidAccessException, "Unable to get glpk solution from unoptimized model.");
             }
 
             double value = 0;
diff --git a/src/solver/GlpkLpSolver.h b/src/solver/GlpkLpSolver.h
index 2bc2bf397..0931e1ca5 100644
--- a/src/solver/GlpkLpSolver.h
+++ b/src/solver/GlpkLpSolver.h
@@ -14,7 +14,6 @@
 namespace storm {
     namespace solver {
 #ifdef STORM_HAVE_GLPK
-        
         /*!
          * A class that implements the LpSolver interface using glpk as the background solver.
          */
@@ -56,34 +55,66 @@ namespace storm {
              */
             virtual ~GlpkLpSolver();
             
-            virtual uint_fast64_t createContinuousVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) override;
-            virtual uint_fast64_t createIntegerVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) override;
-            virtual uint_fast64_t createBinaryVariable(std::string const& name, double objectiveFunctionCoefficient) override;
+            // Methods to add continuous variables.
+            virtual void addBoundedContinuousVariable(std::string const& name, double lowerBound, double upperBound, double objectiveFunctionCoefficient = 0) override;
+            virtual void addLowerBoundedContinuousVariable(std::string const& name, double lowerBound, double objectiveFunctionCoefficient = 0) override;
+            virtual void addUpperBoundedContinuousVariable(std::string const& name, double upperBound, double objectiveFunctionCoefficient = 0) override;
+            virtual void addUnboundedContinuousVariable(std::string const& name, double objectiveFunctionCoefficient = 0) override;
+            
+            // Methods to add integer variables.
+            virtual void addBoundedIntegerVariable(std::string const& name, double lowerBound, double upperBound, double objectiveFunctionCoefficient = 0) override;
+            virtual void addLowerBoundedIntegerVariable(std::string const& name, double lowerBound, double objectiveFunctionCoefficient = 0) override;
+            virtual void addUpperBoundedIntegerVariable(std::string const& name, double upperBound, double objectiveFunctionCoefficient = 0) override;
+            virtual void addUnboundedIntegerVariable(std::string const& name, double objectiveFunctionCoefficient = 0) override;
+
+            // Methods to add binary variables.
+            virtual void addBinaryVariable(std::string const& name, double objectiveFunctionCoefficient = 0) override;
+
+            // Methods to incorporate recent changes.
             virtual void update() const override;
             
-            virtual void addConstraint(std::string const& name, std::vector<uint_fast64_t> const& variables, std::vector<double> const& coefficients, BoundType const& boundType, double rightHandSideValue) override;
+            // Methods to add constraints
+            virtual void addConstraint(std::string const& name, storm::expressions::Expression const& constraint) override;
             
+            // Methods to optimize and retrieve optimality status.
             virtual void optimize() const override;
             virtual bool isInfeasible() const override;
             virtual bool isUnbounded() const override;
             virtual bool isOptimal() const override;
 
-            virtual int_fast64_t getIntegerValue(uint_fast64_t variableIndex) const override;
-            virtual bool getBinaryValue(uint_fast64_t variableIndex) const override;
-            virtual double getContinuousValue(uint_fast64_t variableIndex) const override;
+            // Methods to retrieve values of variables and the objective function in the optimal solutions.
+            virtual double getContinuousValue(std::string const& name) const override;
+            virtual int_fast64_t getIntegerValue(std::string const& name) const override;
+            virtual bool getBinaryValue(std::string const& name) const override;
             virtual double getObjectiveValue() const override;
 
+            // Methods to print the LP problem to a file.
             virtual void writeModelToFile(std::string const& filename) const override;
             
         private:
+            /*!
+             * Adds a variable with the given name, type, lower and upper bound and objective function coefficient.
+             *
+             * @param name The name of the variable.
+             * @param variableType The type of the variable in terms of glpk's constants.
+             * @param boundType A glpk flag indicating which bounds apply to the variable.
+             * @param lowerBound The lower bound of the range of the variable.
+             * @param upperBound The upper bound of the range of the variable.
+             * @param objectiveFunctionCoefficient The coefficient of the variable in the objective function.
+             */
+            void addVariable(std::string const& name, int variableType, int boundType, double lowerBound, double upperBound, double objectiveFunctionCoefficient);
+            
             // The glpk LP problem.
             glp_prob* lp;
             
-            // A counter that keeps track of the next free variable index.
-            uint_fast64_t nextVariableIndex;
+            // A mapping from variable names to their indices.
+            std::map<std::string, int> variableNameToIndexMap;
+            
+            // A counter used for getting the next variable index.
+            int nextVariableIndex;
             
-            // A counter that keeps track of the next free constraint index.
-            uint_fast64_t nextConstraintIndex;
+            // A counter used for getting the next constraint index.
+            int nextConstraintIndex;
             
             // A flag storing whether the model is an LP or an MILP.
             bool modelContainsIntegerVariables;
@@ -121,15 +152,39 @@ namespace storm {
                 throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support.";
             }
             
-            virtual uint_fast64_t createContinuousVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) override {
+            virtual void addBoundedContinuousVariable(std::string const& name, double lowerBound, double upperBound, double objectiveFunctionCoefficient = 0) override {
+                throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support.";
+            }
+            
+            virtual void addLowerBoundedContinuousVariable(std::string const& name, double lowerBound, double objectiveFunctionCoefficient = 0) override {
+                throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support.";
+            }
+            
+            virtual void addUpperBoundedContinuousVariable(std::string const& name, double upperBound, double objectiveFunctionCoefficient = 0) override {
+                throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support.";
+            }
+            
+            virtual void addUnboundedContinuousVariable(std::string const& name, double objectiveFunctionCoefficient = 0) override {
+                throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support.";
+            }
+            
+            virtual void addBoundedIntegerVariable(std::string const& name, double lowerBound, double upperBound, double objectiveFunctionCoefficient = 0) override {
+                throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support.";
+            }
+            
+            virtual void addLowerBoundedIntegerVariable(std::string const& name, double lowerBound, double objectiveFunctionCoefficient = 0) override {
+                throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support.";
+            }
+            
+            virtual void addUpperBoundedIntegerVariable(std::string const& name, double upperBound, double objectiveFunctionCoefficient = 0) override {
                 throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support.";
             }
             
-            virtual uint_fast64_t createIntegerVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) override {
+            virtual void addUnboundedIntegerVariable(std::string const& name, double objectiveFunctionCoefficient = 0) override {
                 throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support.";
             }
             
-            virtual uint_fast64_t createBinaryVariable(std::string const& name, double objectiveFunctionCoefficient) override {
+            virtual uint_fast64_t addBinaryVariable(std::string const& name, double objectiveFunctionCoefficient = 0) override {
                 throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support.";
             }
             
@@ -137,14 +192,14 @@ namespace storm {
                 throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support.";
             }
             
-            virtual void addConstraint(std::string const& name, std::vector<uint_fast64_t> const& variables, std::vector<double> const& coefficients, BoundType const& boundType, double rightHandSideValue) override {
+            virtual void addConstraint(std::string const& name, storm::expressions::Expression const& constraint) override {
                 throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support.";
             }
-                        
+            
             virtual void optimize() const override {
                 throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support.";
             }
-
+            
             virtual bool isInfeasible() const override {
                 throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support.";
             }
@@ -157,15 +212,15 @@ namespace storm {
                 throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support.";
             }
             
-            virtual int_fast64_t getIntegerValue(uint_fast64_t variableIndex) const override {
+            virtual double getContinuousValue(std::string const& name) const override {
                 throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support.";
             }
             
-            virtual bool getBinaryValue(uint_fast64_t variableIndex) const override {
+            virtual int_fast64_t getIntegerValue(std::string const& name) const override {
                 throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support.";
             }
             
-            virtual double getContinuousValue(uint_fast64_t variableIndex) const override {
+            virtual bool getBinaryValue(std::string const& name) const override {
                 throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support.";
             }
             
diff --git a/src/solver/GurobiLpSolver.cpp b/src/solver/GurobiLpSolver.cpp
index 75a0d5a7e..676867ecb 100644
--- a/src/solver/GurobiLpSolver.cpp
+++ b/src/solver/GurobiLpSolver.cpp
@@ -3,13 +3,12 @@
 #ifdef STORM_HAVE_GUROBI
 #include <numeric>
 
-#include "src/exceptions/InvalidStateException.h"
-#include "src/settings/Settings.h"
+#include "src/storage/expressions/LinearCoefficientVisitor.h"
 
-#include "log4cplus/logger.h"
-#include "log4cplus/loggingmacros.h"
-
-extern log4cplus::Logger logger;
+#include "src/settings/Settings.h"
+#include "src/exceptions/ExceptionMacros.h"
+#include "src/exceptions/InvalidStateException.h"
+#include "src/exceptions/InvalidAccessException.h"
 
 bool GurobiLpSolverOptionsRegistered = storm::settings::Settings::registerNewModule([] (storm::settings::Settings* instance) -> bool {
     instance->addOption(storm::settings::OptionBuilder("GurobiLpSolver", "gurobithreads", "", "The number of threads that may be used by Gurobi.").addArgument(storm::settings::ArgumentBuilder::createUnsignedIntegerArgument("count", "The number of threads.").setDefaultValueUnsignedInteger(1).build()).build());
@@ -43,7 +42,7 @@ namespace storm {
             }
         }
         
-        GurobiLpSolver::GurobiLpSolver(std::string const& name) : GurobiLpSolver(name, MINIMIZE) {
+        GurobiLpSolver::GurobiLpSolver(std::string const& name) : GurobiLpSolver(name, ModelSense::Minimize) {
             // Intentionally left empty.
         }
         
@@ -51,7 +50,7 @@ namespace storm {
             // Intentionally left empty.
         }
         
-        GurobiLpSolver::GurobiLpSolver() : GurobiLpSolver("", MINIMIZE) {
+        GurobiLpSolver::GurobiLpSolver() : GurobiLpSolver("", ModelSense::Minimize) {
             // Intentionally left empty.
         }
         
@@ -66,145 +65,124 @@ namespace storm {
 
 			// Enable the following line to only print the output of Gurobi if the debug flag is set.
             error = GRBsetintparam(env, "OutputFlag", storm::settings::Settings::getInstance()->isSet("debug") || storm::settings::Settings::getInstance()->isSet("gurobioutput") ? 1 : 0);
-			if (error) {
-				LOG4CPLUS_ERROR(logger, "Unable to set Gurobi Parameter OutputFlag (" << GRBgeterrormsg(env) << ", error code " << error << ").");
-				throw storm::exceptions::InvalidStateException() << "Unable to set Gurobi Parameter OutputFlag (" << GRBgeterrormsg(env) << ", error code " << error << ").";
-			}
+            LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to set Gurobi Parameter OutputFlag (" << GRBgeterrormsg(env) << ", error code " << error << ").");
             
             // Enable the following line to restrict Gurobi to one thread only.
             error = GRBsetintparam(env, "Threads", storm::settings::Settings::getInstance()->getOptionByLongName("gurobithreads").getArgument(0).getValueAsUnsignedInteger());
-			if (error) {
-				LOG4CPLUS_ERROR(logger, "Unable to set Gurobi Parameter Threads (" << GRBgeterrormsg(env) << ", error code " << error << ").");
-				throw storm::exceptions::InvalidStateException() << "Unable to set Gurobi Parameter Threads (" << GRBgeterrormsg(env) << ", error code " << error << ").";
-			}
+            LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to set Gurobi Parameter Threads (" << GRBgeterrormsg(env) << ", error code " << error << ").");
 
             // Enable the following line to force Gurobi to be as precise about the binary variables as required by the given precision option.
             error = GRBsetdblparam(env, "IntFeasTol", storm::settings::Settings::getInstance()->getOptionByLongName("gurobiinttol").getArgument(0).getValueAsDouble());
-			if (error) {
-				LOG4CPLUS_ERROR(logger, "Unable to set Gurobi Parameter IntFeasTol (" << GRBgeterrormsg(env) << ", error code " << error << ").");
-				throw storm::exceptions::InvalidStateException() << "Unable to set Gurobi Parameter IntFeasTol (" << GRBgeterrormsg(env) << ", error code " << error << ").";
-			}
+            LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to set Gurobi Parameter IntFeasTol (" << GRBgeterrormsg(env) << ", error code " << error << ").");
         }
         
         void GurobiLpSolver::update() const {
             int error = GRBupdatemodel(model);
-            if (error) {
-				LOG4CPLUS_ERROR(logger, "Unable to update Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ").");
-				throw storm::exceptions::InvalidStateException() << "Unable to update Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ").";
-            }
+            LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to update Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ").");
             
             // Since the model changed, we erase the optimality flag.
             this->currentModelHasBeenOptimized = false;
         }
         
-        uint_fast64_t GurobiLpSolver::createContinuousVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) {
-            int error = 0;
-            switch (variableType) {
-                case LpSolver::BOUNDED:
-                    error = GRBaddvar(model, 0, nullptr, nullptr, objectiveFunctionCoefficient, lowerBound, upperBound, GRB_CONTINUOUS, name.c_str());
-                    break;
-                case LpSolver::UNBOUNDED:
-                    error = GRBaddvar(model, 0, nullptr, nullptr, objectiveFunctionCoefficient, -GRB_INFINITY, GRB_INFINITY, GRB_CONTINUOUS, name.c_str());
-                    break;
-                case LpSolver::UPPER_BOUND:
-                    error = GRBaddvar(model, 0, nullptr, nullptr, objectiveFunctionCoefficient, -GRB_INFINITY, upperBound, GRB_CONTINUOUS, name.c_str());
-                    break;
-                case LpSolver::LOWER_BOUND:
-                    error = GRBaddvar(model, 0, nullptr, nullptr, objectiveFunctionCoefficient, lowerBound, GRB_INFINITY, GRB_CONTINUOUS, name.c_str());
-                    break;
-            }
-            
-            if (error) {
-				LOG4CPLUS_ERROR(logger, "Could not create binary Gurobi variable (" << GRBgeterrormsg(env) << ", error code " << error << ").");
-				throw storm::exceptions::InvalidStateException() << "Could not create binary Gurobi variable (" << GRBgeterrormsg(env) << ", error code " << error << ").";
-            }
-            ++nextVariableIndex;
-            return nextVariableIndex - 1;
+        void GurobiLpSolver::addBoundedContinuousVariable(std::string const& name, double lowerBound, double upperBound, double objectiveFunctionCoefficient) {
+            this->addVariable(name, GRB_CONTINUOUS, lowerBound, upperBound, objectiveFunctionCoefficient);
         }
         
-        uint_fast64_t GurobiLpSolver::createIntegerVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) {
-            int error = 0;
-            switch (variableType) {
-                case LpSolver::BOUNDED:
-                    error = GRBaddvar(model, 0, nullptr, nullptr, objectiveFunctionCoefficient, lowerBound, upperBound, GRB_INTEGER, name.c_str());
-                    break;
-                case LpSolver::UNBOUNDED:
-                    error = GRBaddvar(model, 0, nullptr, nullptr, objectiveFunctionCoefficient, -GRB_INFINITY, GRB_INFINITY, GRB_INTEGER, name.c_str());
-                    break;
-                case LpSolver::UPPER_BOUND:
-                    error = GRBaddvar(model, 0, nullptr, nullptr, objectiveFunctionCoefficient, -GRB_INFINITY, upperBound, GRB_INTEGER, name.c_str());
-                    break;
-                case LpSolver::LOWER_BOUND:
-                    error = GRBaddvar(model, 0, nullptr, nullptr, objectiveFunctionCoefficient, lowerBound, GRB_INFINITY, GRB_INTEGER, name.c_str());
-                    break;
-            }
+        void GurobiLpSolver::addLowerBoundedContinuousVariable(std::string const& name, double lowerBound, double objectiveFunctionCoefficient) {
+            this->addVariable(name, GRB_CONTINUOUS, lowerBound, GRB_INFINITY, objectiveFunctionCoefficient);
+        }
 
-            if (error) {
-				LOG4CPLUS_ERROR(logger, "Could not create binary Gurobi variable (" << GRBgeterrormsg(env) << ", error code " << error << ").");
-				throw storm::exceptions::InvalidStateException() << "Could not create binary Gurobi variable (" << GRBgeterrormsg(env) << ", error code " << error << ").";
-            }
-            ++nextVariableIndex;
-            return nextVariableIndex - 1;
+        void GurobiLpSolver::addUpperBoundedContinuousVariable(std::string const& name, double upperBound, double objectiveFunctionCoefficient) {
+            this->addVariable(name, GRB_CONTINUOUS, -GRB_INFINITY, upperBound, objectiveFunctionCoefficient);
+        }
+
+        void GurobiLpSolver::addUnboundedContinuousVariable(std::string const& name, double objectiveFunctionCoefficient) {
+            this->addVariable(name, GRB_CONTINUOUS, -GRB_INFINITY, GRB_INFINITY, objectiveFunctionCoefficient);
         }
         
-        uint_fast64_t GurobiLpSolver::createBinaryVariable(std::string const& name, double objectiveFunctionCoefficient) {
-            int error = GRBaddvar(model, 0, nullptr, nullptr, objectiveFunctionCoefficient, 0.0, 1.0, GRB_BINARY, name.c_str());
-            if (error) {
-				LOG4CPLUS_ERROR(logger, "Could not create binary Gurobi variable (" << GRBgeterrormsg(env) << ", error code " << error << ").");
-				throw storm::exceptions::InvalidStateException() << "Could not create binary Gurobi variable (" << GRBgeterrormsg(env) << ", error code " << error << ").";
-            }
-            ++nextVariableIndex;
-            return nextVariableIndex - 1;
+        void GurobiLpSolver::addBoundedIntegerVariable(std::string const& name, double lowerBound, double upperBound, double objectiveFunctionCoefficient) {
+            this->addVariable(name, GRB_INTEGER, lowerBound, upperBound, objectiveFunctionCoefficient);
         }
         
-        void GurobiLpSolver::addConstraint(std::string const& name, std::vector<uint_fast64_t> const& variables, std::vector<double> const& coefficients, BoundType const& boundType, double rightHandSideValue) {
-            if (variables.size() != coefficients.size()) {
-                LOG4CPLUS_ERROR(logger, "Sizes of variable indices vector and coefficients vector do not match.");
-                throw storm::exceptions::InvalidStateException() << "Sizes of variable indices vector and coefficients vector do not match.";
-            }
+        void GurobiLpSolver::addLowerBoundedIntegerVariable(std::string const& name, double lowerBound, double objectiveFunctionCoefficient) {
+            this->addVariable(name, GRB_INTEGER, lowerBound, GRB_INFINITY, objectiveFunctionCoefficient);
+        }
+        
+        void GurobiLpSolver::addUpperBoundedIntegerVariable(std::string const& name, double upperBound, double objectiveFunctionCoefficient) {
+            this->addVariable(name, GRB_INTEGER, -GRB_INFINITY, upperBound, objectiveFunctionCoefficient);
+        }
+        
+        void GurobiLpSolver::addUnboundedIntegerVariable(std::string const& name, double objectiveFunctionCoefficient) {
+            this->addVariable(name, GRB_INTEGER, -GRB_INFINITY, GRB_INFINITY, objectiveFunctionCoefficient);
+        }
+        
+        void GurobiLpSolver::addBinaryVariable(std::string const& name, double objectiveFunctionCoefficient) {
+            this->addVariable(name, GRB_BINARY, 0, 1, objectiveFunctionCoefficient);
+        }
+
+        void GurobiLpSolver::addVariable(std::string const& name, char variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) {
+            // Check whether variable already exists.
+            auto nameIndexPair = this->variableNameToIndexMap.find(name);
+            LOG_THROW(nameIndexPair == this->variableNameToIndexMap.end(), storm::exceptions::InvalidArgumentException, "Variable '" << nameIndexPair->first << "' already exists.");
             
-            // As Gurobi requires the indices to be unique, we now explicitly make them unique. For this, we sort the
-            // variables and coefficients and eliminated duplicates by adding the coefficients.
+            // Check for valid variable type.
+            LOG_ASSERT(variableType == GRB_CONTINUOUS || variableType == GRB_INTEGER || variableType == GRB_BINARY, "Illegal type '" << variableType << "' for Gurobi variable.");
             
-            // We start by sorting both vectors.
-            std::vector<uint_fast64_t> sortedVariables(variables);
-			std::vector<double> sortedCoefficients(coefficients);
-            std::vector<uint_fast64_t> permutation(variables.size());
-            std::iota(permutation.begin(), permutation.end(), 0);
-            std::sort(permutation.begin(), permutation.end(), [&] (uint_fast64_t i, uint_fast64_t j) { return variables[i] < variables[j]; } );
-            std::transform(permutation.begin(), permutation.end(), sortedVariables.begin(), [&] (uint_fast64_t i) { return variables[i]; } );
-            std::transform(permutation.begin(), permutation.end(), sortedCoefficients.begin(), [&] (uint_fast64_t i) { return coefficients[i]; } );
+            // Finally, create the actual variable.
+            int error = 0;
+            error = GRBaddvar(model, 0, nullptr, nullptr, objectiveFunctionCoefficient, lowerBound, upperBound, variableType, name.c_str());
+            LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Could not create binary Gurobi variable (" << GRBgeterrormsg(env) << ", error code " << error << ").");
+            this->variableNameToIndexMap.emplace(name, nextVariableIndex);
+            ++nextVariableIndex;
+        }
+                
+        void GurobiLpSolver::addConstraint(std::string const& name, storm::expressions::Expression const& constraint) {            
+            LOG_THROW(constraint.isRelationalExpression(), storm::exceptions::InvalidArgumentException, "Illegal constraint is not a relational expression.");
+            LOG_THROW(constraint.getOperator() != storm::expressions::OperatorType::NotEqual, storm::exceptions::InvalidArgumentException, "Illegal constraint uses inequality operator.");
             
-            // Now we perform the duplicate elimination step.
-            std::vector<int> uniqueVariables;
-            std::vector<double> uniqueCoefficients;
-			for (uint_fast64_t i = 0; i < sortedVariables.size(); ++i) {
-				if (!uniqueVariables.empty() && uniqueVariables.back() == sortedVariables[i]) {
-					uniqueCoefficients.back() += sortedCoefficients[i];
+            std::pair<storm::expressions::SimpleValuation, double> leftCoefficients = storm::expressions::LinearCoefficientVisitor().getLinearCoefficients(constraint.getOperand(0));
+            std::pair<storm::expressions::SimpleValuation, double> rightCoefficients = storm::expressions::LinearCoefficientVisitor().getLinearCoefficients(constraint.getOperand(1));
+            for (auto const& identifier : rightCoefficients.first.getDoubleIdentifiers()) {
+                if (leftCoefficients.first.containsDoubleIdentifier(identifier)) {
+                    leftCoefficients.first.setDoubleValue(identifier, leftCoefficients.first.getDoubleValue(identifier) - rightCoefficients.first.getDoubleValue(identifier));
                 } else {
-					uniqueVariables.push_back(static_cast<int>(sortedVariables[i]));
-					uniqueCoefficients.push_back(sortedCoefficients[i]);
+                    leftCoefficients.first.addDoubleIdentifier(identifier, -rightCoefficients.first.getDoubleValue(identifier));
                 }
             }
+            rightCoefficients.second -= leftCoefficients.second;
             
-            bool strictBound = boundType == LESS || boundType == GREATER;
-            char sense = boundType == LESS || boundType == LESS_EQUAL ? GRB_LESS_EQUAL : boundType == EQUAL ? GRB_EQUAL : GRB_GREATER_EQUAL;
-            
-            // If the constraint enforces a strict bound, we need to do some tweaking of the right-hand side value, because Gurobi only supports
-            // non-strict bounds.
-            if (strictBound) {
-                if (boundType == LESS) {
-                    rightHandSideValue -= storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble();
-                } else if (boundType == GREATER) {
-                    rightHandSideValue += storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble();
-                }
+            // Now we need to transform the coefficients to the vector representation.
+            std::vector<int> variables;
+            std::vector<double> coefficients;
+            for (auto const& identifier : leftCoefficients.first.getDoubleIdentifiers()) {
+                auto identifierIndexPair = this->variableNameToIndexMap.find(identifier);
+                LOG_THROW(identifierIndexPair != this->variableNameToIndexMap.end(), storm::exceptions::InvalidArgumentException, "Constraint contains illegal identifier '" << identifier << "'.");
+                variables.push_back(identifierIndexPair->second);
+                coefficients.push_back(leftCoefficients.first.getDoubleValue(identifier));
             }
-            int error = GRBaddconstr(model, uniqueVariables.size(), uniqueVariables.data(), uniqueCoefficients.data(), sense, rightHandSideValue, name == "" ? nullptr : name.c_str());
             
-            if (error) {
-                LOG4CPLUS_ERROR(logger, "Unable to assert Gurobi constraint (" << GRBgeterrormsg(env) << ", error code " << error << ").");
-				throw storm::exceptions::InvalidStateException() << "Unable to assert Gurobi constraint (" << GRBgeterrormsg(env) << ", error code " << error << ").";
+            // Determine the type of the constraint and add it properly.
+            int error = 0;
+            switch (constraint.getOperator()) {
+                case storm::expressions::OperatorType::Less:
+                    error = GRBaddconstr(model, variables.size(), variables.data(), coefficients.data(), GRB_LESS_EQUAL, rightCoefficients.second - storm::settings::Settings::getInstance()->getOptionByLongName("gurobiinttol").getArgument(0).getValueAsDouble(), name == "" ? nullptr : name.c_str());
+                    break;
+                case storm::expressions::OperatorType::LessOrEqual:
+                    error = GRBaddconstr(model, variables.size(), variables.data(), coefficients.data(), GRB_LESS_EQUAL, rightCoefficients.second, name == "" ? nullptr : name.c_str());
+                    break;
+                case storm::expressions::OperatorType::Greater:
+                    error = GRBaddconstr(model, variables.size(), variables.data(), coefficients.data(), GRB_GREATER_EQUAL, rightCoefficients.second + storm::settings::Settings::getInstance()->getOptionByLongName("gurobiinttol").getArgument(0).getValueAsDouble(), name == "" ? nullptr : name.c_str());
+                    break;
+                case storm::expressions::OperatorType::GreaterOrEqual:
+                    error = GRBaddconstr(model, variables.size(), variables.data(), coefficients.data(), GRB_GREATER_EQUAL, rightCoefficients.second, name == "" ? nullptr : name.c_str());
+                    break;
+                case storm::expressions::OperatorType::Equal:
+                    error = GRBaddconstr(model, variables.size(), variables.data(), coefficients.data(), GRB_EQUAL, rightCoefficients.second, name == "" ? nullptr : name.c_str());
+                    break;
+                default:
+                    LOG_ASSERT(false, "Illegal operator in LP solver constraint.");
             }
+            LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Could not assert constraint (" << GRBgeterrormsg(env) << ", error code " << error << ").");
         }
         
         void GurobiLpSolver::optimize() const {
@@ -212,18 +190,12 @@ namespace storm {
             this->update();
          
             // Set the most recently set model sense.
-            int error = GRBsetintattr(model, "ModelSense", this->getModelSense() == MINIMIZE ? 1 : -1);
-            if (error) {
-				LOG4CPLUS_ERROR(logger, "Unable to set Gurobi model sense (" << GRBgeterrormsg(env) << ", error code " << error << ").");
-				throw storm::exceptions::InvalidStateException() << "Unable to set Gurobi model sense (" << GRBgeterrormsg(env) << ", error code " << error << ").";
-            }
+            int error = GRBsetintattr(model, "ModelSense", this->getModelSense() == ModelSense::Minimize ? 1 : -1);
+            LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to set Gurobi model sense (" << GRBgeterrormsg(env) << ", error code " << error << ").");
             
             // Then we actually optimize the model.
             error = GRBoptimize(model);
-            if (error) {
-				LOG4CPLUS_ERROR(logger, "Unable to optimize Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ").");
-				throw storm::exceptions::InvalidStateException() << "Unable to optimize Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ").";
-            }
+            LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to optimize Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ").");
             
             this->currentModelHasBeenOptimized = true;
         }
@@ -236,34 +208,21 @@ namespace storm {
             int optimalityStatus = 0;
             
             int error = GRBgetintattr(model, GRB_INT_ATTR_STATUS, &optimalityStatus);
-            if (error) {
-				LOG4CPLUS_ERROR(logger, "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ").");
-				throw storm::exceptions::InvalidStateException() << "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ").";
-            }
+            LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ").");
             
             // By default, Gurobi may tell us only that the model is either infeasible or unbounded. To decide which one
             // it is, we need to perform an extra step.
             if (optimalityStatus == GRB_INF_OR_UNBD) {
-                std::cout << "here" << std::endl;
                 error = GRBsetintparam(GRBgetenv(model), GRB_INT_PAR_DUALREDUCTIONS, 0);
-                if (error) {
-					LOG4CPLUS_ERROR(logger, "Unable to set Gurobi parameter (" << GRBgeterrormsg(env) << ", error code " << error << ").");
-					throw storm::exceptions::InvalidStateException() << "Unable to set Gurobi parameter (" << GRBgeterrormsg(env) << ", error code " << error << ").";
-                }
+                LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to set Gurobi parameter (" << GRBgeterrormsg(env) << ", error code " << error << ").");
                 
                 this->optimize();
                 
                 error = GRBgetintattr(model, GRB_INT_ATTR_STATUS, &optimalityStatus);
-                if (error) {
-					LOG4CPLUS_ERROR(logger, "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ").");
-					throw storm::exceptions::InvalidStateException() << "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ").";
-                }
+                LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ").");
                 
                 error = GRBsetintparam(GRBgetenv(model), GRB_INT_PAR_DUALREDUCTIONS, 1);
-                if (error) {
-					LOG4CPLUS_ERROR(logger, "Unable to set Gurobi parameter (" << GRBgeterrormsg(env) << ", error code " << error << ").");
-					throw storm::exceptions::InvalidStateException() << "Unable to set Gurobi parameter (" << GRBgeterrormsg(env) << ", error code " << error << ").";
-                }
+                LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to set Gurobi parameter (" << GRBgeterrormsg(env) << ", error code " << error << ").");
             }
             
             return optimalityStatus == GRB_INFEASIBLE;
@@ -277,33 +236,21 @@ namespace storm {
             int optimalityStatus = 0;
             
             int error = GRBgetintattr(model, GRB_INT_ATTR_STATUS, &optimalityStatus);
-            if (error) {
-				LOG4CPLUS_ERROR(logger, "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ").");
-				throw storm::exceptions::InvalidStateException() << "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ").";
-            }
+            LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ").");
             
             // By default, Gurobi may tell us only that the model is either infeasible or unbounded. To decide which one
             // it is, we need to perform an extra step.
             if (optimalityStatus == GRB_INF_OR_UNBD) {
                 error = GRBsetintparam(GRBgetenv(model), GRB_INT_PAR_DUALREDUCTIONS, 0);
-                if (error) {
-					LOG4CPLUS_ERROR(logger, "Unable to set Gurobi parameter (" << GRBgeterrormsg(env) << ", error code " << error << ").");
-					throw storm::exceptions::InvalidStateException() << "Unable to set Gurobi parameter (" << GRBgeterrormsg(env) << ", error code " << error << ").";
-                }
+                LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to set Gurobi parameter (" << GRBgeterrormsg(env) << ", error code " << error << ").");
                 
                 this->optimize();
 
                 error = GRBgetintattr(model, GRB_INT_ATTR_STATUS, &optimalityStatus);
-                if (error) {
-					LOG4CPLUS_ERROR(logger, "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ").");
-					throw storm::exceptions::InvalidStateException() << "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ").";
-                }
+                LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ").");
 
                 error = GRBsetintparam(GRBgetenv(model), GRB_INT_PAR_DUALREDUCTIONS, 1);
-                if (error) {
-					LOG4CPLUS_ERROR(logger, "Unable to set Gurobi parameter (" << GRBgeterrormsg(env) << ", error code " << error << ").");
-					throw storm::exceptions::InvalidStateException() << "Unable to set Gurobi parameter (" << GRBgeterrormsg(env) << ", error code " << error << ").";
-                }
+                LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to set Gurobi parameter (" << GRBgeterrormsg(env) << ", error code " << error << ").");
             }
             
             return optimalityStatus == GRB_UNBOUNDED;
@@ -316,120 +263,79 @@ namespace storm {
             int optimalityStatus = 0;
             
             int error = GRBgetintattr(model, GRB_INT_ATTR_STATUS, &optimalityStatus);
-            if (error) {
-				LOG4CPLUS_ERROR(logger, "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ").");
-				throw storm::exceptions::InvalidStateException() << "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ").";
-            }
+            LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to retrieve optimization status of Gurobi model (" << GRBgeterrormsg(env) << ", error code " << error << ").");
             
             return optimalityStatus == GRB_OPTIMAL;
         }
         
-        int_fast64_t GurobiLpSolver::getIntegerValue(uint_fast64_t variableIndex) const {
+        double GurobiLpSolver::getContinuousValue(std::string const& name) const {
             if (!this->isOptimal()) {
-                if (this->isInfeasible()) {
-                    LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution from infeasible model (" << GRBgeterrormsg(env) << ").");
-                    throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from infeasible model (" << GRBgeterrormsg(env) << ").";
-                } else if (this->isUnbounded()) {
-                    LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution from unbounded model (" << GRBgeterrormsg(env) << ").");
-                    throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unbounded model (" << GRBgeterrormsg(env) << ").";
-                } else {
-                    LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution from unoptimized model (" << GRBgeterrormsg(env) << ").");
-                    throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unoptimized model (" << GRBgeterrormsg(env) << ").";
-                }
+                LOG_THROW(!this->isInfeasible(), storm::exceptions::InvalidAccessException, "Unable to get Gurobi solution from infeasible model (" << GRBgeterrormsg(env) << ").");
+                LOG_THROW(!this->isUnbounded(), storm::exceptions::InvalidAccessException, "Unable to get Gurobi solution from unbounded model (" << GRBgeterrormsg(env) << ").");
+                LOG_THROW(false, storm::exceptions::InvalidAccessException, "Unable to get Gurobi solution from unoptimized model (" << GRBgeterrormsg(env) << ").");
             }
             
-            double value = 0;
-            int error = GRBgetdblattrelement(model, GRB_DBL_ATTR_X, variableIndex, &value);
-            if (error) {
-				LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution (" << GRBgeterrormsg(env) << ", error code " << error << ").");
-				throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution (" << GRBgeterrormsg(env) << ", error code " << error << ").";
-            }
+            auto variableIndexPair = this->variableNameToIndexMap.find(name);
+            LOG_THROW(variableIndexPair != this->variableNameToIndexMap.end(), storm::exceptions::InvalidAccessException, "Accessing value of unknown variable '" << name << "'.");
             
-            if (std::abs(value - static_cast<int>(value)) <= storm::settings::Settings::getInstance()->getOptionByLongName("gurobiinttol").getArgument(0).getValueAsDouble()) {
-                // Nothing to do in this case.
-            } else if (std::abs(value) > storm::settings::Settings::getInstance()->getOptionByLongName("gurobiinttol").getArgument(0).getValueAsDouble()) {
-                LOG4CPLUS_ERROR(logger, "Illegal value for integer variable in Gurobi solution (" << value << ").");
-                throw storm::exceptions::InvalidStateException() << "Illegal value for integer variable in Gurobi solution (" << value << ").";
-            }
+            double value = 0;
+            int error = GRBgetdblattrelement(model, GRB_DBL_ATTR_X, variableIndexPair->second, &value);
+            LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to get Gurobi solution (" << GRBgeterrormsg(env) << ", error code " << error << ").");
             
-            return static_cast<int_fast64_t>(value);
+            return value;
         }
         
-        bool GurobiLpSolver::getBinaryValue(uint_fast64_t variableIndex) const {
+        int_fast64_t GurobiLpSolver::getIntegerValue(std::string const& name) const {
             if (!this->isOptimal()) {
-                if (this->isInfeasible()) {
-                    LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution from infeasible model (" << GRBgeterrormsg(env) << ").");
-                    throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from infeasible model (" << GRBgeterrormsg(env) << ").";
-                } else if (this->isUnbounded()) {
-                    LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution from unbounded model (" << GRBgeterrormsg(env) << ").");
-                    throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unbounded model (" << GRBgeterrormsg(env) << ").";
-                } else {
-                    LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution from unoptimized model (" << GRBgeterrormsg(env) << ").");
-                    throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unoptimized model (" << GRBgeterrormsg(env) << ").";
-                }
-            }
-
-            double value = 0;
-            int error = GRBgetdblattrelement(model, GRB_DBL_ATTR_X, variableIndex, &value);
-            if (error) {
-				LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution (" << GRBgeterrormsg(env) << ", error code " << error << ").");
-				throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution (" << GRBgeterrormsg(env) << ", error code " << error << ").";
+                LOG_THROW(!this->isInfeasible(), storm::exceptions::InvalidAccessException, "Unable to get Gurobi solution from infeasible model (" << GRBgeterrormsg(env) << ").");
+                LOG_THROW(!this->isUnbounded(), storm::exceptions::InvalidAccessException, "Unable to get Gurobi solution from unbounded model (" << GRBgeterrormsg(env) << ").");
+                LOG_THROW(false, storm::exceptions::InvalidAccessException, "Unable to get Gurobi solution from unoptimized model (" << GRBgeterrormsg(env) << ").");
             }
             
-            if (std::abs(value - 1) <= storm::settings::Settings::getInstance()->getOptionByLongName("gurobiinttol").getArgument(0).getValueAsDouble()) {
-                // Nothing to do in this case.
-            } else if (std::abs(value) > storm::settings::Settings::getInstance()->getOptionByLongName("gurobiinttol").getArgument(0).getValueAsDouble()) {
-                LOG4CPLUS_ERROR(logger, "Illegal value for binary variable in Gurobi solution (" << value << ").");
-                throw storm::exceptions::InvalidStateException() << "Illegal value for binary variable in Gurobi solution (" << value << ").";
-            }
+            auto variableIndexPair = this->variableNameToIndexMap.find(name);
+            LOG_THROW(variableIndexPair != this->variableNameToIndexMap.end(), storm::exceptions::InvalidAccessException, "Accessing value of unknown variable '" << name << "'.");
             
-            return static_cast<bool>(value);
+            double value = 0;
+            int error = GRBgetdblattrelement(model, GRB_DBL_ATTR_X, variableIndexPair->second, &value);
+            LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to get Gurobi solution (" << GRBgeterrormsg(env) << ", error code " << error << ").");
+            LOG_THROW(std::abs(static_cast<int>(value) - value) <= storm::settings::Settings::getInstance()->getOptionByLongName("gurobiinttol").getArgument(0).getValueAsDouble(), storm::exceptions::InvalidStateException, "Illegal value for integer variable in Gurobi solution (" << value << ").");
+            
+            return static_cast<int_fast64_t>(value);
         }
         
-        double GurobiLpSolver::getContinuousValue(uint_fast64_t variableIndex) const {
+        bool GurobiLpSolver::getBinaryValue(std::string const& name) const {
             if (!this->isOptimal()) {
-                if (this->isInfeasible()) {
-                    LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution from infeasible model (" << GRBgeterrormsg(env) << ").");
-                    throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from infeasible model (" << GRBgeterrormsg(env) << ").";
-                } else if (this->isUnbounded()) {
-                    LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution from unbounded model (" << GRBgeterrormsg(env) << ").");
-                    throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unbounded model (" << GRBgeterrormsg(env) << ").";
-                } else {
-                    LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution from unoptimized model (" << GRBgeterrormsg(env) << ").");
-                    throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unoptimized model (" << GRBgeterrormsg(env) << ").";
-                }
+                LOG_THROW(!this->isInfeasible(), storm::exceptions::InvalidAccessException, "Unable to get Gurobi solution from infeasible model (" << GRBgeterrormsg(env) << ").");
+                LOG_THROW(!this->isUnbounded(), storm::exceptions::InvalidAccessException, "Unable to get Gurobi solution from unbounded model (" << GRBgeterrormsg(env) << ").");
+                LOG_THROW(false, storm::exceptions::InvalidAccessException, "Unable to get Gurobi solution from unoptimized model (" << GRBgeterrormsg(env) << ").");
             }
 
+            auto variableIndexPair = this->variableNameToIndexMap.find(name);
+            LOG_THROW(variableIndexPair != this->variableNameToIndexMap.end(), storm::exceptions::InvalidAccessException, "Accessing value of unknown variable '" << name << "'.");
+            
             double value = 0;
-            int error = GRBgetdblattrelement(model, GRB_DBL_ATTR_X, variableIndex, &value);
-            if (error) {
-				LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution (" << GRBgeterrormsg(env) << ", error code " << error << ").");
-				throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution (" << GRBgeterrormsg(env) << ", error code " << error << ").";
+            int error = GRBgetdblattrelement(model, GRB_DBL_ATTR_X, variableIndexPair->second, &value);
+            LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to get Gurobi solution (" << GRBgeterrormsg(env) << ", error code " << error << ").");
+
+            if (value > 0.5) {
+                LOG_THROW(std::abs(static_cast<int>(value) - 1) <= storm::settings::Settings::getInstance()->getOptionByLongName("gurobiinttol").getArgument(0).getValueAsDouble(), storm::exceptions::InvalidStateException, "Illegal value for integer variable in Gurobi solution (" << value << ").");
+            } else {
+                LOG_THROW(value <= storm::settings::Settings::getInstance()->getOptionByLongName("gurobiinttol").getArgument(0).getValueAsDouble(), storm::exceptions::InvalidStateException, "Illegal value for integer variable in Gurobi solution (" << value << ").");
             }
             
-            return value;
+            return static_cast<bool>(value);
         }
         
         double GurobiLpSolver::getObjectiveValue() const {
             if (!this->isOptimal()) {
-                if (this->isInfeasible()) {
-                    LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution from infeasible model (" << GRBgeterrormsg(env) << ").");
-                    throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from infeasible model (" << GRBgeterrormsg(env) << ").";
-                } else if (this->isUnbounded()) {
-                    LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution from unbounded model (" << GRBgeterrormsg(env) << ").");
-                    throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unbounded model (" << GRBgeterrormsg(env) << ").";
-                } else {
-                    LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution from unoptimized model (" << GRBgeterrormsg(env) << ").");
-                    throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution from unoptimized model (" << GRBgeterrormsg(env) << ").";
-                }
+                LOG_THROW(!this->isInfeasible(), storm::exceptions::InvalidAccessException, "Unable to get Gurobi solution from infeasible model (" << GRBgeterrormsg(env) << ").");
+                LOG_THROW(!this->isUnbounded(), storm::exceptions::InvalidAccessException, "Unable to get Gurobi solution from unbounded model (" << GRBgeterrormsg(env) << ").");
+                LOG_THROW(false, storm::exceptions::InvalidAccessException, "Unable to get Gurobi solution from unoptimized model (" << GRBgeterrormsg(env) << ").");
             }
             
             double value = 0;
             int error = GRBgetdblattr(model, GRB_DBL_ATTR_OBJVAL, &value);
-            if (error) {
-				LOG4CPLUS_ERROR(logger, "Unable to get Gurobi solution (" << GRBgeterrormsg(env) << ", error code " << error << ").");
-				throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution (" << GRBgeterrormsg(env) << ", error code " << error << ").";
-            }
+            LOG_THROW(error == 0, storm::exceptions::InvalidStateException, "Unable to get Gurobi solution (" << GRBgeterrormsg(env) << ", error code " << error << ").");
             
             return value;
         }
diff --git a/src/solver/GurobiLpSolver.h b/src/solver/GurobiLpSolver.h
index 18c5bbdbd..90bef47eb 100644
--- a/src/solver/GurobiLpSolver.h
+++ b/src/solver/GurobiLpSolver.h
@@ -60,23 +60,40 @@ namespace storm {
              */
             virtual ~GurobiLpSolver();
             
-            virtual uint_fast64_t createContinuousVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) override;
-            virtual uint_fast64_t createIntegerVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) override;
-            virtual uint_fast64_t createBinaryVariable(std::string const& name, double objectiveFunctionCoefficient) override;
+            // Methods to add continuous variables.
+            virtual void addBoundedContinuousVariable(std::string const& name, double lowerBound, double upperBound, double objectiveFunctionCoefficient = 0) override;
+            virtual void addLowerBoundedContinuousVariable(std::string const& name, double lowerBound, double objectiveFunctionCoefficient = 0) override;
+            virtual void addUpperBoundedContinuousVariable(std::string const& name, double upperBound, double objectiveFunctionCoefficient = 0) override;
+            virtual void addUnboundedContinuousVariable(std::string const& name, double objectiveFunctionCoefficient = 0) override;
+            
+            // Methods to add integer variables.
+            virtual void addBoundedIntegerVariable(std::string const& name, double lowerBound, double upperBound, double objectiveFunctionCoefficient = 0) override;
+            virtual void addLowerBoundedIntegerVariable(std::string const& name, double lowerBound, double objectiveFunctionCoefficient = 0) override;
+            virtual void addUpperBoundedIntegerVariable(std::string const& name, double upperBound, double objectiveFunctionCoefficient = 0) override;
+            virtual void addUnboundedIntegerVariable(std::string const& name, double objectiveFunctionCoefficient = 0) override;
+            
+            // Methods to add binary variables.
+            virtual void addBinaryVariable(std::string const& name, double objectiveFunctionCoefficient = 0) override;
+            
+            // Methods to incorporate recent changes.
             virtual void update() const override;
-
-            virtual void addConstraint(std::string const& name, std::vector<uint_fast64_t> const& variables, std::vector<double> const& coefficients, BoundType const& boundType, double rightHandSideValue) override;
             
+            // Methods to add constraints
+            virtual void addConstraint(std::string const& name, storm::expressions::Expression const& constraint) override;
+            
+            // Methods to optimize and retrieve optimality status.
             virtual void optimize() const override;
             virtual bool isInfeasible() const override;
             virtual bool isUnbounded() const override;
             virtual bool isOptimal() const override;
-
-            virtual int_fast64_t getIntegerValue(uint_fast64_t variableIndex) const override;
-            virtual bool getBinaryValue(uint_fast64_t variableIndex) const override;
-            virtual double getContinuousValue(uint_fast64_t variableIndex) const override;
+            
+            // Methods to retrieve values of variables and the objective function in the optimal solutions.
+            virtual double getContinuousValue(std::string const& name) const override;
+            virtual int_fast64_t getIntegerValue(std::string const& name) const override;
+            virtual bool getBinaryValue(std::string const& name) const override;
             virtual double getObjectiveValue() const override;
-
+            
+            // Methods to print the LP problem to a file.
             virtual void writeModelToFile(std::string const& filename) const override;
             
         private:
@@ -85,14 +102,28 @@ namespace storm {
              */
             void setGurobiEnvironmentProperties() const;
             
+            /*!
+             * Adds a variable with the given name, type, lower and upper bound and objective function coefficient.
+             *
+             * @param name The name of the variable.
+             * @param variableType The type of the variable in terms of Gurobi's constants.
+             * @param lowerBound The lower bound of the range of the variable.
+             * @param upperBound The upper bound of the range of the variable.
+             * @param objectiveFunctionCoefficient The coefficient of the variable in the objective function.
+             */
+            void addVariable(std::string const& name, char variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient);
+            
             // The Gurobi environment.
             GRBenv* env;
             
             // The Gurobi model.
             GRBmodel* model;
             
-            // A counter that keeps track of the next free variable index.
-            uint_fast64_t nextVariableIndex;
+            // The index of the next variable.
+            int nextVariableIndex;
+            
+            // A mapping from variable names to their indices.
+            std::map<std::string, int> variableNameToIndexMap;
         };
 #else
         // If Gurobi is not available, we provide a stub implementation that emits an error if any of its methods is called.
@@ -114,27 +145,46 @@ namespace storm {
                 throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support.";
             }
             
-            virtual uint_fast64_t createContinuousVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) override {
+            virtual void addBoundedContinuousVariable(std::string const& name, double lowerBound, double upperBound, double objectiveFunctionCoefficient = 0) override {
                 throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support.";
             }
             
-            virtual uint_fast64_t createIntegerVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) override {
+            virtual void addLowerBoundedContinuousVariable(std::string const& name, double lowerBound, double objectiveFunctionCoefficient = 0) override {
+                throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support.";            }
+            
+            virtual void addUpperBoundedContinuousVariable(std::string const& name, double upperBound, double objectiveFunctionCoefficient = 0) override {
                 throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support.";
             }
             
-            virtual uint_fast64_t createBinaryVariable(std::string const& name, double objectiveFunctionCoefficient) override {
+            virtual void addUnboundedContinuousVariable(std::string const& name, double objectiveFunctionCoefficient = 0) override {
                 throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support.";
             }
             
-            virtual void update() const override {
+            virtual void addBoundedIntegerVariable(std::string const& name, double lowerBound, double upperBound, double objectiveFunctionCoefficient = 0) override {
+                throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support.";
+            }
+            
+            virtual void addLowerBoundedIntegerVariable(std::string const& name, double lowerBound, double objectiveFunctionCoefficient = 0) override {
+                throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support.";
+            }
+            
+            virtual void addUpperBoundedIntegerVariable(std::string const& name, double upperBound, double objectiveFunctionCoefficient = 0) override {
+                throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support.";
+            }
+            
+            virtual void addUnboundedIntegerVariable(std::string const& name, double objectiveFunctionCoefficient = 0) override {
                 throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support.";
             }
             
-            virtual void addConstraint(std::string const& name, std::vector<uint_fast64_t> const& variables, std::vector<double> const& coefficients, BoundType const& boundType, double rightHandSideValue) override {
+            virtual uint_fast64_t addBinaryVariable(std::string const& name, double objectiveFunctionCoefficient = 0) override {
                 throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support.";
             }
             
-            virtual void setModelSense(ModelSense const& modelSense) {
+            virtual void update() const override {
+                throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support.";
+            }
+            
+            virtual void addConstraint(std::string const& name, storm::expressions::Expression const& constraint) override {
                 throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support.";
             }
             
@@ -154,15 +204,15 @@ namespace storm {
                 throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support.";
             }
             
-            virtual int_fast64_t getIntegerValue(uint_fast64_t variableIndex) const override {
+            virtual double getContinuousValue(std::string const& name) const override {
                 throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support.";
             }
             
-            virtual bool getBinaryValue(uint_fast64_t variableIndex) const override {
+            virtual int_fast64_t getIntegerValue(std::string const& name) const override {
                 throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support.";
             }
             
-            virtual double getContinuousValue(uint_fast64_t variableIndex) const override {
+            virtual bool getBinaryValue(std::string const& name) const override {
                 throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for Gurobi. Yet, a method was called that requires this support. Please choose a version of support with Gurobi support.";
             }
             
@@ -175,7 +225,7 @@ namespace storm {
             }
         };
 #endif
-    
+
     }
 }
 
diff --git a/src/solver/LpSolver.h b/src/solver/LpSolver.h
index 311ee4b44..8c729a323 100644
--- a/src/solver/LpSolver.h
+++ b/src/solver/LpSolver.h
@@ -5,42 +5,25 @@
 #include <vector>
 #include <cstdint>
 
+#include "src/storage/expressions/Expression.h"
+
 namespace storm {
     namespace solver {
-
         /*!
          * An interface that captures the functionality of an LP solver.
          */
         class LpSolver {
         public:
-            // An enumeration to represent the possible types of variables. Variables may be either unbounded, have only
-            // a lower or an upper bound, respectively, or be bounded from below and from above.
-            enum VariableType {
-                UNBOUNDED,
-                LOWER_BOUND,
-                UPPER_BOUND,
-                BOUNDED,
-            };
-            
-            // An enumeration to represent the possible types of constraints.
-            enum BoundType {
-                LESS,
-                LESS_EQUAL,
-                GREATER,
-                GREATER_EQUAL,
-                EQUAL
-            };
-            
             // An enumeration to represent whether the objective function is to be minimized or maximized.
-            enum ModelSense {
-                MINIMIZE,
-                MAXIMIZE
+            enum class ModelSense {
+                Minimize,
+                Maximize
             };
             
             /*!
              * Creates an empty LP solver. By default the objective function is assumed to be minimized.
              */
-            LpSolver() : currentModelHasBeenOptimized(false), modelSense(MINIMIZE) {
+            LpSolver() : currentModelHasBeenOptimized(false), modelSense(ModelSense::Minimize) {
                 // Intentionally left empty.
             }
             
@@ -55,52 +38,99 @@ namespace storm {
             }
 
             /*!
-             * Creates a continuous variable, i.e. a variable that may take all real values within its bounds.
+             * Registers an upper- and lower-bounded continuous variable, i.e. a variable that may take all real values
+             * within its bounds.
+             *
+             * @param name The name of the variable.
+             * @param lowerBound The lower bound of the variable.
+             * @param upperBound The upper bound of the variable.
+             * @param objectiveFunctionCoefficient The coefficient with which the variable appears in the objective
+             * function. If omitted (or set to zero), the variable is irrelevant for the objective value.
+             */
+            virtual void addBoundedContinuousVariable(std::string const& name, double lowerBound, double upperBound, double objectiveFunctionCoefficient = 0) = 0;
+            
+            /*!
+             * Registers a lower-bounded continuous variable, i.e. a variable that may take all real values up to its
+             * lower bound.
+             *
+             * @param name The name of the variable.
+             * @param lowerBound The lower bound of the variable.
+             * @param objectiveFunctionCoefficient The coefficient with which the variable appears in the objective
+             * function. If omitted (or set to zero), the variable is irrelevant for the objective value.
+             */
+            virtual void addLowerBoundedContinuousVariable(std::string const& name, double lowerBound, double objectiveFunctionCoefficient = 0) = 0;
+
+            /*!
+             * Registers an upper-bounded continuous variable, i.e. a variable that may take all real values up to its
+             * upper bound.
+             *
+             * @param name The name of the variable.
+             * @param upperBound The upper bound of the variable.
+             * @param objectiveFunctionCoefficient The coefficient with which the variable appears in the objective
+             * function. If omitted (or set to zero), the variable is irrelevant for the objective value.
+             */
+            virtual void addUpperBoundedContinuousVariable(std::string const& name, double upperBound, double objectiveFunctionCoefficient = 0) = 0;
+            
+            /*!
+             * Registers a unbounded continuous variable, i.e. a variable that may take all real values.
              *
              * @param name The name of the variable.
-             * @param variableType The type of the variable.
-             * @param lowerBound The lower bound of the variable. Note that this parameter is ignored if the variable 
-             * is not bounded from below.
-             * @param upperBound The upper bound of the variable. Note that this parameter is ignored if the variable
-             * is not bounded from above.
              * @param objectiveFunctionCoefficient The coefficient with which the variable appears in the objective
-             * function. If set to zero, the variable is irrelevant for the objective value.
-             * @return The index of the newly created variable. This index can be used to retrieve the variables value
-             * in a solution after the model has been optimized.
+             * function. If omitted (or set to zero), the variable is irrelevant for the objective value.
              */
-            virtual uint_fast64_t createContinuousVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) = 0;
+            virtual void addUnboundedContinuousVariable(std::string const& name, double objectiveFunctionCoefficient = 0) = 0;
             
             /*!
-             * Creates an integer variable.
+             * Registers an upper- and lower-bounded integer variable, i.e. a variable that may take all integer values
+             * within its bounds.
+             *
+             * @param name The name of the variable.
+             * @param lowerBound The lower bound of the variable.
+             * @param upperBound The upper bound of the variable.
+             * @param objectiveFunctionCoefficient The coefficient with which the variable appears in the objective
+             * function. If omitted (or set to zero), the variable is irrelevant for the objective value.
+             */
+            virtual void addBoundedIntegerVariable(std::string const& name, double lowerBound, double upperBound, double objectiveFunctionCoefficient = 0) = 0;
+            
+            /*!
+             * Registers a lower-bounded integer variable, i.e. a variable that may take all integer values up to its
+             * lower bound.
+             *
+             * @param name The name of the variable.
+             * @param lowerBound The lower bound of the variable.
+             * @param objectiveFunctionCoefficient The coefficient with which the variable appears in the objective
+             * function. If omitted (or set to zero), the variable is irrelevant for the objective value.
+             */
+            virtual void addLowerBoundedIntegerVariable(std::string const& name, double lowerBound, double objectiveFunctionCoefficient = 0) = 0;
+            
+            /*!
+             * Registers an upper-bounded integer variable, i.e. a variable that may take all integer values up to its
+             * lower bound.
+             *
+             * @param name The name of the variable.
+             * @param upperBound The upper bound of the variable.
+             * @param objectiveFunctionCoefficient The coefficient with which the variable appears in the objective
+             * function. If omitted (or set to zero), the variable is irrelevant for the objective value.
+             */
+            virtual void addUpperBoundedIntegerVariable(std::string const& name, double upperBound, double objectiveFunctionCoefficient = 0) = 0;
+
+            /*!
+             * Registers an unbounded integer variable, i.e. a variable that may take all integer values.
              *
              * @param name The name of the variable.
-             * @param variableType The type of the variable.
-             * @param lowerBound The lower bound of the variable. Note that this parameter is ignored if the variable
-             * is not bounded from below.
-             * @param upperBound The upper bound of the variable. Note that this parameter is ignored if the variable
-             * is not bounded from above.
              * @param objectiveFunctionCoefficient The coefficient with which the variable appears in the objective
-             * function. If set to zero, the variable is irrelevant for the objective value.
-             * @return The index of the newly created variable. This index can be used to retrieve the variables value
-             * in a solution after the model has been optimized.
+             * function. If omitted (or set to zero), the variable is irrelevant for the objective value.
              */
-            virtual uint_fast64_t createIntegerVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) = 0;
+            virtual void addUnboundedIntegerVariable(std::string const& name, double objectiveFunctionCoefficient = 0) = 0;
             
             /*!
-             * Creates a binary variable, i.e. a variable that may be either zero or one.
+             * Registers a boolean variable, i.e. a variable that may be either 0 or 1.
              *
              * @param name The name of the variable.
-             * @param variableType The type of the variable.
-             * @param lowerBound The lower bound of the variable. Note that this parameter is ignored if the variable
-             * is not bounded from below.
-             * @param upperBound The upper bound of the variable. Note that this parameter is ignored if the variable
-             * is not bounded from above.
              * @param objectiveFunctionCoefficient The coefficient with which the variable appears in the objective
-             * function. If set to zero, the variable is irrelevant for the objective value.
-             * @return The index of the newly created variable. This index can be used to retrieve the variables value
-             * in a solution after the model has been optimized.
+             * function. If omitted (or set to zero), the variable is irrelevant for the objective value.
              */
-            virtual uint_fast64_t createBinaryVariable(std::string const& name, double objectiveFunctionCoefficient) = 0;
+            virtual void addBinaryVariable(std::string const& name, double objectiveFunctionCoefficient = 0) = 0;
             
             /*!
              * Updates the model to make the variables that have been declared since the last call to <code>update</code>
@@ -109,18 +139,13 @@ namespace storm {
             virtual void update() const = 0;
             
             /*!
-             * Adds a constraint of the form
-             *      a_1*x_1 + ... + a_n*x_n  op  c
-             * to the LP problem.
+             * Adds a the given constraint to the LP problem.
              *
              * @param name The name of the constraint. If empty, the constraint has no name.
-             * @param variables A vector of variable indices that define the appearing variables x_1, ..., x_n.
-             * @param coefficients A vector of coefficients that define the a_1, ..., a_n. The i-ith entry specifies the
-             * coefficient of the variable whose index appears at the i-th position in the vector of variable indices.
-             * @param boundType The bound type specifies the operator op used in the constraint.
-             * @param rhs This defines the value of the constant appearing on the right-hand side of the constraint.
+             * @param constraint An expression that represents the constraint. The given constraint must be a linear
+             * (in)equality over the registered variables.
              */
-            virtual void addConstraint(std::string const& name, std::vector<uint_fast64_t> const& variables, std::vector<double> const& coefficients, BoundType const& boundType, double rhs) = 0;
+            virtual void addConstraint(std::string const& name, storm::expressions::Expression const& constraint) = 0;
             
             /*!
              * Optimizes the LP problem previously constructed. Afterwards, the methods isInfeasible, isUnbounded and
@@ -154,34 +179,31 @@ namespace storm {
             virtual bool isOptimal() const = 0;
             
             /*!
-             * Retrieves the value of the integer variable with the given index. Note that this may only be called, if
+             * Retrieves the value of the integer variable with the given name. Note that this may only be called, if
              * the model was found to be optimal, i.e. iff isOptimal() returns true.
              *
-             * @param variableIndex The index of the integer variable whose value to query. If this index does not
-             * belong to a previously declared integer variable, the behaviour is undefined.
+             * @param name The name of the variable whose integer value (in the optimal solution) to retrieve.
              * @return The value of the integer variable in the optimal solution.
              */
-            virtual int_fast64_t getIntegerValue(uint_fast64_t variableIndex) const = 0;
+            virtual int_fast64_t getIntegerValue(std::string const& name) const = 0;
             
             /*!
-             * Retrieves the value of the binary variable with the given index. Note that this may only be called, if
+             * Retrieves the value of the binary variable with the given name. Note that this may only be called, if
              * the model was found to be optimal, i.e. iff isOptimal() returns true.
              *
-             * @param variableIndex The index of the binary variable whose value to query. If this index does not
-             * belong to a previously declared binary variable, the behaviour is undefined.
+             * @param name The name of the variable whose binary (boolean) value (in the optimal solution) to retrieve.
              * @return The value of the binary variable in the optimal solution.
              */
-            virtual bool getBinaryValue(uint_fast64_t variableIndex) const = 0;
+            virtual bool getBinaryValue(std::string const& name) const = 0;
             
             /*!
-             * Retrieves the value of the continuous variable with the given index. Note that this may only be called,
+             * Retrieves the value of the continuous variable with the given name. Note that this may only be called,
              * if the model was found to be optimal, i.e. iff isOptimal() returns true.
              *
-             * @param variableIndex The index of the continuous variable whose value to query. If this index does not
-             * belong to a previously declared continuous variable, the behaviour is undefined.
+             * @param name The name of the variable whose continuous value (in the optimal solution) to retrieve.
              * @return The value of the continuous variable in the optimal solution.
              */
-            virtual double getContinuousValue(uint_fast64_t variableIndex) const = 0;
+            virtual double getContinuousValue(std::string const& name) const = 0;
 
             /*!
              * Retrieves the value of the objective function. Note that this may only be called, if the model was found
diff --git a/src/storage/MaximalEndComponentDecomposition.cpp b/src/storage/MaximalEndComponentDecomposition.cpp
index 56ebbd34c..0f6a44f65 100644
--- a/src/storage/MaximalEndComponentDecomposition.cpp
+++ b/src/storage/MaximalEndComponentDecomposition.cpp
@@ -88,7 +88,7 @@ namespace storm {
                             for (uint_fast64_t choice = nondeterministicChoiceIndices[state]; choice < nondeterministicChoiceIndices[state + 1]; ++choice) {
                                 bool choiceContainedInMEC = true;
                                 for (auto const& entry : transitionMatrix.getRow(choice)) {
-                                    if (scc.find(entry.first) == scc.end()) {
+                                    if (scc.find(entry.getColumn()) == scc.end()) {
                                         choiceContainedInMEC = false;
                                         break;
                                     }
@@ -116,8 +116,8 @@ namespace storm {
                         statesToCheck.clear();
                         for (auto state : statesToRemove) {
                             for (auto const& entry : backwardTransitions.getRow(state)) {
-                                if (scc.find(entry.first) != scc.end()) {
-                                    statesToCheck.set(entry.first);
+                                if (scc.find(entry.getColumn()) != scc.end()) {
+                                    statesToCheck.set(entry.getColumn());
                                 }
                             }
                         }
@@ -154,7 +154,7 @@ namespace storm {
                     for (uint_fast64_t choice = nondeterministicChoiceIndices[state]; choice < nondeterministicChoiceIndices[state + 1]; ++choice) {
                         bool choiceContained = true;
                         for (auto const& entry : transitionMatrix.getRow(choice)) {
-                            if (mecStateSet.find(entry.first) == mecStateSet.end()) {
+                            if (mecStateSet.find(entry.getColumn()) == mecStateSet.end()) {
                                 choiceContained = false;
                                 break;
                             }
diff --git a/src/storage/SparseMatrix.cpp b/src/storage/SparseMatrix.cpp
index 6955ec053..c6bfd0214 100644
--- a/src/storage/SparseMatrix.cpp
+++ b/src/storage/SparseMatrix.cpp
@@ -17,6 +17,41 @@ extern log4cplus::Logger logger;
 namespace storm {
     namespace storage {
         
+        template<typename T>
+        MatrixEntry<T>::MatrixEntry(uint_fast64_t column, T value) : entry(column, value) {
+            // Intentionally left empty.
+        }
+        
+        template<typename T>
+        MatrixEntry<T>::MatrixEntry(std::pair<uint_fast64_t, T>&& pair) : entry(std::move(pair)) {
+            // Intentionally left empty.
+        }
+        
+        template<typename T>
+        uint_fast64_t const& MatrixEntry<T>::getColumn() const {
+            return this->entry.first;
+        }
+        
+        template<typename T>
+        uint_fast64_t& MatrixEntry<T>::getColumn() {
+            return this->entry.first;
+        }
+        
+        template<typename T>
+        T const& MatrixEntry<T>::getValue() const {
+            return this->entry.second;
+        }
+        
+        template<typename T>
+        T& MatrixEntry<T>::getValue() {
+            return this->entry.second;
+        }
+        
+        template<typename T>
+        std::pair<uint_fast64_t, T> const& MatrixEntry<T>::getColumnValuePair() const {
+            return this->entry;
+        }
+        
         template<typename T>
         SparseMatrixBuilder<T>::SparseMatrixBuilder(uint_fast64_t rows, uint_fast64_t columns, uint_fast64_t entries, bool hasCustomRowGrouping, uint_fast64_t rowGroups) : rowCountSet(rows != 0), rowCount(rows), columnCountSet(columns != 0), columnCount(columns), entryCount(entries), hasCustomRowGrouping(hasCustomRowGrouping), rowGroupCountSet(rowGroups != 0), rowGroupCount(rowGroups), rowGroupIndices(), storagePreallocated(rows != 0 && columns != 0 && entries != 0), columnsAndValues(), rowIndications(), currentEntryCount(0), lastRow(0), lastColumn(0), currentRowGroup(0) {
             this->prepareInternalStorage();
@@ -168,7 +203,7 @@ namespace storm {
         void SparseMatrixBuilder<T>::prepareInternalStorage() {
             // Only allocate the memory for the matrix contents if the dimensions of the matrix are already known.
             if (storagePreallocated) {
-                columnsAndValues = std::vector<std::pair<uint_fast64_t, T>>(entryCount, std::make_pair(0, storm::utility::constantZero<T>()));
+                columnsAndValues = std::vector<MatrixEntry<T>>(entryCount, MatrixEntry<T>(0, storm::utility::constantZero<T>()));
                 rowIndications = std::vector<uint_fast64_t>(rowCount + 1, 0);
             } else {
                 rowIndications.push_back(0);
@@ -218,17 +253,17 @@ namespace storm {
         }
         
         template<typename T>
-        SparseMatrix<T>::SparseMatrix() : rowCount(0), columnCount(0), entryCount(0), columnsAndValues(), rowIndications(), rowGroupIndices() {
+        SparseMatrix<T>::SparseMatrix() : rowCount(0), columnCount(0), entryCount(0), nonzeroEntryCount(0), columnsAndValues(), rowIndications(), rowGroupIndices() {
             // Intentionally left empty.
         }
         
         template<typename T>
-        SparseMatrix<T>::SparseMatrix(SparseMatrix<T> const& other) : rowCount(other.rowCount), columnCount(other.columnCount), entryCount(other.entryCount), columnsAndValues(other.columnsAndValues), rowIndications(other.rowIndications), rowGroupIndices(other.rowGroupIndices) {
+        SparseMatrix<T>::SparseMatrix(SparseMatrix<T> const& other) : rowCount(other.rowCount), columnCount(other.columnCount), entryCount(other.entryCount), nonzeroEntryCount(other.nonzeroEntryCount), columnsAndValues(other.columnsAndValues), rowIndications(other.rowIndications), rowGroupIndices(other.rowGroupIndices) {
             // Intentionally left empty.
         }
         
         template<typename T>
-        SparseMatrix<T>::SparseMatrix(SparseMatrix<T>&& other) : rowCount(other.rowCount), columnCount(other.columnCount), entryCount(other.entryCount), columnsAndValues(std::move(other.columnsAndValues)), rowIndications(std::move(other.rowIndications)), rowGroupIndices(std::move(other.rowGroupIndices)) {
+        SparseMatrix<T>::SparseMatrix(SparseMatrix<T>&& other) : rowCount(other.rowCount), columnCount(other.columnCount), entryCount(other.entryCount), nonzeroEntryCount(other.nonzeroEntryCount), columnsAndValues(std::move(other.columnsAndValues)), rowIndications(std::move(other.rowIndications)), rowGroupIndices(std::move(other.rowGroupIndices)) {
             // Now update the source matrix
             other.rowCount = 0;
             other.columnCount = 0;
@@ -236,13 +271,21 @@ namespace storm {
         }
         
         template<typename T>
-        SparseMatrix<T>::SparseMatrix(uint_fast64_t columnCount, std::vector<uint_fast64_t> const& rowIndications, std::vector<std::pair<uint_fast64_t, T>> const& columnsAndValues, std::vector<uint_fast64_t> const& rowGroupIndices) : rowCount(rowIndications.size() - 1), columnCount(columnCount), entryCount(columnsAndValues.size()), columnsAndValues(columnsAndValues), rowIndications(rowIndications), rowGroupIndices(rowGroupIndices) {
-            // Intentionally left empty.
+        SparseMatrix<T>::SparseMatrix(uint_fast64_t columnCount, std::vector<uint_fast64_t> const& rowIndications, std::vector<MatrixEntry<T>> const& columnsAndValues, std::vector<uint_fast64_t> const& rowGroupIndices) : rowCount(rowIndications.size() - 1), columnCount(columnCount), entryCount(columnsAndValues.size()), nonzeroEntryCount(0), columnsAndValues(columnsAndValues), rowIndications(rowIndications), rowGroupIndices(rowGroupIndices) {
+            for (auto const& element : *this) {
+                if (element.getValue() != storm::utility::constantZero<T>()) {
+                    ++this->nonzeroEntryCount;
+                }
+            }
         }
         
         template<typename T>
-        SparseMatrix<T>::SparseMatrix(uint_fast64_t columnCount, std::vector<uint_fast64_t>&& rowIndications, std::vector<std::pair<uint_fast64_t, T>>&& columnsAndValues, std::vector<uint_fast64_t>&& rowGroupIndices) : rowCount(rowIndications.size() - 1), columnCount(columnCount), entryCount(columnsAndValues.size()), columnsAndValues(std::move(columnsAndValues)), rowIndications(std::move(rowIndications)), rowGroupIndices(std::move(rowGroupIndices)) {
-            // Intentionally left empty.
+        SparseMatrix<T>::SparseMatrix(uint_fast64_t columnCount, std::vector<uint_fast64_t>&& rowIndications, std::vector<MatrixEntry<T>>&& columnsAndValues, std::vector<uint_fast64_t>&& rowGroupIndices) : rowCount(rowIndications.size() - 1), columnCount(columnCount), entryCount(columnsAndValues.size()), nonzeroEntryCount(0), columnsAndValues(std::move(columnsAndValues)), rowIndications(std::move(rowIndications)), rowGroupIndices(std::move(rowGroupIndices)) {
+            for (auto const& element : *this) {
+                if (element.getValue() != storm::utility::constantZero<T>()) {
+                    ++this->nonzeroEntryCount;
+                }
+            }
         }
                 
         template<typename T>
@@ -252,6 +295,7 @@ namespace storm {
                 rowCount = other.rowCount;
                 columnCount = other.columnCount;
                 entryCount = other.entryCount;
+                nonzeroEntryCount = other.nonzeroEntryCount;
                 
                 columnsAndValues = other.columnsAndValues;
                 rowIndications = other.rowIndications;
@@ -268,6 +312,7 @@ namespace storm {
                 rowCount = other.rowCount;
                 columnCount = other.columnCount;
                 entryCount = other.entryCount;
+                nonzeroEntryCount = other.nonzeroEntryCount;
                 
                 columnsAndValues = std::move(other.columnsAndValues);
                 rowIndications = std::move(other.rowIndications);
@@ -294,17 +339,17 @@ namespace storm {
             for (uint_fast64_t row = 0; row < this->getRowCount(); ++row) {
                 for (const_iterator it1 = this->begin(row), ite1 = this->end(row), it2 = other.begin(row), ite2 = other.end(row); it1 != ite1 && it2 != ite2; ++it1, ++it2) {
                     // Skip over all zero entries in both matrices.
-                    while (it1 != ite1 && it1->second == storm::utility::constantZero<T>()) {
+                    while (it1 != ite1 && it1->getValue() == storm::utility::constantZero<T>()) {
                         ++it1;
                     }
-                    while (it2 != ite2 && it2->second == storm::utility::constantZero<T>()) {
+                    while (it2 != ite2 && it2->getValue() == storm::utility::constantZero<T>()) {
                         ++it2;
                     }
                     if ((it1 == ite1) || (it2 == ite2)) {
                         equalityResult = (it1 == ite1) ^ (it2 == ite2);
                         break;
                     } else {
-                        if (it1->first != it2->first || it1->second != it2->second) {
+                        if (it1->getColumn() != it2->getColumn() || it1->getValue() != it2->getValue()) {
                             equalityResult = false;
                             break;
                         }
@@ -330,6 +375,11 @@ namespace storm {
             return entryCount;
         }
         
+        template<typename T>
+        uint_fast64_t SparseMatrix<T>::getNonzeroEntryCount() const {
+            return nonzeroEntryCount;
+        }
+        
         template<typename T>
         uint_fast64_t SparseMatrix<T>::getRowGroupCount() const {
             return rowGroupIndices.size() - 1;
@@ -374,12 +424,12 @@ namespace storm {
             
             // If there is at least one entry in this row, we can just set it to one, modify its column value to the
             // one given by the parameter and set all subsequent elements of this row to zero.
-            columnValuePtr->first = column;
-            columnValuePtr->second = storm::utility::constantOne<T>();
+            columnValuePtr->getColumn() = column;
+            columnValuePtr->getValue() = storm::utility::constantOne<T>();
             ++columnValuePtr;
             for (; columnValuePtr != columnValuePtrEnd; ++columnValuePtr) {
-                columnValuePtr->first = 0;
-                columnValuePtr->second = storm::utility::constantZero<T>();
+                columnValuePtr->getColumn() = 0;
+                columnValuePtr->getValue() = storm::utility::constantZero<T>();
             }
         }
         
@@ -387,8 +437,8 @@ namespace storm {
         T SparseMatrix<T>::getConstrainedRowSum(uint_fast64_t row, storm::storage::BitVector const& constraint) const {
             T result(0);
             for (const_iterator it = this->begin(row), ite = this->end(row); it != ite; ++it) {
-                if (constraint.get(it->first)) {
-                    result += it->second;
+                if (constraint.get(it->getColumn())) {
+                    result += it->getValue();
                 }
             }
             return result;
@@ -442,10 +492,10 @@ namespace storm {
                     bool foundDiagonalElement = false;
                     
                     for (const_iterator it = this->begin(i), ite = this->end(i); it != ite; ++it) {
-                        if (columnConstraint.get(it->first)) {
+                        if (columnConstraint.get(it->getColumn())) {
                             ++subEntries;
                             
-                            if (index == it->first) {
+                            if (index == it->getColumn()) {
                                 foundDiagonalElement = true;
                             }
                         }
@@ -492,14 +542,14 @@ namespace storm {
                     bool insertedDiagonalElement = false;
                     
                     for (const_iterator it = this->begin(i), ite = this->end(i); it != ite; ++it) {
-                        if (columnConstraint.get(it->first)) {
-                            if (index == it->first) {
+                        if (columnConstraint.get(it->getColumn())) {
+                            if (index == it->getColumn()) {
                                 insertedDiagonalElement = true;
-                            } else if (insertDiagonalEntries && !insertedDiagonalElement && it->first > index) {
+                            } else if (insertDiagonalEntries && !insertedDiagonalElement && it->getColumn() > index) {
                                 matrixBuilder.addNextValue(rowCount, bitsSetBeforeIndex[index], storm::utility::constantZero<T>());
                                 insertedDiagonalElement = true;
                             }
-                            matrixBuilder.addNextValue(rowCount, bitsSetBeforeIndex[it->first], it->second);
+                            matrixBuilder.addNextValue(rowCount, bitsSetBeforeIndex[it->getColumn()], it->getValue());
                         }
                     }
                     if (insertDiagonalEntries && !insertedDiagonalElement) {
@@ -525,7 +575,7 @@ namespace storm {
                 // Iterate through that row and count the number of slots we have to reserve for copying.
                 bool foundDiagonalElement = false;
                 for (const_iterator it = this->begin(rowToCopy), ite = this->end(rowToCopy); it != ite; ++it) {
-                    if (it->first == rowGroupIndex) {
+                    if (it->getColumn() == rowGroupIndex) {
                         foundDiagonalElement = true;
                     }
                     ++subEntries;
@@ -547,13 +597,13 @@ namespace storm {
                 // there is no entry yet.
                 bool insertedDiagonalElement = false;
                 for (const_iterator it = this->begin(rowToCopy), ite = this->end(rowToCopy); it != ite; ++it) {
-                    if (it->first == rowGroupIndex) {
+                    if (it->getColumn() == rowGroupIndex) {
                         insertedDiagonalElement = true;
-                    } else if (insertDiagonalEntries && !insertedDiagonalElement && it->first > rowGroupIndex) {
+                    } else if (insertDiagonalEntries && !insertedDiagonalElement && it->getColumn() > rowGroupIndex) {
                         matrixBuilder.addNextValue(rowGroupIndex, rowGroupIndex, storm::utility::constantZero<T>());
                         insertedDiagonalElement = true;
                     }
-                    matrixBuilder.addNextValue(rowGroupIndex, it->first, it->second);
+                    matrixBuilder.addNextValue(rowGroupIndex, it->getColumn(), it->getValue());
                 }
                 if (insertDiagonalEntries && !insertedDiagonalElement) {
                     matrixBuilder.addNextValue(rowGroupIndex, rowGroupIndex, storm::utility::constantZero<T>());
@@ -571,13 +621,13 @@ namespace storm {
             uint_fast64_t entryCount = this->getEntryCount();
             
             std::vector<uint_fast64_t> rowIndications(rowCount + 1);
-            std::vector<std::pair<uint_fast64_t, T>> columnsAndValues(entryCount);
+            std::vector<MatrixEntry<T>> columnsAndValues(entryCount);
             
             // First, we need to count how many entries each column has.
             for (uint_fast64_t group = 0; group < columnCount; ++group) {
                 for (auto const& transition : joinGroups ? this->getRowGroup(group) : this->getRow(group)) {
-                    if (transition.second != storm::utility::constantZero<T>()) {
-                        ++rowIndications[transition.first + 1];
+                    if (transition.getValue() != storm::utility::constantZero<T>()) {
+                        ++rowIndications[transition.getColumn() + 1];
                     }
                 }
             }
@@ -595,9 +645,9 @@ namespace storm {
             // Now we are ready to actually fill in the values of the transposed matrix.
             for (uint_fast64_t group = 0; group < columnCount; ++group) {
                 for (auto const& transition : joinGroups ? this->getRowGroup(group) : this->getRow(group)) {
-                    if (transition.second != storm::utility::constantZero<T>()) {
-                        columnsAndValues[nextIndices[transition.first]] = std::make_pair(group, transition.second);
-                        nextIndices[transition.first]++;
+                    if (transition.getValue() != storm::utility::constantZero<T>()) {
+                        columnsAndValues[nextIndices[transition.getColumn()]] = std::make_pair(group, transition.getValue());
+                        nextIndices[transition.getColumn()]++;
                     }
                 }
             }
@@ -626,8 +676,8 @@ namespace storm {
             bool foundDiagonalElement = false;
             for (uint_fast64_t group = 0; group < this->getRowGroupCount(); ++group) {
                 for (auto& entry : this->getRowGroup(group)) {
-                    if (entry.first == group) {
-                        entry.second = one - entry.second;
+                    if (entry.getColumn() == group) {
+                        entry.getValue() = one - entry.getValue();
                         foundDiagonalElement = true;
                     }
                 }
@@ -644,8 +694,8 @@ namespace storm {
             // Iterate over all row groups and negate all the elements that are not on the diagonal.
             for (uint_fast64_t group = 0; group < this->getRowGroupCount(); ++group) {
                 for (auto& entry : this->getRowGroup(group)) {
-                    if (entry.first != group) {
-                        entry.second = -entry.second;
+                    if (entry.getColumn() != group) {
+                        entry.getValue() = -entry.getValue();
                     }
                 }
             }
@@ -656,8 +706,8 @@ namespace storm {
             // Iterate over all rows and negate all the elements that are not on the diagonal.
             for (uint_fast64_t group = 0; group < this->getRowGroupCount(); ++group) {
                 for (auto& entry : this->getRowGroup(group)) {
-                    if (entry.first == group) {
-                        entry.second = storm::utility::constantZero<T>();
+                    if (entry.getColumn() == group) {
+                        entry.getValue() = storm::utility::constantZero<T>();
                     }
                 }
             }
@@ -680,9 +730,9 @@ namespace storm {
                 // to invert the entry.
                 T diagonalValue = storm::utility::constantZero<T>();
                 for (const_iterator it = this->begin(rowNumber), ite = this->end(rowNumber); it != ite; ++it) {
-                    if (it->first == rowNumber) {
-                        diagonalValue += it->second;
-                    } else if (it->first > rowNumber) {
+                    if (it->getColumn() == rowNumber) {
+                        diagonalValue += it->getValue();
+                    } else if (it->getColumn() > rowNumber) {
                         break;
                     }
                 }
@@ -701,13 +751,13 @@ namespace storm {
             // add the result to the corresponding position in the vector.
             for (uint_fast64_t row = 0; row < rowCount && row < otherMatrix.rowCount; ++row) {
                 for (const_iterator it1 = this->begin(row), ite1 = this->end(row), it2 = otherMatrix.begin(row), ite2 = otherMatrix.end(row); it1 != ite1 && it2 != ite2; ++it1) {
-                    if (it1->first < it2->first) {
+                    if (it1->getColumn() < it2->getColumn()) {
                         continue;
                     } else {
                         // If the precondition of this method (i.e. that the given matrix is a submatrix
                         // of the current one) was fulfilled, we know now that the two elements are in
                         // the same column, so we can multiply and add them to the row sum vector.
-                        result[row] += it2->second * it1->second;
+                        result[row] += it2->getValue() * it1->getValue();
                         ++it2;
                     }
                 }
@@ -735,7 +785,7 @@ namespace storm {
                                       *resultIterator = storm::utility::constantZero<T>();
                                       
                                       for (ite = this->begin() + *(rowIterator + 1); it != ite; ++it) {
-                                          *resultIterator += it->second * vector[it->first];
+                                          *resultIterator += it->getValue() * vector[it->getColumn()];
                                       }
                                   }
                               });
@@ -750,7 +800,7 @@ namespace storm {
                 *resultIterator = storm::utility::constantZero<T>();
                 
                 for (ite = this->begin() + *(rowIterator + 1); it != ite; ++it) {
-                    *resultIterator += it->second * vector[it->first];
+                    *resultIterator += it->getValue() * vector[it->getColumn()];
                 }
             }
 #endif
@@ -761,7 +811,7 @@ namespace storm {
             uint_fast64_t size = sizeof(*this);
             
             // Add size of columns and values.
-            size += sizeof(std::pair<uint_fast64_t, T>) * columnsAndValues.capacity();
+            size += sizeof(MatrixEntry<T>) * columnsAndValues.capacity();
             
             // Add row_indications size.
             size += sizeof(uint_fast64_t) * rowIndications.capacity();
@@ -833,7 +883,7 @@ namespace storm {
         T SparseMatrix<T>::getRowSum(uint_fast64_t row) const {
             T sum = storm::utility::constantZero<T>();
             for (const_iterator it = this->begin(row), ite = this->end(row); it != ite; ++it) {
-                sum += it->second;
+                sum += it->getValue();
             }
             return sum;
         }
@@ -849,10 +899,10 @@ namespace storm {
             for (uint_fast64_t row = 0; row < this->getRowCount(); ++row) {
                 for (const_iterator it1 = this->begin(row), ite1 = this->end(row), it2 = matrix.begin(row), ite2 = matrix.end(row); it1 != ite1; ++it1) {
                     // Skip over all entries of the other matrix that are before the current entry in the current matrix.
-                    while (it2 != ite2 && it2->first < it1->first) {
+                    while (it2 != ite2 && it2->getColumn() < it1->getColumn()) {
                         ++it2;
                     }
-                    if (it2 == ite2 || it1->first != it2->first) {
+                    if (it2 == ite2 || it1->getColumn() != it2->getColumn()) {
                         return false;
                     }
                 }
@@ -879,8 +929,8 @@ namespace storm {
                     out << i << "\t(\t";
                     uint_fast64_t currentRealIndex = 0;
                     while (currentRealIndex < matrix.columnCount) {
-                        if (nextIndex < matrix.rowIndications[i + 1] && currentRealIndex == matrix.columnsAndValues[nextIndex].first) {
-                            out << matrix.columnsAndValues[nextIndex].second << "\t";
+                        if (nextIndex < matrix.rowIndications[i + 1] && currentRealIndex == matrix.columnsAndValues[nextIndex].getColumn()) {
+                            out << matrix.columnsAndValues[nextIndex].getValue() << "\t";
                             ++nextIndex;
                         } else {
                             out << "0\t";
@@ -915,10 +965,12 @@ namespace storm {
             return result;
         }
         
-        // Explicitly instantiate the builder and the matrix.
+        // Explicitly instantiate the entry, builder and the matrix.
+        template class MatrixEntry<double>;
         template class SparseMatrixBuilder<double>;
         template class SparseMatrix<double>;
         template std::ostream& operator<<(std::ostream& out, SparseMatrix<double> const& matrix);
+        template class MatrixEntry<int>;
         template class SparseMatrixBuilder<int>;
         template class SparseMatrix<int>;
         template std::ostream& operator<<(std::ostream& out, SparseMatrix<int> const& matrix);
diff --git a/src/storage/SparseMatrix.h b/src/storage/SparseMatrix.h
index f85dfdd52..b60cbdc16 100644
--- a/src/storage/SparseMatrix.h
+++ b/src/storage/SparseMatrix.h
@@ -8,6 +8,7 @@
 
 #include "src/storage/BitVector.h"
 #include "src/utility/constants.h"
+#include "src/utility/OsDetection.h"
 
 #include "src/exceptions/InvalidArgumentException.h"
 #include "src/exceptions/OutOfRangeException.h"
@@ -27,6 +28,83 @@ namespace storm {
         // Forward declare matrix class.
         template<typename T> class SparseMatrix;
         
+        template<typename T>
+        class MatrixEntry {
+        public:
+            /*!
+             * Constructs a matrix entry with the given column and value.
+             *
+             * @param column The column of the matrix entry.
+             * @param value The value of the matrix entry.
+             */
+            MatrixEntry(uint_fast64_t column, T value);
+            
+            /*!
+             * Move-constructs the matrix entry fro the given column-value pair.
+             *
+             * @param pair The column-value pair from which to move-construct the matrix entry.
+             */
+            MatrixEntry(std::pair<uint_fast64_t, T>&& pair);
+            
+            MatrixEntry() = default;
+            MatrixEntry(MatrixEntry const& other) = default;
+            MatrixEntry& operator=(MatrixEntry const& other) = default;
+#ifndef WINDOWS
+            MatrixEntry(MatrixEntry&& other) = default;
+            MatrixEntry& operator=(MatrixEntry&& other) = default;
+#endif
+            
+            /*!
+             * Retrieves the column of the matrix entry.
+             *
+             * @return The column of the matrix entry.
+             */
+            uint_fast64_t const& getColumn() const;
+            
+            /*!
+             * Retrieves the column of the matrix entry.
+             *
+             * @return The column of the matrix entry.
+             */
+            uint_fast64_t& getColumn();
+            
+            /*!
+             * Retrieves the value of the matrix entry.
+             *
+             * @return The value of the matrix entry.
+             */
+            T const& getValue() const;
+
+            /*!
+             * Retrieves the value of the matrix entry.
+             *
+             * @return The value of the matrix entry.
+             */
+            T& getValue();
+            
+            /*!
+             * Retrieves a pair of column and value that characterizes this entry.
+             *
+             * @return A column-value pair that characterizes this entry.
+             */
+            std::pair<uint_fast64_t, T> const& getColumnValuePair() const;
+            
+        private:
+            // The actual matrix entry.
+            std::pair<uint_fast64_t, T> entry;
+        };
+        
+        /*!
+         * Computes the hash value of a matrix entry.
+         */
+        template<typename T>
+        std::size_t hash_value(MatrixEntry<T> const& matrixEntry) {
+            std::size_t seed = 0;
+            boost::hash_combine(seed, matrixEntry.getColumn());
+            boost::hash_combine(seed, matrixEntry.getValue());
+            return seed;
+        }
+        
         /*!
          * A class that can be used to build a sparse matrix by adding value by value.
          */
@@ -128,7 +206,7 @@ namespace storm {
             bool storagePreallocated;
             
             // The storage for the columns and values of all entries in the matrix.
-            std::vector<std::pair<uint_fast64_t, T>> columnsAndValues;
+            std::vector<MatrixEntry<T>> columnsAndValues;
             
             // A vector containing the indices at which each given row begins. This index is to be interpreted as an
             // index in the valueStorage and the columnIndications vectors. Put differently, the values of the entries
@@ -175,8 +253,8 @@ namespace storm {
             friend class storm::adapters::EigenAdapter;
             friend class storm::adapters::StormAdapter;
             
-            typedef typename std::vector<std::pair<uint_fast64_t, T>>::iterator iterator;
-            typedef typename std::vector<std::pair<uint_fast64_t, T>>::const_iterator const_iterator;
+            typedef typename std::vector<MatrixEntry<T>>::iterator iterator;
+            typedef typename std::vector<MatrixEntry<T>>::const_iterator const_iterator;
             
             /*!
              * This class represents a number of consecutive rows of the matrix.
@@ -284,7 +362,7 @@ namespace storm {
              * @param columnsAndValues The vector containing the columns and values of the entries in the matrix.
              * @param rowGroupIndices The vector representing the row groups in the matrix.
              */
-            SparseMatrix(uint_fast64_t columnCount, std::vector<uint_fast64_t> const& rowIndications, std::vector<std::pair<uint_fast64_t, T>> const& columnsAndValues, std::vector<uint_fast64_t> const& rowGroupIndices);
+            SparseMatrix(uint_fast64_t columnCount, std::vector<uint_fast64_t> const& rowIndications, std::vector<MatrixEntry<T>> const& columnsAndValues, std::vector<uint_fast64_t> const& rowGroupIndices);
             
             /*!
              * Constructs a sparse matrix by moving the given contents.
@@ -294,7 +372,7 @@ namespace storm {
              * @param columnsAndValues The vector containing the columns and values of the entries in the matrix.
              * @param rowGroupIndices The vector representing the row groups in the matrix.
              */
-            SparseMatrix(uint_fast64_t columnCount, std::vector<uint_fast64_t>&& rowIndications, std::vector<std::pair<uint_fast64_t, T>>&& columnsAndValues, std::vector<uint_fast64_t>&& rowGroupIndices);
+            SparseMatrix(uint_fast64_t columnCount, std::vector<uint_fast64_t>&& rowIndications, std::vector<MatrixEntry<T>>&& columnsAndValues, std::vector<uint_fast64_t>&& rowGroupIndices);
 
             /*!
              * Assigns the contents of the given matrix to the current one by deep-copying its contents.
@@ -339,6 +417,13 @@ namespace storm {
              */
             uint_fast64_t getEntryCount() const;
             
+            /*!
+             * Returns the number of nonzero entries in the matrix.
+             *
+             * @return The number of nonzero entries in the matrix.
+             */
+            uint_fast64_t getNonzeroEntryCount() const;
+            
             /*!
              * Returns the number of row groups in the matrix.
              *
@@ -651,8 +736,11 @@ namespace storm {
             // The number of entries in the matrix.
             uint_fast64_t entryCount;
             
+            // The number of nonzero entries in the matrix.
+            uint_fast64_t nonzeroEntryCount;
+            
             // The storage for the columns and values of all entries in the matrix.
-            std::vector<std::pair<uint_fast64_t, T>> columnsAndValues;
+            std::vector<MatrixEntry<T>> columnsAndValues;
             
             // A vector containing the indices at which each given row begins. This index is to be interpreted as an
             // index in the valueStorage and the columnIndications vectors. Put differently, the values of the entries
diff --git a/src/storage/StronglyConnectedComponentDecomposition.cpp b/src/storage/StronglyConnectedComponentDecomposition.cpp
index 6e9f08386..b14559bcb 100644
--- a/src/storage/StronglyConnectedComponentDecomposition.cpp
+++ b/src/storage/StronglyConnectedComponentDecomposition.cpp
@@ -114,33 +114,33 @@ namespace storm {
                 // Now, traverse all successors of the current state.
                 for(; successorIt != model.getRows(currentState).end(); ++successorIt) {
                     // Record if the current state has a self-loop if we are to drop naive SCCs later.
-                    if (dropNaiveSccs && currentState == successorIt->first) {
+                    if (dropNaiveSccs && currentState == successorIt->getColumn()) {
                         statesWithSelfloop.set(currentState, true);
                     }
                     
                     // If we have not visited the successor already, we need to perform the procedure recursively on the
                     // newly found state, but only if it belongs to the subsystem in which we are interested.
-                    if (subsystem.get(successorIt->first)) {
-                        if (!visitedStates.get(successorIt->first)) {
+                    if (subsystem.get(successorIt->getColumn())) {
+                        if (!visitedStates.get(successorIt->getColumn())) {
                             // Save current iterator position so we can continue where we left off later.
                             recursionIteratorStack.pop_back();
                             recursionIteratorStack.push_back(successorIt);
                             
                             // Put unvisited successor on top of our recursion stack and remember that.
-                            recursionStateStack.push_back(successorIt->first);
-                            statesInStack[successorIt->first] = true;
+                            recursionStateStack.push_back(successorIt->getColumn());
+                            statesInStack[successorIt->getColumn()] = true;
                             
                             // Also, put initial value for iterator on corresponding recursion stack.
-                            recursionIteratorStack.push_back(model.getRows(successorIt->first).begin());
+                            recursionIteratorStack.push_back(model.getRows(successorIt->getColumn()).begin());
                             
                             // Perform the actual recursion step in an iterative way.
                             goto recursionStepForward;
                             
                         recursionStepBackward:
-                            lowlinks[currentState] = std::min(lowlinks[currentState], lowlinks[successorIt->first]);
-                        } else if (tarjanStackStates.get(successorIt->first)) {
+                            lowlinks[currentState] = std::min(lowlinks[currentState], lowlinks[successorIt->getColumn()]);
+                        } else if (tarjanStackStates.get(successorIt->getColumn())) {
                             // Update the lowlink of the current state.
-                            lowlinks[currentState] = std::min(lowlinks[currentState], stateIndices[successorIt->first]);
+                            lowlinks[currentState] = std::min(lowlinks[currentState], stateIndices[successorIt->getColumn()]);
                         }
                     }
                 }
@@ -165,7 +165,7 @@ namespace storm {
                     if (onlyBottomSccs) {
                         for (auto const& state : scc) {
                             for (auto const& successor : model.getRows(state)) {
-                                if (scc.find(successor.first) == scc.end()) {
+                                if (scc.find(successor.getColumn()) == scc.end()) {
                                     isBottomScc = false;
                                     break;
                                 }
diff --git a/src/storage/dd/CuddDd.cpp b/src/storage/dd/CuddDd.cpp
index 72abb87bf..cdff92894 100644
--- a/src/storage/dd/CuddDd.cpp
+++ b/src/storage/dd/CuddDd.cpp
@@ -473,15 +473,15 @@ namespace storm {
             return this->ddManager;
         }
         
-        DdForwardIterator<DdType::CUDD> Dd<DdType::CUDD>::begin() const {
+        DdForwardIterator<DdType::CUDD> Dd<DdType::CUDD>::begin(bool enumerateDontCareMetaVariables) const {
             int* cube;
             double value;
             DdGen* generator = this->getCuddAdd().FirstCube(&cube, &value);
-            return DdForwardIterator<DdType::CUDD>(this->getDdManager(), generator, cube, value, Cudd_IsGenEmpty(generator), &this->getContainedMetaVariableNames());
+            return DdForwardIterator<DdType::CUDD>(this->getDdManager(), generator, cube, value, Cudd_IsGenEmpty(generator), &this->getContainedMetaVariableNames(), enumerateDontCareMetaVariables);
         }
         
-        DdForwardIterator<DdType::CUDD> Dd<DdType::CUDD>::end() const {
-            return DdForwardIterator<DdType::CUDD>(this->getDdManager(), nullptr, nullptr, 0, true, nullptr);
+        DdForwardIterator<DdType::CUDD> Dd<DdType::CUDD>::end(bool enumerateDontCareMetaVariables) const {
+            return DdForwardIterator<DdType::CUDD>(this->getDdManager(), nullptr, nullptr, 0, true, nullptr, enumerateDontCareMetaVariables);
         }
         
         std::ostream & operator<<(std::ostream& out, const Dd<DdType::CUDD>& dd) {
diff --git a/src/storage/dd/CuddDd.h b/src/storage/dd/CuddDd.h
index f345c28a2..9f638d329 100644
--- a/src/storage/dd/CuddDd.h
+++ b/src/storage/dd/CuddDd.h
@@ -440,16 +440,20 @@ namespace storm {
             /*!
              * Retrieves an iterator that points to the first meta variable assignment with a non-zero function value.
              *
+             * @param enumerateDontCareMetaVariables If set to true, all meta variable assignments are enumerated, even
+             * if a meta variable does not at all influence the the function value.
              * @return An iterator that points to the first meta variable assignment with a non-zero function value.
              */
-            DdForwardIterator<DdType::CUDD> begin() const;
+            DdForwardIterator<DdType::CUDD> begin(bool enumerateDontCareMetaVariables = true) const;
             
             /*!
              * Retrieves an iterator that points past the end of the container.
              *
+             * @param enumerateDontCareMetaVariables If set to true, all meta variable assignments are enumerated, even
+             * if a meta variable does not at all influence the the function value.
              * @return An iterator that points past the end of the container.
              */
-            DdForwardIterator<DdType::CUDD> end() const;
+            DdForwardIterator<DdType::CUDD> end(bool enumerateDontCareMetaVariables = true) const;
             
             friend std::ostream & operator<<(std::ostream& out, const Dd<DdType::CUDD>& dd);
         private:
diff --git a/src/storage/dd/CuddDdForwardIterator.cpp b/src/storage/dd/CuddDdForwardIterator.cpp
index c0e754c8b..cce5c112c 100644
--- a/src/storage/dd/CuddDdForwardIterator.cpp
+++ b/src/storage/dd/CuddDdForwardIterator.cpp
@@ -5,7 +5,11 @@
 
 namespace storm {
     namespace dd {
-        DdForwardIterator<DdType::CUDD>::DdForwardIterator(std::shared_ptr<DdManager<DdType::CUDD>> ddManager, DdGen* generator, int* cube, double value, bool isAtEnd, std::set<std::string> const* metaVariables) : ddManager(ddManager), generator(generator), cube(cube), value(value), isAtEnd(isAtEnd), metaVariables(metaVariables), cubeCounter(), relevantDontCareDdVariables(), currentValuation() {
+        DdForwardIterator<DdType::CUDD>::DdForwardIterator() : ddManager(), generator(), cube(), value(), isAtEnd(), metaVariables(), enumerateDontCareMetaVariables(), cubeCounter(), relevantDontCareDdVariables(), currentValuation() {
+            // Intentionally left empty.
+        }
+        
+        DdForwardIterator<DdType::CUDD>::DdForwardIterator(std::shared_ptr<DdManager<DdType::CUDD>> ddManager, DdGen* generator, int* cube, double value, bool isAtEnd, std::set<std::string> const* metaVariables, bool enumerateDontCareMetaVariables) : ddManager(ddManager), generator(generator), cube(cube), value(value), isAtEnd(isAtEnd), metaVariables(metaVariables), enumerateDontCareMetaVariables(enumerateDontCareMetaVariables), cubeCounter(), relevantDontCareDdVariables(), currentValuation() {
             // If the given generator is not yet at its end, we need to create the current valuation from the cube from
             // scratch.
             if (!this->isAtEnd) {
@@ -49,6 +53,7 @@ namespace storm {
         }
         
         DdForwardIterator<DdType::CUDD>::~DdForwardIterator() {
+            // We free the pointers sind Cudd allocates them using malloc rather than new/delete.
             if (this->cube != nullptr) {
                 free(this->cube);
             }
@@ -108,29 +113,60 @@ namespace storm {
             // don't cares. In the latter case, we add them to a special list, so we can iterate over their concrete
             // valuations later.
             for (auto const& metaVariableName : *this->metaVariables) {
+                bool metaVariableAppearsInCube = false;
+                std::vector<std::tuple<ADD, std::string, uint_fast64_t>> localRelenvantDontCareDdVariables;
                 auto const& metaVariable = this->ddManager->getMetaVariable(metaVariableName);
                 if (metaVariable.getType() == DdMetaVariable<DdType::CUDD>::MetaVariableType::Bool) {
                     if (this->cube[metaVariable.getDdVariables()[0].getCuddAdd().NodeReadIndex()] == 0) {
-                        currentValuation.setBooleanValue(metaVariableName, false);
+                        metaVariableAppearsInCube = true;
+                        if (!currentValuation.containsBooleanIdentifier(metaVariableName)) {
+                            currentValuation.addBooleanIdentifier(metaVariableName, false);
+                        } else {
+                            currentValuation.setBooleanValue(metaVariableName, false);
+                        }
                     } else if (this->cube[metaVariable.getDdVariables()[0].getCuddAdd().NodeReadIndex()] == 1) {
-                        currentValuation.setBooleanValue(metaVariableName, true);
+                        metaVariableAppearsInCube = true;
+                        if (!currentValuation.containsBooleanIdentifier(metaVariableName)) {
+                            currentValuation.addBooleanIdentifier(metaVariableName, true);
+                        } else {
+                            currentValuation.setBooleanValue(metaVariableName, true);
+                        }
                     } else {
-                        relevantDontCareDdVariables.push_back(std::make_tuple(metaVariable.getDdVariables()[0].getCuddAdd(), metaVariableName, 0));
+                        localRelenvantDontCareDdVariables.push_back(std::make_tuple(metaVariable.getDdVariables()[0].getCuddAdd(), metaVariableName, 0));
                     }
                 } else {
                     int_fast64_t intValue = 0;
                     for (uint_fast64_t bitIndex = 0; bitIndex < metaVariable.getNumberOfDdVariables(); ++bitIndex) {
                         if (cube[metaVariable.getDdVariables()[bitIndex].getCuddAdd().NodeReadIndex()] == 0) {
                             // Leave bit unset.
+                            metaVariableAppearsInCube = true;
                         } else if (cube[metaVariable.getDdVariables()[bitIndex].getCuddAdd().NodeReadIndex()] == 1) {
                             intValue |= 1ull << (metaVariable.getNumberOfDdVariables() - bitIndex - 1);
+                            metaVariableAppearsInCube = true;
                         } else {
                             // Temporarily leave bit unset so we can iterate trough the other option later.
                             // Add the bit to the relevant don't care bits.
-                            this->relevantDontCareDdVariables.push_back(std::make_tuple(metaVariable.getDdVariables()[bitIndex].getCuddAdd(), metaVariableName, metaVariable.getNumberOfDdVariables() - bitIndex - 1));
+                            localRelenvantDontCareDdVariables.push_back(std::make_tuple(metaVariable.getDdVariables()[bitIndex].getCuddAdd(), metaVariableName, metaVariable.getNumberOfDdVariables() - bitIndex - 1));
                         }
                     }
-                    currentValuation.setIntegerValue(metaVariableName, intValue + metaVariable.getLow());
+                    if (this->enumerateDontCareMetaVariables || metaVariableAppearsInCube) {
+                        if (!currentValuation.containsIntegerIdentifier(metaVariableName)) {
+                            currentValuation.addIntegerIdentifier(metaVariableName);
+                        }
+                        currentValuation.setIntegerValue(metaVariableName, intValue + metaVariable.getLow());
+                    }
+                }
+                
+                // If all meta variables are to be enumerated or the meta variable appeared in the cube, we register the
+                // missing bits to later enumerate all possible valuations.
+                if (this->enumerateDontCareMetaVariables || metaVariableAppearsInCube) {
+                    relevantDontCareDdVariables.insert(relevantDontCareDdVariables.end(), localRelenvantDontCareDdVariables.begin(), localRelenvantDontCareDdVariables.end());
+                }
+                
+                // If the meta variable does not appear in the cube and we're not supposed to enumerate such meta variables
+                // we remove the meta variable from the valuation.
+                if (!this->enumerateDontCareMetaVariables && !metaVariableAppearsInCube) {
+                    currentValuation.removeIdentifier(metaVariableName);
                 }
             }
             
diff --git a/src/storage/dd/CuddDdForwardIterator.h b/src/storage/dd/CuddDdForwardIterator.h
index 054438d2e..8b08af929 100644
--- a/src/storage/dd/CuddDdForwardIterator.h
+++ b/src/storage/dd/CuddDdForwardIterator.h
@@ -25,7 +25,7 @@ namespace storm {
             friend class Dd<DdType::CUDD>;
 
             // Default-instantiate the constructor.
-            DdForwardIterator() = default;
+            DdForwardIterator();
             
             // Forbid copy-construction and copy assignment, because ownership of the internal pointer is unclear then.
             DdForwardIterator(DdForwardIterator<DdType::CUDD> const& other) = delete;
@@ -82,8 +82,10 @@ namespace storm {
              * @param isAtEnd A flag that indicates whether the iterator is at its end and may not be moved forward any
              * more.
              * @param metaVariables The meta variables that appear in the DD.
+             * @param enumerateDontCareMetaVariables If set to true, all meta variable assignments are enumerated, even
+             * if a meta variable does not at all influence the the function value.
              */
-            DdForwardIterator(std::shared_ptr<DdManager<DdType::CUDD>> ddManager, DdGen* generator, int* cube, double value, bool isAtEnd, std::set<std::string> const* metaVariables = nullptr);
+            DdForwardIterator(std::shared_ptr<DdManager<DdType::CUDD>> ddManager, DdGen* generator, int* cube, double value, bool isAtEnd, std::set<std::string> const* metaVariables = nullptr, bool enumerateDontCareMetaVariables = true);
             
             /*!
              * Recreates the internal information when a new cube needs to be treated.
@@ -114,6 +116,10 @@ namespace storm {
             // The set of meta variables appearing in the DD.
             std::set<std::string> const* metaVariables;
             
+            // A flag that indicates whether the iterator is supposed to enumerate meta variable valuations even if
+            // they don't influence the function value.
+            bool enumerateDontCareMetaVariables;
+            
             // A number that represents how many assignments of the current cube have already been returned previously.
             // This is needed, because cubes may represent many assignments (if they have don't care variables).
             uint_fast64_t cubeCounter;
diff --git a/src/storage/dd/CuddDdManager.cpp b/src/storage/dd/CuddDdManager.cpp
index 64261d0aa..087382c71 100644
--- a/src/storage/dd/CuddDdManager.cpp
+++ b/src/storage/dd/CuddDdManager.cpp
@@ -32,7 +32,7 @@ bool CuddOptionsRegistered = storm::settings::Settings::registerNewModule([] (st
 namespace storm {
     namespace dd {
         DdManager<DdType::CUDD>::DdManager() : metaVariableMap(), cuddManager() {
-            this->cuddManager.SetMaxMemory(storm::settings::Settings::getInstance()->getOptionByLongName("cuddmaxmem").getArgument(0).getValueAsUnsignedInteger() * 1024);
+            this->cuddManager.SetMaxMemory(storm::settings::Settings::getInstance()->getOptionByLongName("cuddmaxmem").getArgument(0).getValueAsUnsignedInteger() * 1024 * 1024);
             this->cuddManager.SetEpsilon(storm::settings::Settings::getInstance()->getOptionByLongName("cuddprec").getArgument(0).getValueAsDouble());
         }
         
@@ -224,6 +224,10 @@ namespace storm {
         Cudd& DdManager<DdType::CUDD>::getCuddManager() {
             return this->cuddManager;
         }
+
+        Cudd const& DdManager<DdType::CUDD>::getCuddManager() const {
+            return this->cuddManager;
+        }
         
         std::vector<std::string> DdManager<DdType::CUDD>::getDdVariableNames() const {
             // First, we initialize a list DD variables and their names.
@@ -246,5 +250,22 @@ namespace storm {
             
             return result;
         }
+        
+        void DdManager<DdType::CUDD>::allowDynamicReordering(bool value) {
+            if (value) {
+                this->getCuddManager().AutodynEnable(CUDD_REORDER_GROUP_SIFT_CONV);
+            } else {
+                this->getCuddManager().AutodynDisable();
+            }
+        }
+        
+        bool DdManager<DdType::CUDD>::isDynamicReorderingAllowed() const {
+            Cudd_ReorderingType type;
+            return this->getCuddManager().ReorderingStatus(&type);
+        }
+        
+        void DdManager<DdType::CUDD>::triggerReordering() {
+            this->getCuddManager().ReduceHeap(CUDD_REORDER_GROUP_SIFT_CONV, 0);
+        }
     }
 }
\ No newline at end of file
diff --git a/src/storage/dd/CuddDdManager.h b/src/storage/dd/CuddDdManager.h
index 25844598c..6fba4d254 100644
--- a/src/storage/dd/CuddDdManager.h
+++ b/src/storage/dd/CuddDdManager.h
@@ -138,6 +138,25 @@ namespace storm {
              */
             bool hasMetaVariable(std::string const& metaVariableName) const;
             
+            /*!
+             * Sets whether or not dynamic reordering is allowed for the DDs managed by this manager.
+             *
+             * @param value If set to true, dynamic reordering is allowed and forbidden otherwise.
+             */
+            void allowDynamicReordering(bool value);
+            
+            /*!
+             * Retrieves whether dynamic reordering is currently allowed.
+             *
+             * @return True iff dynamic reordering is currently allowed.
+             */
+            bool isDynamicReorderingAllowed() const;
+            
+            /*!
+             * Triggers a reordering of the DDs managed by this manager.
+             */
+            void triggerReordering();
+            
         private:
             /*!
              * Retrieves a list of names of the DD variables in the order of their index.
@@ -153,6 +172,13 @@ namespace storm {
              */
             Cudd& getCuddManager();
             
+            /*!
+             * Retrieves the underlying CUDD manager.
+             *
+             * @return The underlying CUDD manager.
+             */
+            Cudd const& getCuddManager() const;
+            
             // A mapping from variable names to the meta variable information.
             std::unordered_map<std::string, DdMetaVariable<DdType::CUDD>> metaVariableMap;
             
diff --git a/src/storage/expressions/BaseExpression.cpp b/src/storage/expressions/BaseExpression.cpp
index 2aef5ab6e..38a0604e2 100644
--- a/src/storage/expressions/BaseExpression.cpp
+++ b/src/storage/expressions/BaseExpression.cpp
@@ -49,6 +49,10 @@ namespace storm {
             LOG_THROW(false, storm::exceptions::InvalidAccessException, "Unable to access identifier of non-constant, non-variable expression.");
         }
         
+        OperatorType BaseExpression::getOperator() const {
+            LOG_THROW(false, storm::exceptions::InvalidAccessException, "Unable to access operator of non-function application expression.");
+        }
+        
         bool BaseExpression::containsVariables() const {
             return false;
         }
@@ -69,18 +73,12 @@ namespace storm {
             return false;
         }
         
-        std::shared_ptr<BaseExpression const> BaseExpression::getSharedPointer() const {
-            return this->shared_from_this();
+        bool BaseExpression::isFunctionApplication() const {
+            return false;
         }
         
-        std::ostream& operator<<(std::ostream& stream, ExpressionReturnType const& enumValue) {
-            switch (enumValue) {
-                case ExpressionReturnType::Undefined: stream << "undefined"; break;
-                case ExpressionReturnType::Bool: stream << "bool"; break;
-                case ExpressionReturnType::Int: stream << "int"; break;
-                case ExpressionReturnType::Double: stream << "double"; break;
-            }
-            return stream;
+        std::shared_ptr<BaseExpression const> BaseExpression::getSharedPointer() const {
+            return this->shared_from_this();
         }
         
         std::ostream& operator<<(std::ostream& stream, BaseExpression const& expression) {
diff --git a/src/storage/expressions/BaseExpression.h b/src/storage/expressions/BaseExpression.h
index 5a9cdba75..8afe11b91 100644
--- a/src/storage/expressions/BaseExpression.h
+++ b/src/storage/expressions/BaseExpression.h
@@ -7,20 +7,15 @@
 #include <map>
 #include <iostream>
 
+#include "src/storage/expressions/ExpressionReturnType.h"
 #include "src/storage/expressions/Valuation.h"
 #include "src/storage/expressions/ExpressionVisitor.h"
+#include "src/storage/expressions/OperatorType.h"
 #include "src/exceptions/InvalidArgumentException.h"
 #include "src/utility/OsDetection.h"
 
 namespace storm {
-    namespace expressions {
-        /*!
-         * Each node in an expression tree has a uniquely defined type from this enum.
-         */
-        enum class ExpressionReturnType {Undefined, Bool, Int, Double};
-        
-        std::ostream& operator<<(std::ostream& stream, ExpressionReturnType const& enumValue);
-        
+    namespace expressions {        
         /*!
          * The base class of all expression classes.
          */
@@ -97,6 +92,14 @@ namespace storm {
              */
             virtual std::string const& getIdentifier() const;
             
+            /*!
+             * Retrieves the operator of a function application. This is only legal to call if the expression is
+             * function application.
+             *
+             * @return The operator associated with the function application.
+             */
+            virtual OperatorType getOperator() const;
+            
             /*!
              * Retrieves whether the expression contains a variable.
              *
@@ -132,6 +135,13 @@ namespace storm {
              */
             virtual bool isFalse() const;
             
+            /*!
+             * Checks if the expression is a function application (of any sort).
+             *
+             * @return True iff the expression is a function application.
+             */
+            virtual bool isFunctionApplication() const;
+            
             /*!
              * Retrieves the set of all variables that appear in the expression.
              *
diff --git a/src/storage/expressions/BinaryBooleanFunctionExpression.cpp b/src/storage/expressions/BinaryBooleanFunctionExpression.cpp
index e270293a8..061c231af 100644
--- a/src/storage/expressions/BinaryBooleanFunctionExpression.cpp
+++ b/src/storage/expressions/BinaryBooleanFunctionExpression.cpp
@@ -13,6 +13,16 @@ namespace storm {
             return this->operatorType;
         }
         
+        storm::expressions::OperatorType BinaryBooleanFunctionExpression::getOperator() const {
+            switch (this->getOperatorType()) {
+                case OperatorType::And: return storm::expressions::OperatorType::And; break;
+                case OperatorType::Or: return storm::expressions::OperatorType::Or; break;
+                case OperatorType::Xor: return storm::expressions::OperatorType::Xor; break;
+                case OperatorType::Implies: return storm::expressions::OperatorType::Implies; break;
+                case OperatorType::Iff: return storm::expressions::OperatorType::Iff; break;
+            }
+        }
+                
         bool BinaryBooleanFunctionExpression::evaluateAsBool(Valuation const* valuation) const {
             LOG_THROW(this->hasBooleanReturnType(), storm::exceptions::InvalidTypeException, "Unable to evaluate expression as boolean.");
             
diff --git a/src/storage/expressions/BinaryBooleanFunctionExpression.h b/src/storage/expressions/BinaryBooleanFunctionExpression.h
index 8b1bb7437..2a1d2417d 100644
--- a/src/storage/expressions/BinaryBooleanFunctionExpression.h
+++ b/src/storage/expressions/BinaryBooleanFunctionExpression.h
@@ -33,6 +33,7 @@ namespace storm {
             virtual ~BinaryBooleanFunctionExpression() = default;
             
             // Override base class methods.
+            virtual storm::expressions::OperatorType getOperator() const override;
             virtual bool evaluateAsBool(Valuation const* valuation = nullptr) const override;
             virtual std::shared_ptr<BaseExpression const> simplify() const override;
             virtual void accept(ExpressionVisitor* visitor) const override;
diff --git a/src/storage/expressions/BinaryExpression.cpp b/src/storage/expressions/BinaryExpression.cpp
index 09fb1cf70..bdb7fb59f 100644
--- a/src/storage/expressions/BinaryExpression.cpp
+++ b/src/storage/expressions/BinaryExpression.cpp
@@ -9,6 +9,10 @@ namespace storm {
             // Intentionally left empty.
         }
         
+        bool BinaryExpression::isFunctionApplication() const {
+            return true;
+        }
+        
         bool BinaryExpression::containsVariables() const {
             return this->getFirstOperand()->containsVariables() || this->getSecondOperand()->containsVariables();
 		}
@@ -41,7 +45,7 @@ namespace storm {
         
         std::shared_ptr<BaseExpression const> BinaryExpression::getOperand(uint_fast64_t operandIndex) const {
             LOG_THROW(operandIndex < 2, storm::exceptions::InvalidAccessException, "Unable to access operand " << operandIndex << " in expression of arity 2.");
-            if (operandIndex == 1) {
+            if (operandIndex == 0) {
                 return this->getFirstOperand();
             } else {
                 return this->getSecondOperand();
diff --git a/src/storage/expressions/BinaryExpression.h b/src/storage/expressions/BinaryExpression.h
index 9daf9505d..3f9be4d06 100644
--- a/src/storage/expressions/BinaryExpression.h
+++ b/src/storage/expressions/BinaryExpression.h
@@ -30,6 +30,7 @@ namespace storm {
             virtual ~BinaryExpression() = default;
 
             // Override base class methods.
+            virtual bool isFunctionApplication() const override;
             virtual bool containsVariables() const override;
             virtual uint_fast64_t getArity() const override;
 			virtual std::shared_ptr<BaseExpression const> getOperand(uint_fast64_t operandIndex) const override;
diff --git a/src/storage/expressions/BinaryNumericalFunctionExpression.cpp b/src/storage/expressions/BinaryNumericalFunctionExpression.cpp
index 225bea963..f6172821d 100644
--- a/src/storage/expressions/BinaryNumericalFunctionExpression.cpp
+++ b/src/storage/expressions/BinaryNumericalFunctionExpression.cpp
@@ -14,6 +14,17 @@ namespace storm {
             return this->operatorType;
         }
         
+        storm::expressions::OperatorType BinaryNumericalFunctionExpression::getOperator() const {
+            switch (this->getOperatorType()) {
+                case OperatorType::Plus: return storm::expressions::OperatorType::Plus; break;
+                case OperatorType::Minus: return storm::expressions::OperatorType::Minus; break;
+                case OperatorType::Times: return storm::expressions::OperatorType::Times; break;
+                case OperatorType::Divide: return storm::expressions::OperatorType::Divide; break;
+                case OperatorType::Min: return storm::expressions::OperatorType::Min; break;
+                case OperatorType::Max: return storm::expressions::OperatorType::Max; break;
+            }
+        }
+        
         int_fast64_t BinaryNumericalFunctionExpression::evaluateAsInt(Valuation const* valuation) const {
             LOG_THROW(this->hasIntegralReturnType(), storm::exceptions::InvalidTypeException, "Unable to evaluate expression as integer.");
             
diff --git a/src/storage/expressions/BinaryNumericalFunctionExpression.h b/src/storage/expressions/BinaryNumericalFunctionExpression.h
index 4e8573a4c..13ee489df 100644
--- a/src/storage/expressions/BinaryNumericalFunctionExpression.h
+++ b/src/storage/expressions/BinaryNumericalFunctionExpression.h
@@ -33,6 +33,7 @@ namespace storm {
             virtual ~BinaryNumericalFunctionExpression() = default;
             
             // Override base class methods.
+            virtual storm::expressions::OperatorType getOperator() const override;
             virtual int_fast64_t evaluateAsInt(Valuation const* valuation = nullptr) const override;
             virtual double evaluateAsDouble(Valuation const* valuation = nullptr) const override;
             virtual std::shared_ptr<BaseExpression const> simplify() const override;
diff --git a/src/storage/expressions/BinaryRelationExpression.cpp b/src/storage/expressions/BinaryRelationExpression.cpp
index 7e48c2286..f31f97fce 100644
--- a/src/storage/expressions/BinaryRelationExpression.cpp
+++ b/src/storage/expressions/BinaryRelationExpression.cpp
@@ -8,7 +8,18 @@ namespace storm {
         BinaryRelationExpression::BinaryRelationExpression(ExpressionReturnType returnType, std::shared_ptr<BaseExpression const> const& firstOperand, std::shared_ptr<BaseExpression const> const& secondOperand, RelationType relationType) : BinaryExpression(returnType, firstOperand, secondOperand), relationType(relationType) {
             // Intentionally left empty.
         }
-                
+        
+        storm::expressions::OperatorType BinaryRelationExpression::getOperator() const {
+            switch (this->getRelationType()) {
+                case RelationType::Equal: return storm::expressions::OperatorType::Equal; break;
+                case RelationType::NotEqual: return storm::expressions::OperatorType::NotEqual; break;
+                case RelationType::Less: return storm::expressions::OperatorType::Less; break;
+                case RelationType::LessOrEqual: return storm::expressions::OperatorType::LessOrEqual; break;
+                case RelationType::Greater: return storm::expressions::OperatorType::Greater; break;
+                case RelationType::GreaterOrEqual: return storm::expressions::OperatorType::GreaterOrEqual; break;
+            }
+        }
+        
         bool BinaryRelationExpression::evaluateAsBool(Valuation const* valuation) const {
             LOG_THROW(this->hasBooleanReturnType(), storm::exceptions::InvalidTypeException, "Unable to evaluate expression as boolean.");
 
diff --git a/src/storage/expressions/BinaryRelationExpression.h b/src/storage/expressions/BinaryRelationExpression.h
index b88ef71f9..4e03a598e 100644
--- a/src/storage/expressions/BinaryRelationExpression.h
+++ b/src/storage/expressions/BinaryRelationExpression.h
@@ -33,6 +33,7 @@ namespace storm {
             virtual ~BinaryRelationExpression() = default;
             
             // Override base class methods.
+            virtual storm::expressions::OperatorType getOperator() const override;
             virtual bool evaluateAsBool(Valuation const* valuation = nullptr) const override;
             virtual std::shared_ptr<BaseExpression const> simplify() const override;
             virtual void accept(ExpressionVisitor* visitor) const override;
diff --git a/src/storage/expressions/Expression.cpp b/src/storage/expressions/Expression.cpp
index ca2616b95..09810bb31 100644
--- a/src/storage/expressions/Expression.cpp
+++ b/src/storage/expressions/Expression.cpp
@@ -5,6 +5,7 @@
 #include "src/storage/expressions/SubstitutionVisitor.h"
 #include "src/storage/expressions/IdentifierSubstitutionVisitor.h"
 #include "src/storage/expressions/TypeCheckVisitor.h"
+#include "src/storage/expressions/LinearityCheckVisitor.h"
 #include "src/storage/expressions/Expressions.h"
 #include "src/exceptions/InvalidTypeException.h"
 #include "src/exceptions/ExceptionMacros.h"
@@ -16,27 +17,27 @@ namespace storm {
         }
         
 		Expression Expression::substitute(std::map<std::string, Expression> const& identifierToExpressionMap) const {
-            return SubstitutionVisitor<std::map<std::string, Expression>>(identifierToExpressionMap).substitute(this->getBaseExpressionPointer().get());
+            return SubstitutionVisitor<std::map<std::string, Expression>>(identifierToExpressionMap).substitute(*this);
         }
 
 		Expression Expression::substitute(std::unordered_map<std::string, Expression> const& identifierToExpressionMap) const {
-			return SubstitutionVisitor<std::unordered_map<std::string, Expression>>(identifierToExpressionMap).substitute(this->getBaseExpressionPointer().get());
+			return SubstitutionVisitor<std::unordered_map<std::string, Expression>>(identifierToExpressionMap).substitute(*this);
 		}
         
 		Expression Expression::substitute(std::map<std::string, std::string> const& identifierToIdentifierMap) const {
-			return IdentifierSubstitutionVisitor<std::map<std::string, std::string>>(identifierToIdentifierMap).substitute(this->getBaseExpressionPointer().get());
+			return IdentifierSubstitutionVisitor<std::map<std::string, std::string>>(identifierToIdentifierMap).substitute(*this);
         }
 
 		Expression Expression::substitute(std::unordered_map<std::string, std::string> const& identifierToIdentifierMap) const {
-			return IdentifierSubstitutionVisitor<std::unordered_map<std::string, std::string>>(identifierToIdentifierMap).substitute(this->getBaseExpressionPointer().get());
+			return IdentifierSubstitutionVisitor<std::unordered_map<std::string, std::string>>(identifierToIdentifierMap).substitute(*this);
 		}
         
         void Expression::check(std::map<std::string, storm::expressions::ExpressionReturnType> const& identifierToTypeMap) const {
-            return TypeCheckVisitor<std::map<std::string, storm::expressions::ExpressionReturnType>>(identifierToTypeMap).check(this->getBaseExpressionPointer().get());
+            return TypeCheckVisitor<std::map<std::string, storm::expressions::ExpressionReturnType>>(identifierToTypeMap).check(*this);
         }
 
         void Expression::check(std::unordered_map<std::string, storm::expressions::ExpressionReturnType> const& identifierToTypeMap) const {
-            return TypeCheckVisitor<std::unordered_map<std::string, storm::expressions::ExpressionReturnType>>(identifierToTypeMap).check(this->getBaseExpressionPointer().get());
+            return TypeCheckVisitor<std::unordered_map<std::string, storm::expressions::ExpressionReturnType>>(identifierToTypeMap).check(*this);
         }
 
         bool Expression::evaluateAsBool(Valuation const* valuation) const {
@@ -55,6 +56,14 @@ namespace storm {
             return Expression(this->getBaseExpression().simplify());
         }
         
+        OperatorType Expression::getOperator() const {
+            return this->getBaseExpression().getOperator();
+        }
+        
+        bool Expression::isFunctionApplication() const {
+            return this->getBaseExpression().isFunctionApplication();
+        }
+        
         uint_fast64_t Expression::getArity() const {
             return this->getBaseExpression().getArity();
         }
@@ -102,6 +111,24 @@ namespace storm {
 			}
 		}
         
+        bool Expression::isRelationalExpression() const {
+            if (!this->isFunctionApplication()) {
+                return false;
+            }
+            
+            return this->getOperator() == OperatorType::Equal || this->getOperator() == OperatorType::NotEqual
+            || this->getOperator() == OperatorType::Less || this->getOperator() == OperatorType::LessOrEqual
+            || this->getOperator() == OperatorType::Greater || this->getOperator() == OperatorType::GreaterOrEqual;
+        }
+        
+        bool Expression::isLinear() const {
+            return LinearityCheckVisitor().check(*this);
+        }
+        
+        std::set<std::string> Expression::getVariables() const {
+            return this->getBaseExpression().getVariables();
+        }
+        
         BaseExpression const& Expression::getBaseExpression() const {
             return *this->expressionPtr;
         }
diff --git a/src/storage/expressions/Expression.h b/src/storage/expressions/Expression.h
index 6ad063c91..504e3d96e 100644
--- a/src/storage/expressions/Expression.h
+++ b/src/storage/expressions/Expression.h
@@ -6,6 +6,7 @@
 #include <unordered_map>
 
 #include "src/storage/expressions/BaseExpression.h"
+#include "src/storage/expressions/ExpressionVisitor.h"
 #include "src/utility/OsDetection.h"
 
 namespace storm {
@@ -157,6 +158,21 @@ namespace storm {
              */
             Expression simplify();
             
+            /*!
+             * Retrieves the operator of a function application. This is only legal to call if the expression is
+             * function application.
+             *
+             * @return The operator associated with the function application.
+             */
+            OperatorType getOperator() const;
+            
+            /*!
+             * Checks if the expression is a function application (of any sort).
+             *
+             * @return True iff the expression is a function application.
+             */
+            bool isFunctionApplication() const;
+            
             /*!
              * Retrieves the arity of the expression.
              *
@@ -215,6 +231,21 @@ namespace storm {
              */
             bool isFalse() const;
             
+            /*!
+             * Retrieves whether this expression is a relation expression, i.e., an expression that has a relation
+             * (equal, not equal, less, less or equal, etc.) as its top-level operator.
+             *
+             * @return True iff the expression is a relation expression.
+             */
+            bool isRelationalExpression() const;
+            
+            /*!
+             * Retrieves whether this expression is a linear expression.
+             *
+             * @return True iff the expression is linear.
+             */
+            bool isLinear() const;
+            
             /*!
              * Retrieves the set of all variables that appear in the expression.
              *
@@ -271,6 +302,13 @@ namespace storm {
              */
             bool hasBooleanReturnType() const;
             
+            /*!
+             * Accepts the given visitor.
+             *
+             * @param visitor The visitor to accept.
+             */
+            void accept(ExpressionVisitor* visitor) const;
+            
             friend std::ostream& operator<<(std::ostream& stream, Expression const& expression);
 
         private:
diff --git a/src/storage/expressions/ExpressionReturnType.cpp b/src/storage/expressions/ExpressionReturnType.cpp
new file mode 100644
index 000000000..c10810f6c
--- /dev/null
+++ b/src/storage/expressions/ExpressionReturnType.cpp
@@ -0,0 +1,15 @@
+#include "src/storage/expressions/ExpressionReturnType.h"
+
+namespace storm {
+    namespace expressions {
+        std::ostream& operator<<(std::ostream& stream, ExpressionReturnType const& enumValue) {
+            switch (enumValue) {
+                case ExpressionReturnType::Undefined: stream << "undefined"; break;
+                case ExpressionReturnType::Bool: stream << "bool"; break;
+                case ExpressionReturnType::Int: stream << "int"; break;
+                case ExpressionReturnType::Double: stream << "double"; break;
+            }
+            return stream;
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/storage/expressions/ExpressionReturnType.h b/src/storage/expressions/ExpressionReturnType.h
new file mode 100644
index 000000000..0cf928ed8
--- /dev/null
+++ b/src/storage/expressions/ExpressionReturnType.h
@@ -0,0 +1,17 @@
+#ifndef STORM_STORAGE_EXPRESSIONS_EXPRESSIONRETURNTYPE_H_
+#define STORM_STORAGE_EXPRESSIONS_EXPRESSIONRETURNTYPE_H_
+
+#include <iostream>
+
+namespace storm {
+    namespace expressions {
+        /*!
+         * Each node in an expression tree has a uniquely defined type from this enum.
+         */
+        enum class ExpressionReturnType {Undefined, Bool, Int, Double};
+        
+        std::ostream& operator<<(std::ostream& stream, ExpressionReturnType const& enumValue);
+    }
+}
+
+#endif /* STORM_STORAGE_EXPRESSIONS_EXPRESSIONRETURNTYPE_H_ */
\ No newline at end of file
diff --git a/src/storage/expressions/IdentifierSubstitutionVisitor.cpp b/src/storage/expressions/IdentifierSubstitutionVisitor.cpp
index a2153af6d..19724dea1 100644
--- a/src/storage/expressions/IdentifierSubstitutionVisitor.cpp
+++ b/src/storage/expressions/IdentifierSubstitutionVisitor.cpp
@@ -13,8 +13,8 @@ namespace storm {
         }
         
 		template<typename MapType>
-        Expression IdentifierSubstitutionVisitor<MapType>::substitute(BaseExpression const* expression) {
-            expression->accept(this);
+        Expression IdentifierSubstitutionVisitor<MapType>::substitute(Expression const& expression) {
+            expression.getBaseExpression().accept(this);
             return Expression(this->expressionStack.top());
         }
         
diff --git a/src/storage/expressions/IdentifierSubstitutionVisitor.h b/src/storage/expressions/IdentifierSubstitutionVisitor.h
index 23dae79de..8e8723a70 100644
--- a/src/storage/expressions/IdentifierSubstitutionVisitor.h
+++ b/src/storage/expressions/IdentifierSubstitutionVisitor.h
@@ -26,7 +26,7 @@ namespace storm {
              * @return The expression in which all identifiers in the key set of the previously given mapping are
              * substituted with the mapped-to expressions.
              */
-            Expression substitute(BaseExpression const* expression);
+            Expression substitute(Expression const& expression);
             
             virtual void visit(IfThenElseExpression const* expression) override;
             virtual void visit(BinaryBooleanFunctionExpression const* expression) override;
diff --git a/src/storage/expressions/IfThenElseExpression.cpp b/src/storage/expressions/IfThenElseExpression.cpp
index 6b84cde84..50c71af75 100644
--- a/src/storage/expressions/IfThenElseExpression.cpp
+++ b/src/storage/expressions/IfThenElseExpression.cpp
@@ -1,11 +1,41 @@
 #include "src/storage/expressions/IfThenElseExpression.h"
 
+#include "src/exceptions/ExceptionMacros.h"
+#include "src/exceptions/InvalidAccessException.h"
+
 namespace storm {
     namespace expressions {
         IfThenElseExpression::IfThenElseExpression(ExpressionReturnType returnType, std::shared_ptr<BaseExpression const> const& condition, std::shared_ptr<BaseExpression const> const& thenExpression, std::shared_ptr<BaseExpression const> const& elseExpression) : BaseExpression(returnType), condition(condition), thenExpression(thenExpression), elseExpression(elseExpression) {
             // Intentionally left empty.
         }
         
+        std::shared_ptr<BaseExpression const> IfThenElseExpression::getOperand(uint_fast64_t operandIndex) const {
+            LOG_THROW(operandIndex < 3, storm::exceptions::InvalidAccessException, "Unable to access operand " << operandIndex << " in expression of arity 3.");
+            if (operandIndex == 0) {
+                return this->getCondition();
+            } else if (operandIndex == 1) {
+                return this->getThenExpression();
+            } else {
+                return this->getElseExpression();
+            }
+        }
+        
+        OperatorType IfThenElseExpression::getOperator() const {
+            return OperatorType::Ite;
+        }
+        
+        bool IfThenElseExpression::isFunctionApplication() const {
+            return true;
+        }
+        
+        bool IfThenElseExpression::containsVariables() const {
+            return this->getCondition()->containsVariables() || this->getThenExpression()->containsVariables() || this->getElseExpression()->containsVariables();
+        }
+        
+        uint_fast64_t IfThenElseExpression::getArity() const {
+            return 3;
+        }
+        
         bool IfThenElseExpression::evaluateAsBool(Valuation const* valuation) const {
             bool conditionValue = this->condition->evaluateAsBool(valuation);
             if (conditionValue) {
diff --git a/src/storage/expressions/IfThenElseExpression.h b/src/storage/expressions/IfThenElseExpression.h
index 0e59c097a..63619d852 100644
--- a/src/storage/expressions/IfThenElseExpression.h
+++ b/src/storage/expressions/IfThenElseExpression.h
@@ -27,6 +27,11 @@ namespace storm {
             virtual ~IfThenElseExpression() = default;
             
             // Override base class methods.
+            virtual std::shared_ptr<BaseExpression const> getOperand(uint_fast64_t operandIndex) const override;
+            virtual OperatorType getOperator() const override;
+            virtual bool isFunctionApplication() const override;
+            virtual bool containsVariables() const override;
+            virtual uint_fast64_t getArity() const override;
             virtual bool evaluateAsBool(Valuation const* valuation = nullptr) const override;
             virtual int_fast64_t evaluateAsInt(Valuation const* valuation = nullptr) const override;
             virtual double evaluateAsDouble(Valuation const* valuation = nullptr) const override;
diff --git a/src/storage/expressions/LinearCoefficientVisitor.cpp b/src/storage/expressions/LinearCoefficientVisitor.cpp
new file mode 100644
index 000000000..7525d6dde
--- /dev/null
+++ b/src/storage/expressions/LinearCoefficientVisitor.cpp
@@ -0,0 +1,151 @@
+#include "src/storage/expressions/LinearCoefficientVisitor.h"
+
+#include "src/storage/expressions/Expressions.h"
+#include "src/exceptions/ExceptionMacros.h"
+#include "src/exceptions/InvalidArgumentException.h"
+
+namespace storm {
+    namespace expressions {
+        std::pair<SimpleValuation, double> LinearCoefficientVisitor::getLinearCoefficients(Expression const& expression) {
+            expression.getBaseExpression().accept(this);
+            return resultStack.top();
+        }
+        
+        void LinearCoefficientVisitor::visit(IfThenElseExpression const* expression) {
+            LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Expression is non-linear.");
+        }
+        
+        void LinearCoefficientVisitor::visit(BinaryBooleanFunctionExpression const* expression) {
+            LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Expression is non-linear.");
+        }
+        
+        void LinearCoefficientVisitor::visit(BinaryNumericalFunctionExpression const* expression) {
+            if (expression->getOperatorType() == BinaryNumericalFunctionExpression::OperatorType::Plus) {
+                expression->getFirstOperand()->accept(this);
+                std::pair<SimpleValuation, double> leftResult = resultStack.top();
+                resultStack.pop();
+                expression->getSecondOperand()->accept(this);
+                std::pair<SimpleValuation, double>& rightResult = resultStack.top();
+                
+                // Now add the left result to the right result.
+                for (auto const& identifier : leftResult.first.getDoubleIdentifiers()) {
+                    if (rightResult.first.containsDoubleIdentifier(identifier)) {
+                        rightResult.first.setDoubleValue(identifier, leftResult.first.getDoubleValue(identifier) + rightResult.first.getDoubleValue(identifier));
+                    } else {
+                        rightResult.first.setDoubleValue(identifier, leftResult.first.getDoubleValue(identifier));
+                    }
+                }
+                rightResult.second += leftResult.second;
+                return;
+            } else if (expression->getOperatorType() == BinaryNumericalFunctionExpression::OperatorType::Minus) {
+                expression->getFirstOperand()->accept(this);
+                std::pair<SimpleValuation, double> leftResult = resultStack.top();
+                resultStack.pop();
+                expression->getSecondOperand()->accept(this);
+                std::pair<SimpleValuation, double>& rightResult = resultStack.top();
+                
+                // Now subtract the right result from the left result.
+                for (auto const& identifier : leftResult.first.getDoubleIdentifiers()) {
+                    if (rightResult.first.containsDoubleIdentifier(identifier)) {
+                        rightResult.first.setDoubleValue(identifier, leftResult.first.getDoubleValue(identifier) - rightResult.first.getDoubleValue(identifier));
+                    } else {
+                        rightResult.first.setDoubleValue(identifier, leftResult.first.getDoubleValue(identifier));
+                    }
+                }
+                for (auto const& identifier : rightResult.first.getDoubleIdentifiers()) {
+                    if (!leftResult.first.containsDoubleIdentifier(identifier)) {
+                        rightResult.first.setDoubleValue(identifier, -rightResult.first.getDoubleValue(identifier));
+                    }
+                }
+                rightResult.second = leftResult.second - rightResult.second;
+                return;
+            } else if (expression->getOperatorType() == BinaryNumericalFunctionExpression::OperatorType::Times) {
+                expression->getFirstOperand()->accept(this);
+                std::pair<SimpleValuation, double> leftResult = resultStack.top();
+                resultStack.pop();
+                expression->getSecondOperand()->accept(this);
+                std::pair<SimpleValuation, double>& rightResult = resultStack.top();
+                
+                // If the expression is linear, either the left or the right side must not contain variables.
+                LOG_THROW(leftResult.first.getNumberOfIdentifiers() == 0 || rightResult.first.getNumberOfIdentifiers() == 0, storm::exceptions::InvalidArgumentException, "Expression is non-linear.");
+                if (leftResult.first.getNumberOfIdentifiers() == 0) {
+                    for (auto const& identifier : rightResult.first.getDoubleIdentifiers()) {
+                        rightResult.first.setDoubleValue(identifier, leftResult.second * rightResult.first.getDoubleValue(identifier));
+                    }
+                } else {
+                    for (auto const& identifier : leftResult.first.getDoubleIdentifiers()) {
+                        rightResult.first.addDoubleIdentifier(identifier, rightResult.second * leftResult.first.getDoubleValue(identifier));
+                    }
+                }
+                rightResult.second *= leftResult.second;
+                return;
+            } else if (expression->getOperatorType() == BinaryNumericalFunctionExpression::OperatorType::Divide) {
+                expression->getFirstOperand()->accept(this);
+                std::pair<SimpleValuation, double> leftResult = resultStack.top();
+                resultStack.pop();
+                expression->getSecondOperand()->accept(this);
+                std::pair<SimpleValuation, double>& rightResult = resultStack.top();
+                
+                // If the expression is linear, either the left or the right side must not contain variables.
+                LOG_THROW(leftResult.first.getNumberOfIdentifiers() == 0 || rightResult.first.getNumberOfIdentifiers() == 0, storm::exceptions::InvalidArgumentException, "Expression is non-linear.");
+                if (leftResult.first.getNumberOfIdentifiers() == 0) {
+                    for (auto const& identifier : rightResult.first.getDoubleIdentifiers()) {
+                        rightResult.first.setDoubleValue(identifier, leftResult.second / rightResult.first.getDoubleValue(identifier));
+                    }
+                } else {
+                    for (auto const& identifier : leftResult.first.getDoubleIdentifiers()) {
+                        rightResult.first.addDoubleIdentifier(identifier, leftResult.first.getDoubleValue(identifier) / rightResult.second);
+                    }
+                }
+                rightResult.second = leftResult.second / leftResult.second;
+                return;
+            } else {
+                LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Expression is non-linear.");
+            }
+        }
+        
+        void LinearCoefficientVisitor::visit(BinaryRelationExpression const* expression) {
+            LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Expression is non-linear.");
+        }
+        
+        void LinearCoefficientVisitor::visit(VariableExpression const* expression) {
+            SimpleValuation valuation;
+            switch (expression->getReturnType()) {
+                case ExpressionReturnType::Bool: LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Expression is non-linear."); break;
+                case ExpressionReturnType::Int:
+                case ExpressionReturnType::Double: valuation.addDoubleIdentifier(expression->getVariableName(), 1); break;
+                case ExpressionReturnType::Undefined: LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Illegal expression return type."); break;
+            }
+            
+            resultStack.push(std::make_pair(valuation, 0));
+        }
+        
+        void LinearCoefficientVisitor::visit(UnaryBooleanFunctionExpression const* expression) {
+            LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Expression is non-linear.");
+        }
+        
+        void LinearCoefficientVisitor::visit(UnaryNumericalFunctionExpression const* expression) {
+            if (expression->getOperatorType() == UnaryNumericalFunctionExpression::OperatorType::Minus) {
+                // Here, we need to negate all double identifiers.
+                std::pair<SimpleValuation, double>& valuationConstantPair = resultStack.top();
+                for (auto const& identifier : valuationConstantPair.first.getDoubleIdentifiers()) {
+                    valuationConstantPair.first.setDoubleValue(identifier, -valuationConstantPair.first.getDoubleValue(identifier));
+                }
+            } else {
+                LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Expression is non-linear.");
+            }
+        }
+        
+        void LinearCoefficientVisitor::visit(BooleanLiteralExpression const* expression) {
+            LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Expression is non-linear.");
+        }
+        
+        void LinearCoefficientVisitor::visit(IntegerLiteralExpression const* expression) {
+            resultStack.push(std::make_pair(SimpleValuation(), static_cast<double>(expression->getValue())));
+        }
+        
+        void LinearCoefficientVisitor::visit(DoubleLiteralExpression const* expression) {
+            resultStack.push(std::make_pair(SimpleValuation(), expression->getValue()));
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/storage/expressions/LinearCoefficientVisitor.h b/src/storage/expressions/LinearCoefficientVisitor.h
new file mode 100644
index 000000000..263e752c8
--- /dev/null
+++ b/src/storage/expressions/LinearCoefficientVisitor.h
@@ -0,0 +1,46 @@
+#ifndef STORM_STORAGE_EXPRESSIONS_LINEARCOEFFICIENTVISITOR_H_
+#define STORM_STORAGE_EXPRESSIONS_LINEARCOEFFICIENTVISITOR_H_
+
+#include <stack>
+
+#include "src/storage/expressions/Expression.h"
+#include "src/storage/expressions/ExpressionVisitor.h"
+#include "src/storage/expressions/SimpleValuation.h"
+
+namespace storm {
+    namespace expressions {
+        class LinearCoefficientVisitor : public ExpressionVisitor {
+        public:
+            /*!
+             * Creates a linear coefficient visitor.
+             */
+            LinearCoefficientVisitor() = default;
+            
+            /*!
+             * Computes the (double) coefficients of all identifiers appearing in the expression if the expression
+             * was rewritten as a sum of atoms.. If the expression is not linear, an exception is thrown.
+             *
+             * @param expression The expression for which to compute the coefficients.
+             * @return A pair consisting of a mapping from identifiers to their coefficients and the coefficient of
+             * the constant atom.
+             */
+            std::pair<SimpleValuation, double> getLinearCoefficients(Expression const& expression);
+            
+            virtual void visit(IfThenElseExpression const* expression) override;
+            virtual void visit(BinaryBooleanFunctionExpression const* expression) override;
+            virtual void visit(BinaryNumericalFunctionExpression const* expression) override;
+            virtual void visit(BinaryRelationExpression const* expression) override;
+            virtual void visit(VariableExpression const* expression) override;
+            virtual void visit(UnaryBooleanFunctionExpression const* expression) override;
+            virtual void visit(UnaryNumericalFunctionExpression const* expression) override;
+            virtual void visit(BooleanLiteralExpression const* expression) override;
+            virtual void visit(IntegerLiteralExpression const* expression) override;
+            virtual void visit(DoubleLiteralExpression const* expression) override;
+            
+        private:
+            std::stack<std::pair<SimpleValuation, double>> resultStack;
+        };
+    }
+}
+
+#endif /* STORM_STORAGE_EXPRESSIONS_LINEARCOEFFICIENTVISITOR_H_ */
\ No newline at end of file
diff --git a/src/storage/expressions/LinearityCheckVisitor.cpp b/src/storage/expressions/LinearityCheckVisitor.cpp
new file mode 100644
index 000000000..9b382ed22
--- /dev/null
+++ b/src/storage/expressions/LinearityCheckVisitor.cpp
@@ -0,0 +1,112 @@
+#include "src/storage/expressions/LinearityCheckVisitor.h"
+#include "src/storage/expressions/Expressions.h"
+
+#include "src/exceptions/ExceptionMacros.h"
+#include "src/exceptions/InvalidTypeException.h"
+
+namespace storm {
+    namespace expressions {
+        LinearityCheckVisitor::LinearityCheckVisitor() : resultStack() {
+            // Intentionally left empty.
+        }
+        
+        bool LinearityCheckVisitor::check(Expression const& expression) {
+            expression.getBaseExpression().accept(this);
+            return resultStack.top() == LinearityStatus::LinearWithoutVariables || resultStack.top() == LinearityStatus::LinearContainsVariables;
+        }
+        
+        void LinearityCheckVisitor::visit(IfThenElseExpression const* expression) {
+            // An if-then-else expression is never linear.
+            resultStack.push(LinearityStatus::NonLinear);
+        }
+        
+        void LinearityCheckVisitor::visit(BinaryBooleanFunctionExpression const* expression) {
+            // Boolean function applications are not allowed in linear expressions.
+            resultStack.push(LinearityStatus::NonLinear);
+        }
+        
+        void LinearityCheckVisitor::visit(BinaryNumericalFunctionExpression const* expression) {
+            LinearityStatus leftResult;
+            LinearityStatus rightResult;
+            switch (expression->getOperatorType()) {
+                case BinaryNumericalFunctionExpression::OperatorType::Plus:
+                case BinaryNumericalFunctionExpression::OperatorType::Minus:
+                    expression->getFirstOperand()->accept(this);
+                    leftResult = resultStack.top();
+                    
+                    if (leftResult == LinearityStatus::NonLinear) {
+                        return;
+                    } else {
+                        resultStack.pop();
+                        expression->getSecondOperand()->accept(this);
+                        rightResult = resultStack.top();
+                        if (rightResult == LinearityStatus::NonLinear) {
+                            return;
+                        }
+                        resultStack.pop();
+                    }
+                                        
+                    resultStack.push(leftResult == LinearityStatus::LinearContainsVariables || rightResult == LinearityStatus::LinearContainsVariables ? LinearityStatus::LinearContainsVariables : LinearityStatus::LinearWithoutVariables);
+                    break;
+                case BinaryNumericalFunctionExpression::OperatorType::Times:
+                case BinaryNumericalFunctionExpression::OperatorType::Divide:
+                    expression->getFirstOperand()->accept(this);
+                    leftResult = resultStack.top();
+                    
+                    if (leftResult == LinearityStatus::NonLinear) {
+                        return;
+                    } else {
+                        resultStack.pop();
+                        expression->getSecondOperand()->accept(this);
+                        rightResult = resultStack.top();
+                        if (rightResult == LinearityStatus::NonLinear) {
+                            return;
+                        }
+                        resultStack.pop();
+                    }
+                    
+                    if (leftResult == LinearityStatus::LinearContainsVariables && rightResult == LinearityStatus::LinearContainsVariables) {
+                        resultStack.push(LinearityStatus::NonLinear);
+                    }
+                    
+                    resultStack.push(leftResult == LinearityStatus::LinearContainsVariables || rightResult == LinearityStatus::LinearContainsVariables ? LinearityStatus::LinearContainsVariables : LinearityStatus::LinearWithoutVariables);
+                    break;
+                case BinaryNumericalFunctionExpression::OperatorType::Min: resultStack.push(LinearityStatus::NonLinear); break;
+                case BinaryNumericalFunctionExpression::OperatorType::Max: resultStack.push(LinearityStatus::NonLinear); break;
+            }
+        }
+        
+        void LinearityCheckVisitor::visit(BinaryRelationExpression const* expression) {
+            resultStack.push(LinearityStatus::NonLinear);
+        }
+        
+        void LinearityCheckVisitor::visit(VariableExpression const* expression) {
+            resultStack.push(LinearityStatus::LinearContainsVariables);
+        }
+        
+        void LinearityCheckVisitor::visit(UnaryBooleanFunctionExpression const* expression) {
+            // Boolean function applications are not allowed in linear expressions.
+            resultStack.push(LinearityStatus::NonLinear);
+        }
+        
+        void LinearityCheckVisitor::visit(UnaryNumericalFunctionExpression const* expression) {
+            switch (expression->getOperatorType()) {
+                case UnaryNumericalFunctionExpression::OperatorType::Minus: break;
+                case UnaryNumericalFunctionExpression::OperatorType::Floor:
+                case UnaryNumericalFunctionExpression::OperatorType::Ceil: resultStack.pop(); resultStack.push(LinearityStatus::NonLinear); break;
+            }
+        }
+        
+        void LinearityCheckVisitor::visit(BooleanLiteralExpression const* expression) {
+            resultStack.push(LinearityStatus::NonLinear);
+        }
+        
+        void LinearityCheckVisitor::visit(IntegerLiteralExpression const* expression) {
+            resultStack.push(LinearityStatus::LinearWithoutVariables);
+        }
+        
+        void LinearityCheckVisitor::visit(DoubleLiteralExpression const* expression) {
+            resultStack.push(LinearityStatus::LinearWithoutVariables);
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/storage/expressions/LinearityCheckVisitor.h b/src/storage/expressions/LinearityCheckVisitor.h
new file mode 100644
index 000000000..d76b658c8
--- /dev/null
+++ b/src/storage/expressions/LinearityCheckVisitor.h
@@ -0,0 +1,45 @@
+#ifndef STORM_STORAGE_EXPRESSIONS_LINEARITYCHECKVISITOR_H_
+#define STORM_STORAGE_EXPRESSIONS_LINEARITYCHECKVISITOR_H_
+
+#include <stack>
+
+#include "src/storage/expressions/Expression.h"
+#include "src/storage/expressions/ExpressionVisitor.h"
+
+namespace storm {
+    namespace expressions {
+        class LinearityCheckVisitor : public ExpressionVisitor {
+        public:
+            /*!
+             * Creates a linearity check visitor.
+             */
+            LinearityCheckVisitor();
+            
+            /*!
+             * Checks that the given expression is linear.
+             *
+             * @param expression The expression to check for linearity.
+             */
+            bool check(Expression const& expression);
+            
+            virtual void visit(IfThenElseExpression const* expression) override;
+            virtual void visit(BinaryBooleanFunctionExpression const* expression) override;
+            virtual void visit(BinaryNumericalFunctionExpression const* expression) override;
+            virtual void visit(BinaryRelationExpression const* expression) override;
+            virtual void visit(VariableExpression const* expression) override;
+            virtual void visit(UnaryBooleanFunctionExpression const* expression) override;
+            virtual void visit(UnaryNumericalFunctionExpression const* expression) override;
+            virtual void visit(BooleanLiteralExpression const* expression) override;
+            virtual void visit(IntegerLiteralExpression const* expression) override;
+            virtual void visit(DoubleLiteralExpression const* expression) override;
+            
+        private:
+            enum class LinearityStatus { NonLinear, LinearContainsVariables, LinearWithoutVariables };
+            
+            // A stack for communicating the results of the subexpressions.
+            std::stack<LinearityStatus> resultStack;
+        };
+    }
+}
+
+#endif /* STORM_STORAGE_EXPRESSIONS_LINEARITYCHECKVISITOR_H_ */
\ No newline at end of file
diff --git a/src/storage/expressions/OperatorType.h b/src/storage/expressions/OperatorType.h
new file mode 100644
index 000000000..8a4f199aa
--- /dev/null
+++ b/src/storage/expressions/OperatorType.h
@@ -0,0 +1,33 @@
+#ifndef STORM_STORAGE_EXPRESSIONS_OPERATORTYPE_H_
+#define STORM_STORAGE_EXPRESSIONS_OPERATORTYPE_H_
+
+namespace storm {
+    namespace expressions {
+        // An enum representing all possible operator types.
+        enum class OperatorType {
+            And,
+            Or,
+            Xor,
+            Implies,
+            Iff,
+            Plus,
+            Minus,
+            Times,
+            Divide,
+            Min,
+            Max,
+            Equal,
+            NotEqual,
+            Less,
+            LessOrEqual,
+            Greater,
+            GreaterOrEqual,
+            Not,
+            Floor,
+            Ceil,
+            Ite
+        };
+    }
+}
+
+#endif /* STORM_STORAGE_EXPRESSIONS_OPERATORTYPE_H_ */
\ No newline at end of file
diff --git a/src/storage/expressions/SimpleValuation.cpp b/src/storage/expressions/SimpleValuation.cpp
index d29e6a7c8..923bbe84b 100644
--- a/src/storage/expressions/SimpleValuation.cpp
+++ b/src/storage/expressions/SimpleValuation.cpp
@@ -1,103 +1,167 @@
-#include <boost/functional/hash.hpp>
 #include "src/storage/expressions/SimpleValuation.h"
+
+#include <set>
+
+#include <boost/functional/hash.hpp>
 #include "src/exceptions/ExceptionMacros.h"
 #include "src/exceptions/InvalidArgumentException.h"
+#include "src/exceptions/InvalidAccessException.h"
 
 namespace storm {
     namespace expressions {
-        SimpleValuation::SimpleValuation() : booleanIdentifierToIndexMap(new std::unordered_map<std::string, uint_fast64_t>()), integerIdentifierToIndexMap(new std::unordered_map<std::string, uint_fast64_t>()), doubleIdentifierToIndexMap(new std::unordered_map<std::string, uint_fast64_t>()), booleanValues(), integerValues(), doubleValues() {
-            // Intentionally left empty.
-        }
-        
         bool SimpleValuation::operator==(SimpleValuation const& other) const {
-            return this->booleanIdentifierToIndexMap.get() == other.booleanIdentifierToIndexMap.get() && this->integerIdentifierToIndexMap.get() == other.integerIdentifierToIndexMap.get() && this->doubleIdentifierToIndexMap.get() == other.doubleIdentifierToIndexMap.get() && this->booleanValues == other.booleanValues && this->integerValues == other.integerValues && this->doubleValues == other.doubleValues;
+            return this->identifierToValueMap == other.identifierToValueMap;
         }
         
         void SimpleValuation::addBooleanIdentifier(std::string const& name, bool initialValue) {
-            LOG_THROW(this->booleanIdentifierToIndexMap->find(name) == this->booleanIdentifierToIndexMap->end(), storm::exceptions::InvalidArgumentException, "Boolean identifier '" << name << "' already registered.");
-            
-            this->booleanIdentifierToIndexMap->emplace(name, this->booleanValues.size());
-            this->booleanValues.push_back(initialValue);
+            LOG_THROW(this->identifierToValueMap.find(name) == this->identifierToValueMap.end(), storm::exceptions::InvalidArgumentException, "Identifier '" << name << "' already registered.");
+            this->identifierToValueMap.emplace(name, initialValue);
         }
         
         void SimpleValuation::addIntegerIdentifier(std::string const& name, int_fast64_t initialValue) {
-            LOG_THROW(this->booleanIdentifierToIndexMap->find(name) == this->booleanIdentifierToIndexMap->end(), storm::exceptions::InvalidArgumentException, "Integer identifier '" << name << "' already registered.");
-
-            this->integerIdentifierToIndexMap->emplace(name, this->integerValues.size());
-            this->integerValues.push_back(initialValue);
+            LOG_THROW(this->identifierToValueMap.find(name) == this->identifierToValueMap.end(), storm::exceptions::InvalidArgumentException, "Identifier '" << name << "' already registered.");
+            this->identifierToValueMap.emplace(name, initialValue);
         }
         
         void SimpleValuation::addDoubleIdentifier(std::string const& name, double initialValue) {
-            LOG_THROW(this->booleanIdentifierToIndexMap->find(name) == this->booleanIdentifierToIndexMap->end(), storm::exceptions::InvalidArgumentException, "Double identifier '" << name << "' already registered.");
-
-            this->doubleIdentifierToIndexMap->emplace(name, this->doubleValues.size());
-            this->doubleValues.push_back(initialValue);
+            LOG_THROW(this->identifierToValueMap.find(name) == this->identifierToValueMap.end(), storm::exceptions::InvalidArgumentException, "Identifier '" << name << "' already registered.");
+            this->identifierToValueMap.emplace(name, initialValue);
         }
         
         void SimpleValuation::setBooleanValue(std::string const& name, bool value) {
-            this->booleanValues[this->booleanIdentifierToIndexMap->at(name)] = value;
+            this->identifierToValueMap[name] = value;
         }
         
         void SimpleValuation::setIntegerValue(std::string const& name, int_fast64_t value) {
-            this->integerValues[this->integerIdentifierToIndexMap->at(name)] = value;
+            this->identifierToValueMap[name] = value;
         }
         
         void SimpleValuation::setDoubleValue(std::string const& name, double value) {
-            this->doubleValues[this->doubleIdentifierToIndexMap->at(name)] = value;
+            this->identifierToValueMap[name] = value;
+        }
+        
+        void SimpleValuation::removeIdentifier(std::string const& name) {
+            auto nameValuePair = this->identifierToValueMap.find(name);
+            LOG_THROW(nameValuePair != this->identifierToValueMap.end(), storm::exceptions::InvalidArgumentException, "Deleting unknown identifier '" << name << "'.");
+            this->identifierToValueMap.erase(nameValuePair);
+        }
+        
+        ExpressionReturnType SimpleValuation::getIdentifierType(std::string const& name) const {
+            auto nameValuePair = this->identifierToValueMap.find(name);
+            LOG_THROW(nameValuePair != this->identifierToValueMap.end(), storm::exceptions::InvalidAccessException, "Access to unkown identifier '" << name << "'.");
+            if (nameValuePair->second.type() == typeid(bool)) {
+                return ExpressionReturnType::Bool;
+            } else if (nameValuePair->second.type() == typeid(int_fast64_t)) {
+                return ExpressionReturnType::Int;
+            } else {
+                return ExpressionReturnType::Double;
+            }
+        }
+        
+        bool SimpleValuation::containsBooleanIdentifier(std::string const& name) const {
+            auto nameValuePair = this->identifierToValueMap.find(name);
+            if (nameValuePair == this->identifierToValueMap.end()) {
+                return false;
+            }
+            return nameValuePair->second.type() == typeid(bool);
+        }
+        
+        bool SimpleValuation::containsIntegerIdentifier(std::string const& name) const {
+            auto nameValuePair = this->identifierToValueMap.find(name);
+            if (nameValuePair == this->identifierToValueMap.end()) {
+                return false;
+            }
+            return nameValuePair->second.type() == typeid(int_fast64_t);
+        }
+        
+        bool SimpleValuation::containsDoubleIdentifier(std::string const& name) const {
+            auto nameValuePair = this->identifierToValueMap.find(name);
+            if (nameValuePair == this->identifierToValueMap.end()) {
+                return false;
+            }
+            return nameValuePair->second.type() == typeid(double);
         }
         
         bool SimpleValuation::getBooleanValue(std::string const& name) const {
-            auto const& nameIndexPair = this->booleanIdentifierToIndexMap->find(name);
-            return this->booleanValues[nameIndexPair->second];
+            auto nameValuePair = this->identifierToValueMap.find(name);
+            LOG_THROW(nameValuePair != this->identifierToValueMap.end(), storm::exceptions::InvalidAccessException, "Access to unkown identifier '" << name << "'.");
+            return boost::get<bool>(nameValuePair->second);
         }
         
         int_fast64_t SimpleValuation::getIntegerValue(std::string const& name) const {
-            auto const& nameIndexPair = this->integerIdentifierToIndexMap->find(name);
-            return this->integerValues[nameIndexPair->second];
+            auto nameValuePair = this->identifierToValueMap.find(name);
+            LOG_THROW(nameValuePair != this->identifierToValueMap.end(), storm::exceptions::InvalidAccessException, "Access to unkown identifier '" << name << "'.");
+            return boost::get<int_fast64_t>(nameValuePair->second);
         }
         
         double SimpleValuation::getDoubleValue(std::string const& name) const {
-            auto const& nameIndexPair = this->doubleIdentifierToIndexMap->find(name);
-            return this->doubleValues[nameIndexPair->second];
+            auto nameValuePair = this->identifierToValueMap.find(name);
+            LOG_THROW(nameValuePair != this->identifierToValueMap.end(), storm::exceptions::InvalidAccessException, "Access to unkown identifier '" << name << "'.");
+            return boost::get<double>(nameValuePair->second);
         }
         
-        std::ostream& operator<<(std::ostream& stream, SimpleValuation const& valuation) {
-            stream << "valuation { bool [";
-            if (!valuation.booleanValues.empty()) {
-                for (uint_fast64_t i = 0; i < valuation.booleanValues.size() - 1; ++i) {
-                    stream << valuation.booleanValues[i] << ", ";
+        std::size_t SimpleValuation::getNumberOfIdentifiers() const {
+            return this->identifierToValueMap.size();
+        }
+        
+        std::set<std::string> SimpleValuation::getIdentifiers() const {
+            std::set<std::string> result;
+            for (auto const& nameValuePair : this->identifierToValueMap) {
+                result.insert(nameValuePair.first);
+            }
+            return result;
+        }
+        
+        std::set<std::string> SimpleValuation::getBooleanIdentifiers() const {
+            std::set<std::string> result;
+            for (auto const& nameValuePair : this->identifierToValueMap) {
+                if (nameValuePair.second.type() == typeid(bool)) {
+                    result.insert(nameValuePair.first);
+                }
+            }
+            return result;
+        }
+        
+        std::set<std::string> SimpleValuation::getIntegerIdentifiers() const {
+            std::set<std::string> result;
+            for (auto const& nameValuePair : this->identifierToValueMap) {
+                if (nameValuePair.second.type() == typeid(int_fast64_t)) {
+                    result.insert(nameValuePair.first);
                 }
-                stream << valuation.booleanValues.back();
             }
-            stream << "] int [";
-            if (!valuation.integerValues.empty()) {
-                for (uint_fast64_t i = 0; i < valuation.integerValues.size() - 1; ++i) {
-                    stream << valuation.integerValues[i] << ", ";
+            return result;
+        }
+        
+        std::set<std::string> SimpleValuation::getDoubleIdentifiers() const {
+            std::set<std::string> result;
+            for (auto const& nameValuePair : this->identifierToValueMap) {
+                if (nameValuePair.second.type() == typeid(double)) {
+                    result.insert(nameValuePair.first);
                 }
-                stream << valuation.integerValues.back();
             }
-            stream << "] double [";
-            if (!valuation.doubleValues.empty()) {
-                for (uint_fast64_t i = 0; i < valuation.doubleValues.size() - 1; ++i) {
-                    stream << valuation.doubleValues[i] << ", ";
+            return result;
+        }
+        
+        std::ostream& operator<<(std::ostream& stream, SimpleValuation const& valuation) {
+            stream << "{ ";
+            uint_fast64_t elementIndex = 0;
+            for (auto const& nameValuePair : valuation.identifierToValueMap) {
+                stream << nameValuePair.first << " -> " << nameValuePair.second << " ";
+                ++elementIndex;
+                if (elementIndex < valuation.identifierToValueMap.size()) {
+                    stream << ", ";
                 }
-                stream << valuation.doubleValues.back();
             }
-            stream << "] }";
+            stream << "}";
             
             return stream;
         }
         
         std::size_t SimpleValuationPointerHash::operator()(SimpleValuation* valuation) const {
             size_t seed = 0;
-            for (auto const& value : valuation->booleanValues) {
-                boost::hash_combine<bool>(seed, value);
-            }
-            for (auto const& value : valuation->integerValues) {
-                boost::hash_combine<int_fast64_t>(seed, value);
-            }
-            for (auto const& value : valuation->doubleValues) {
-                boost::hash_combine<double>(seed, value);
+            for (auto const& nameValuePair : valuation->identifierToValueMap) {
+                boost::hash_combine(seed, nameValuePair.first);
+                boost::hash_combine(seed, nameValuePair.second);
             }
             return seed;
         }
@@ -107,21 +171,7 @@ namespace storm {
         }
         
         bool SimpleValuationPointerLess::operator()(SimpleValuation* valuation1, SimpleValuation* valuation2) const {
-            // Compare boolean variables.
-            bool less = valuation1->booleanValues < valuation2->booleanValues;
-            if (less) {
-                return true;
-            }
-            less = valuation1->integerValues < valuation2->integerValues;
-            if (less) {
-                return true;
-            }
-            less = valuation1->doubleValues < valuation2->doubleValues;
-            if (less) {
-                return true;
-            } else {
-                return false;
-            }
+            return valuation1->identifierToValueMap < valuation2->identifierToValueMap;
         }
     }
 }
\ No newline at end of file
diff --git a/src/storage/expressions/SimpleValuation.h b/src/storage/expressions/SimpleValuation.h
index a196921a6..fccfe2faa 100644
--- a/src/storage/expressions/SimpleValuation.h
+++ b/src/storage/expressions/SimpleValuation.h
@@ -1,12 +1,12 @@
 #ifndef STORM_STORAGE_EXPRESSIONS_SIMPLEVALUATION_H_
 #define STORM_STORAGE_EXPRESSIONS_SIMPLEVALUATION_H_
 
-#include <memory>
-#include <vector>
-#include <unordered_map>
+#include <boost/container/flat_map.hpp>
+#include <boost/variant.hpp>
 #include <iostream>
 
 #include "src/storage/expressions/Valuation.h"
+#include "src/storage/expressions/ExpressionReturnType.h"
 #include "src/utility/OsDetection.h"
 
 namespace storm {
@@ -16,12 +16,8 @@ namespace storm {
             friend class SimpleValuationPointerHash;
             friend class SimpleValuationPointerLess;
             
-            /*!
-             * Creates a simple valuation without any identifiers.
-            */
-            SimpleValuation();
-
             // Instantiate some constructors and assignments with their default implementations.
+            SimpleValuation() = default;
             SimpleValuation(SimpleValuation const&) = default;
             SimpleValuation& operator=(SimpleValuation const&) = default;
 #ifndef WINDOWS            
@@ -83,7 +79,30 @@ namespace storm {
              */
             void setDoubleValue(std::string const& name, double value);
             
+            /*!
+             * Removes the given identifier from this valuation.
+             *
+             * @param name The name of the identifier that is to be removed.
+             */
+            void removeIdentifier(std::string const& name);
+
+            /*!
+             * Retrieves the type of the identifier with the given name.
+             *
+             * @param name The name of the identifier whose type to retrieve.
+             * @return The type of the identifier with the given name.
+             */
+            ExpressionReturnType getIdentifierType(std::string const& name) const;
+            
             // Override base class methods.
+            virtual bool containsBooleanIdentifier(std::string const& name) const override;
+            virtual bool containsIntegerIdentifier(std::string const& name) const override;
+            virtual bool containsDoubleIdentifier(std::string const& name) const override;
+            virtual std::size_t getNumberOfIdentifiers() const override;
+            virtual std::set<std::string> getIdentifiers() const override;
+            virtual std::set<std::string> getBooleanIdentifiers() const override;
+            virtual std::set<std::string> getIntegerIdentifiers() const override;
+            virtual std::set<std::string> getDoubleIdentifiers() const override;
             virtual bool getBooleanValue(std::string const& name) const override;
             virtual int_fast64_t getIntegerValue(std::string const& name) const override;
             virtual double getDoubleValue(std::string const& name) const override;
@@ -92,22 +111,7 @@ namespace storm {
 
         private:
             // A mapping of boolean identifiers to their local indices in the value container.
-            std::shared_ptr<std::unordered_map<std::string, uint_fast64_t>> booleanIdentifierToIndexMap;
-            
-            // A mapping of integer identifiers to their local indices in the value container.
-            std::shared_ptr<std::unordered_map<std::string, uint_fast64_t>> integerIdentifierToIndexMap;
-            
-            // A mapping of double identifiers to their local indices in the value container.
-            std::shared_ptr<std::unordered_map<std::string, uint_fast64_t>> doubleIdentifierToIndexMap;
-            
-            // The value container for all boolean identifiers.
-            std::vector<bool> booleanValues;
-            
-            // The value container for all integer identifiers.
-            std::vector<int_fast64_t> integerValues;
-            
-            // The value container for all double identifiers.
-            std::vector<double> doubleValues;
+            boost::container::flat_map<std::string, boost::variant<bool, int_fast64_t, double>> identifierToValueMap;
         };
         
         /*!
diff --git a/src/storage/expressions/SubstitutionVisitor.cpp b/src/storage/expressions/SubstitutionVisitor.cpp
index 2559b474b..43aa01ab3 100644
--- a/src/storage/expressions/SubstitutionVisitor.cpp
+++ b/src/storage/expressions/SubstitutionVisitor.cpp
@@ -13,8 +13,8 @@ namespace storm {
         }
 
 		template<typename MapType>
-        Expression SubstitutionVisitor<MapType>::substitute(BaseExpression const* expression) {
-            expression->accept(this);
+        Expression SubstitutionVisitor<MapType>::substitute(Expression const& expression) {
+            expression.getBaseExpression().accept(this);
             return Expression(this->expressionStack.top());
         }
         
diff --git a/src/storage/expressions/SubstitutionVisitor.h b/src/storage/expressions/SubstitutionVisitor.h
index bc58148e3..0ebc0941e 100644
--- a/src/storage/expressions/SubstitutionVisitor.h
+++ b/src/storage/expressions/SubstitutionVisitor.h
@@ -26,7 +26,7 @@ namespace storm {
              * @return The expression in which all identifiers in the key set of the previously given mapping are
              * substituted with the mapped-to expressions.
              */
-            Expression substitute(BaseExpression const* expression);
+            Expression substitute(Expression const& expression);
             
             virtual void visit(IfThenElseExpression const* expression) override;
             virtual void visit(BinaryBooleanFunctionExpression const* expression) override;
diff --git a/src/storage/expressions/TypeCheckVisitor.cpp b/src/storage/expressions/TypeCheckVisitor.cpp
index 1c5899d61..521453042 100644
--- a/src/storage/expressions/TypeCheckVisitor.cpp
+++ b/src/storage/expressions/TypeCheckVisitor.cpp
@@ -12,8 +12,8 @@ namespace storm {
         }
         
         template<typename MapType>
-        void TypeCheckVisitor<MapType>::check(BaseExpression const* expression) {
-            expression->accept(this);
+        void TypeCheckVisitor<MapType>::check(Expression const& expression) {
+            expression.getBaseExpression().accept(this);
         }
         
         template<typename MapType>
diff --git a/src/storage/expressions/TypeCheckVisitor.h b/src/storage/expressions/TypeCheckVisitor.h
index 7772e0e7e..0cbf40f92 100644
--- a/src/storage/expressions/TypeCheckVisitor.h
+++ b/src/storage/expressions/TypeCheckVisitor.h
@@ -24,7 +24,7 @@ namespace storm {
              *
              * @param expression The expression in which to check the types.
              */
-            void check(BaseExpression const* expression);
+            void check(Expression const& expression);
             
             virtual void visit(IfThenElseExpression const* expression) override;
             virtual void visit(BinaryBooleanFunctionExpression const* expression) override;
diff --git a/src/storage/expressions/UnaryBooleanFunctionExpression.cpp b/src/storage/expressions/UnaryBooleanFunctionExpression.cpp
index 7e405bb29..1d9e59119 100644
--- a/src/storage/expressions/UnaryBooleanFunctionExpression.cpp
+++ b/src/storage/expressions/UnaryBooleanFunctionExpression.cpp
@@ -13,6 +13,12 @@ namespace storm {
             return this->operatorType;
         }
         
+        storm::expressions::OperatorType UnaryBooleanFunctionExpression::getOperator() const {
+            switch (this->getOperatorType()) {
+                case OperatorType::Not: return storm::expressions::OperatorType::Not;
+            }
+        }
+        
         bool UnaryBooleanFunctionExpression::evaluateAsBool(Valuation const* valuation) const {
             LOG_THROW(this->hasBooleanReturnType(), storm::exceptions::InvalidTypeException, "Unable to evaluate expression as boolean.");
 
diff --git a/src/storage/expressions/UnaryBooleanFunctionExpression.h b/src/storage/expressions/UnaryBooleanFunctionExpression.h
index 1f2800d8e..c69c6f886 100644
--- a/src/storage/expressions/UnaryBooleanFunctionExpression.h
+++ b/src/storage/expressions/UnaryBooleanFunctionExpression.h
@@ -32,6 +32,7 @@ namespace storm {
             virtual ~UnaryBooleanFunctionExpression() = default;
             
             // Override base class methods.
+            virtual storm::expressions::OperatorType getOperator() const override;
             virtual bool evaluateAsBool(Valuation const* valuation = nullptr) const override;
             virtual std::shared_ptr<BaseExpression const> simplify() const override;
             virtual void accept(ExpressionVisitor* visitor) const override;
diff --git a/src/storage/expressions/UnaryExpression.cpp b/src/storage/expressions/UnaryExpression.cpp
index cc75e6fa3..169466003 100644
--- a/src/storage/expressions/UnaryExpression.cpp
+++ b/src/storage/expressions/UnaryExpression.cpp
@@ -9,6 +9,10 @@ namespace storm {
             // Intentionally left empty.
         }
 
+        bool UnaryExpression::isFunctionApplication() const {
+            return true;
+        }
+        
         bool UnaryExpression::containsVariables() const {
             return this->getOperand()->containsVariables();
         }
diff --git a/src/storage/expressions/UnaryExpression.h b/src/storage/expressions/UnaryExpression.h
index 27b903638..5473d3122 100644
--- a/src/storage/expressions/UnaryExpression.h
+++ b/src/storage/expressions/UnaryExpression.h
@@ -26,6 +26,7 @@ namespace storm {
             virtual ~UnaryExpression() = default;
             
             // Override base class methods.
+            virtual bool isFunctionApplication() const override;
             virtual bool containsVariables() const override;
             virtual uint_fast64_t getArity() const override;
             virtual std::shared_ptr<BaseExpression const> getOperand(uint_fast64_t operandIndex) const override;
diff --git a/src/storage/expressions/UnaryNumericalFunctionExpression.cpp b/src/storage/expressions/UnaryNumericalFunctionExpression.cpp
index 02a4bee30..972f2a24a 100644
--- a/src/storage/expressions/UnaryNumericalFunctionExpression.cpp
+++ b/src/storage/expressions/UnaryNumericalFunctionExpression.cpp
@@ -14,6 +14,14 @@ namespace storm {
             return this->operatorType;
         }
         
+        storm::expressions::OperatorType UnaryNumericalFunctionExpression::getOperator() const {
+            switch (this->getOperatorType()) {
+                case OperatorType::Minus: return storm::expressions::OperatorType::Minus; break;
+                case OperatorType::Floor: return storm::expressions::OperatorType::Floor; break;
+                case OperatorType::Ceil: return storm::expressions::OperatorType::Ceil; break;
+            }
+        }
+        
         int_fast64_t UnaryNumericalFunctionExpression::evaluateAsInt(Valuation const* valuation) const {
             LOG_THROW(this->hasIntegralReturnType(), storm::exceptions::InvalidTypeException, "Unable to evaluate expression as integer.");
 
diff --git a/src/storage/expressions/UnaryNumericalFunctionExpression.h b/src/storage/expressions/UnaryNumericalFunctionExpression.h
index daa0df40f..10b85e63e 100644
--- a/src/storage/expressions/UnaryNumericalFunctionExpression.h
+++ b/src/storage/expressions/UnaryNumericalFunctionExpression.h
@@ -32,6 +32,7 @@ namespace storm {
             virtual ~UnaryNumericalFunctionExpression() = default;
             
             // Override base class methods.
+            virtual storm::expressions::OperatorType getOperator() const override;
             virtual int_fast64_t evaluateAsInt(Valuation const* valuation = nullptr) const override;
             virtual double evaluateAsDouble(Valuation const* valuation = nullptr) const override;
             virtual std::shared_ptr<BaseExpression const> simplify() const override;
diff --git a/src/storage/expressions/Valuation.h b/src/storage/expressions/Valuation.h
index 7aa32a859..a09c4f07c 100644
--- a/src/storage/expressions/Valuation.h
+++ b/src/storage/expressions/Valuation.h
@@ -2,6 +2,7 @@
 #define STORM_STORAGE_EXPRESSIONS_VALUATION_H_
 
 #include <string>
+#include <set>
 
 namespace storm {
     namespace expressions {
@@ -34,6 +35,66 @@ namespace storm {
              * @return The value of the double identifier.
              */
             virtual double getDoubleValue(std::string const& name) const = 0;
+            
+            /*!
+             * Retrieves whether there exists a boolean identifier with the given name in the valuation.
+             *
+             * @param name The name of the boolean identifier to query.
+             * @return True iff the identifier exists and is of boolean type.
+             */
+            virtual bool containsBooleanIdentifier(std::string const& name) const = 0;
+            
+            /*!
+             * Retrieves whether there exists a integer identifier with the given name in the valuation.
+             *
+             * @param name The name of the integer identifier to query.
+             * @return True iff the identifier exists and is of boolean type.
+             */
+            virtual bool containsIntegerIdentifier(std::string const& name) const = 0;
+            
+            /*!
+             * Retrieves whether there exists a double identifier with the given name in the valuation.
+             *
+             * @param name The name of the double identifier to query.
+             * @return True iff the identifier exists and is of boolean type.
+             */
+            virtual bool containsDoubleIdentifier(std::string const& name) const = 0;
+            
+            /*!
+             * Retrieves the number of identifiers in this valuation.
+             *
+             * @return The number of identifiers in this valuation.
+             */
+            virtual std::size_t getNumberOfIdentifiers() const = 0;
+            
+            /*!
+             * Retrieves the set of all identifiers contained in this valuation.
+             *
+             * @return The set of all identifiers contained in this valuation.
+             */
+            virtual std::set<std::string> getIdentifiers() const = 0;
+            
+            /*!
+             * Retrieves the set of boolean identifiers contained in this valuation.
+             *
+             * @return The set of boolean identifiers contained in this valuation.
+             */
+            virtual std::set<std::string> getBooleanIdentifiers() const = 0;
+
+            /*!
+             * Retrieves the set of integer identifiers contained in this valuation.
+             *
+             * @return The set of integer identifiers contained in this valuation.
+             */
+            virtual std::set<std::string> getIntegerIdentifiers() const = 0;
+
+            /*!
+             * Retrieves the set of double identifiers contained in this valuation.
+             *
+             * @return The set of double identifiers contained in this valuation.
+             */
+            virtual std::set<std::string> getDoubleIdentifiers() const = 0;
+
         };
     }
 }
diff --git a/src/storage/prism/Program.cpp b/src/storage/prism/Program.cpp
index 10a1114e6..617a1c52b 100644
--- a/src/storage/prism/Program.cpp
+++ b/src/storage/prism/Program.cpp
@@ -13,7 +13,7 @@ namespace storm {
         Program::Program(ModelType modelType, std::vector<Constant> const& constants, std::vector<BooleanVariable> const& globalBooleanVariables, std::vector<IntegerVariable> const& globalIntegerVariables, std::vector<Formula> const& formulas, std::vector<Module> const& modules, std::vector<RewardModel> const& rewardModels, bool fixInitialConstruct, storm::prism::InitialConstruct const& initialConstruct, std::vector<Label> const& labels, std::string const& filename, uint_fast64_t lineNumber, bool checkValidity) : LocatedInformation(filename, lineNumber), modelType(modelType), constants(constants), constantToIndexMap(), globalBooleanVariables(globalBooleanVariables), globalBooleanVariableToIndexMap(), globalIntegerVariables(globalIntegerVariables), globalIntegerVariableToIndexMap(), formulas(formulas), formulaToIndexMap(), modules(modules), moduleToIndexMap(), rewardModels(rewardModels), rewardModelToIndexMap(), initialConstruct(initialConstruct), labels(labels), labelToIndexMap(), actions(), actionsToModuleIndexMap(), variableToModuleIndexMap() {
             this->createMappings();
             
-            // Create a new initial construct if none was given explicitly.
+            // Create a new initial construct if the corresponding flag was set.
             if (fixInitialConstruct) {
                 if (this->getInitialConstruct().getInitialStatesExpression().isFalse()) {
                     storm::expressions::Expression newInitialExpression = storm::expressions::Expression::createTrue();
diff --git a/src/utility/counterexamples.h b/src/utility/counterexamples.h
index 23b5b372e..8b233e402 100644
--- a/src/utility/counterexamples.h
+++ b/src/utility/counterexamples.h
@@ -36,8 +36,8 @@ namespace storm {
 //                for (auto state : psiStates) {
 //                    analysisInformation[state] = boost::container::flat_set<uint_fast64_t>();
 //                    for (auto const& predecessorEntry : backwardTransitions.getRow(state)) {
-//                        if (predecessorEntry.first != state && !psiStates.get(predecessorEntry.first)) {
-//                            worklist.push(std::make_pair(predecessorEntry.first, state));
+//                        if (predecessorEntry.getColumn() != state && !psiStates.get(predecessorEntry.getColumn())) {
+//                            worklist.push(std::make_pair(predecessorEntry.getColumn(), state));
 //                        }
 //                    }
 //                }
@@ -57,7 +57,7 @@ namespace storm {
 //                        bool choiceTargetsTargetState = false;
 //                        
 //                        for (auto& entry : transitionMatrix.getRow(currentChoice)) {
-//                            if (entry.first == targetState) {
+//                            if (entry.getColumn() == targetState) {
 //                                choiceTargetsTargetState = true;
 //                                break;
 //                            }
@@ -79,8 +79,8 @@ namespace storm {
 //                    if (analysisInformation[currentState].size() != analysisInformationSizeBefore) {
 //                        for (auto& predecessorEntry : backwardTransitions.getRow(currentState)) {
 //                            // Only put the predecessor in the worklist if it's not already a target state.
-//                            if (!psiStates.get(predecessorEntry.first)) {
-//                                worklist.push(std::make_pair(predecessorEntry.first, currentState));
+//                            if (!psiStates.get(predecessorEntry.getColumn())) {
+//                                worklist.push(std::make_pair(predecessorEntry.getColumn(), currentState));
 //                            }
 //                        }
 //                    }
@@ -96,9 +96,9 @@ namespace storm {
                 for (auto state : psiStates) {
                     analysisInformation[state] = boost::container::flat_set<uint_fast64_t>();
                     for (auto const& predecessorEntry : backwardTransitions.getRow(state)) {
-                        if (predecessorEntry.first != state && !statesInWorkList.get(predecessorEntry.first) && !psiStates.get(predecessorEntry.first)) {
-                            worklist.push(predecessorEntry.first);
-                            statesInWorkList.set(predecessorEntry.first);
+                        if (predecessorEntry.getColumn() != state && !statesInWorkList.get(predecessorEntry.getColumn()) && !psiStates.get(predecessorEntry.getColumn())) {
+                            worklist.push(predecessorEntry.getColumn());
+                            statesInWorkList.set(predecessorEntry.getColumn());
                             markedStates.set(state);
                         }
                     }
@@ -116,7 +116,7 @@ namespace storm {
                         bool modifiedChoice = false;
                         
                         for (auto const& entry : transitionMatrix.getRow(currentChoice)) {
-                            if (markedStates.get(entry.first)) {
+                            if (markedStates.get(entry.getColumn())) {
                                 modifiedChoice = true;
                                 break;
                             }
@@ -127,9 +127,9 @@ namespace storm {
                         // and the choice labels.
                         if (modifiedChoice) {
                             for (auto const& entry : transitionMatrix.getRow(currentChoice)) {
-                                if (markedStates.get(entry.first)) {
+                                if (markedStates.get(entry.getColumn())) {
                                     boost::container::flat_set<uint_fast64_t> tmpIntersection;
-                                    std::set_intersection(analysisInformation[currentState].begin(), analysisInformation[currentState].end(), analysisInformation[entry.first].begin(), analysisInformation[entry.first].end(), std::inserter(tmpIntersection, tmpIntersection.begin()));
+                                    std::set_intersection(analysisInformation[currentState].begin(), analysisInformation[currentState].end(), analysisInformation[entry.getColumn()].begin(), analysisInformation[entry.getColumn()].end(), std::inserter(tmpIntersection, tmpIntersection.begin()));
                                     std::set_intersection(analysisInformation[currentState].begin(), analysisInformation[currentState].end(), choiceLabeling[currentChoice].begin(), choiceLabeling[currentChoice].end(), std::inserter(tmpIntersection, tmpIntersection.begin()));
                                     analysisInformation[currentState] = std::move(tmpIntersection);
                                 }
@@ -142,9 +142,9 @@ namespace storm {
                     if (analysisInformation[currentState].size() != analysisInformationSizeBefore) {
                         for (auto const& predecessorEntry : backwardTransitions.getRow(currentState)) {
                             // Only put the predecessor in the worklist if it's not already a target state.
-                            if (!psiStates.get(predecessorEntry.first) && !statesInWorkList.get(predecessorEntry.first)) {
-                                worklist.push(predecessorEntry.first);
-                                statesInWorkList.set(predecessorEntry.first);
+                            if (!psiStates.get(predecessorEntry.getColumn()) && !statesInWorkList.get(predecessorEntry.getColumn())) {
+                                worklist.push(predecessorEntry.getColumn());
+                                statesInWorkList.set(predecessorEntry.getColumn());
                             }
                         }
                         markedStates.set(currentState, true);
diff --git a/src/utility/graph.h b/src/utility/graph.h
index 01d0a7f4f..3d0d95fb1 100644
--- a/src/utility/graph.h
+++ b/src/utility/graph.h
@@ -79,16 +79,16 @@ namespace storm {
                     }
                     
                     for (typename storm::storage::SparseMatrix<T>::const_iterator entryIt = backwardTransitions.begin(currentState), entryIte = backwardTransitions.end(currentState); entryIt != entryIte; ++entryIt) {
-                        if (phiStates[entryIt->first] && (!statesWithProbabilityGreater0.get(entryIt->first) || (useStepBound && remainingSteps[entryIt->first] < currentStepBound - 1))) {
+                        if (phiStates[entryIt->getColumn()] && (!statesWithProbabilityGreater0.get(entryIt->getColumn()) || (useStepBound && remainingSteps[entryIt->getColumn()] < currentStepBound - 1))) {
                             // If we don't have a bound on the number of steps to take, just add the state to the stack.
                             if (!useStepBound) {
-                                statesWithProbabilityGreater0.set(entryIt->first, true);
-                                stack.push_back(entryIt->first);
+                                statesWithProbabilityGreater0.set(entryIt->getColumn(), true);
+                                stack.push_back(entryIt->getColumn());
                             } else if (currentStepBound > 0) {
                                 // If there is at least one more step to go, we need to push the state and the new number of steps.
-                                remainingSteps[entryIt->first] = currentStepBound - 1;
-                                statesWithProbabilityGreater0.set(entryIt->first, true);
-                                stack.push_back(entryIt->first);
+                                remainingSteps[entryIt->getColumn()] = currentStepBound - 1;
+                                statesWithProbabilityGreater0.set(entryIt->getColumn(), true);
+                                stack.push_back(entryIt->getColumn());
                                 stepStack.push_back(currentStepBound - 1);
                             }
                         }
@@ -211,16 +211,16 @@ namespace storm {
                     }
                     
                     for (typename storm::storage::SparseMatrix<T>::const_iterator entryIt = backwardTransitions.begin(currentState), entryIte = backwardTransitions.end(currentState); entryIt != entryIte; ++entryIt) {
-                        if (phiStates.get(entryIt->first) && (!statesWithProbabilityGreater0.get(entryIt->first) || (useStepBound && remainingSteps[entryIt->first] < currentStepBound - 1))) {
+                        if (phiStates.get(entryIt->getColumn()) && (!statesWithProbabilityGreater0.get(entryIt->getColumn()) || (useStepBound && remainingSteps[entryIt->getColumn()] < currentStepBound - 1))) {
                             // If we don't have a bound on the number of steps to take, just add the state to the stack.
                             if (!useStepBound) {
-                                statesWithProbabilityGreater0.set(entryIt->first, true);
-                                stack.push_back(entryIt->first);
+                                statesWithProbabilityGreater0.set(entryIt->getColumn(), true);
+                                stack.push_back(entryIt->getColumn());
                             } else if (currentStepBound > 0) {
                                 // If there is at least one more step to go, we need to push the state and the new number of steps.
-                                remainingSteps[entryIt->first] = currentStepBound - 1;
-                                statesWithProbabilityGreater0.set(entryIt->first, true);
-                                stack.push_back(entryIt->first);
+                                remainingSteps[entryIt->getColumn()] = currentStepBound - 1;
+                                statesWithProbabilityGreater0.set(entryIt->getColumn(), true);
+                                stack.push_back(entryIt->getColumn());
                                 stepStack.push_back(currentStepBound - 1);
                             }
                         }
@@ -290,13 +290,13 @@ namespace storm {
                         stack.pop_back();
                         
                         for (typename storm::storage::SparseMatrix<T>::const_iterator predecessorEntryIt = backwardTransitions.begin(currentState), predecessorEntryIte = backwardTransitions.end(currentState); predecessorEntryIt != predecessorEntryIte; ++predecessorEntryIt) {
-                            if (phiStates.get(predecessorEntryIt->first) && !nextStates.get(predecessorEntryIt->first)) {
+                            if (phiStates.get(predecessorEntryIt->getColumn()) && !nextStates.get(predecessorEntryIt->getColumn())) {
                                 // Check whether the predecessor has only successors in the current state set for one of the
                                 // nondeterminstic choices.
-                                for (uint_fast64_t row = nondeterministicChoiceIndices[predecessorEntryIt->first]; row < nondeterministicChoiceIndices[predecessorEntryIt->first + 1]; ++row) {
+                                for (uint_fast64_t row = nondeterministicChoiceIndices[predecessorEntryIt->getColumn()]; row < nondeterministicChoiceIndices[predecessorEntryIt->getColumn() + 1]; ++row) {
                                     bool allSuccessorsInCurrentStates = true;
                                     for (typename storm::storage::SparseMatrix<T>::const_iterator successorEntryIt = transitionMatrix.begin(row), successorEntryIte = transitionMatrix.end(row); successorEntryIt != successorEntryIte; ++successorEntryIt) {
-                                        if (!currentStates.get(successorEntryIt->first)) {
+                                        if (!currentStates.get(successorEntryIt->getColumn())) {
                                             allSuccessorsInCurrentStates = false;
                                             break;
                                         }
@@ -306,8 +306,8 @@ namespace storm {
                                     // add it to the set of states for the next iteration and perform a backward search from
                                     // that state.
                                     if (allSuccessorsInCurrentStates) {
-                                        nextStates.set(predecessorEntryIt->first, true);
-                                        stack.push_back(predecessorEntryIt->first);
+                                        nextStates.set(predecessorEntryIt->getColumn(), true);
+                                        stack.push_back(predecessorEntryIt->getColumn());
                                         break;
                                     }
                                 }
@@ -401,14 +401,14 @@ namespace storm {
                     }
                     
                     for(typename storm::storage::SparseMatrix<T>::const_iterator predecessorEntryIt = backwardTransitions.begin(currentState), predecessorEntryIte = backwardTransitions.end(currentState); predecessorEntryIt != predecessorEntryIte; ++predecessorEntryIt) {
-                        if (phiStates.get(predecessorEntryIt->first) && (!statesWithProbabilityGreater0.get(predecessorEntryIt->first) || (useStepBound && remainingSteps[predecessorEntryIt->first] < currentStepBound - 1))) {
+                        if (phiStates.get(predecessorEntryIt->getColumn()) && (!statesWithProbabilityGreater0.get(predecessorEntryIt->getColumn()) || (useStepBound && remainingSteps[predecessorEntryIt->getColumn()] < currentStepBound - 1))) {
                             // Check whether the predecessor has at least one successor in the current state set for every
                             // nondeterministic choice.
                             bool addToStatesWithProbabilityGreater0 = true;
-                            for (uint_fast64_t row = nondeterministicChoiceIndices[predecessorEntryIt->first]; row < nondeterministicChoiceIndices[predecessorEntryIt->first + 1]; ++row) {
+                            for (uint_fast64_t row = nondeterministicChoiceIndices[predecessorEntryIt->getColumn()]; row < nondeterministicChoiceIndices[predecessorEntryIt->getColumn() + 1]; ++row) {
                                 bool hasAtLeastOneSuccessorWithProbabilityGreater0 = false;
                                 for (typename storm::storage::SparseMatrix<T>::const_iterator successorEntryIt = transitionMatrix.begin(row), successorEntryIte = transitionMatrix.end(row); successorEntryIt != successorEntryIte; ++successorEntryIt) {
-                                    if (statesWithProbabilityGreater0.get(successorEntryIt->first)) {
+                                    if (statesWithProbabilityGreater0.get(successorEntryIt->getColumn())) {
                                         hasAtLeastOneSuccessorWithProbabilityGreater0 = true;
                                         break;
                                     }
@@ -424,13 +424,13 @@ namespace storm {
                             if (addToStatesWithProbabilityGreater0) {
                                 // If we don't have a bound on the number of steps to take, just add the state to the stack.
                                 if (!useStepBound) {
-                                    statesWithProbabilityGreater0.set(predecessorEntryIt->first, true);
-                                    stack.push_back(predecessorEntryIt->first);
+                                    statesWithProbabilityGreater0.set(predecessorEntryIt->getColumn(), true);
+                                    stack.push_back(predecessorEntryIt->getColumn());
                                 } else if (currentStepBound > 0) {
                                     // If there is at least one more step to go, we need to push the state and the new number of steps.
-                                    remainingSteps[predecessorEntryIt->first] = currentStepBound - 1;
-                                    statesWithProbabilityGreater0.set(predecessorEntryIt->first, true);
-                                    stack.push_back(predecessorEntryIt->first);
+                                    remainingSteps[predecessorEntryIt->getColumn()] = currentStepBound - 1;
+                                    statesWithProbabilityGreater0.set(predecessorEntryIt->getColumn(), true);
+                                    stack.push_back(predecessorEntryIt->getColumn());
                                     stepStack.push_back(currentStepBound - 1);
                                 }
                             }
@@ -501,12 +501,12 @@ namespace storm {
                         stack.pop_back();
                         
                         for(typename storm::storage::SparseMatrix<T>::const_iterator predecessorEntryIt = backwardTransitions.begin(currentState), predecessorEntryIte = backwardTransitions.end(currentState); predecessorEntryIt != predecessorEntryIte; ++predecessorEntryIt) {
-                            if (phiStates.get(predecessorEntryIt->first) && !nextStates.get(predecessorEntryIt->first)) {
+                            if (phiStates.get(predecessorEntryIt->getColumn()) && !nextStates.get(predecessorEntryIt->getColumn())) {
                                 // Check whether the predecessor has only successors in the current state set for all of the
                                 // nondeterminstic choices.
                                 bool allSuccessorsInCurrentStatesForAllChoices = true;
-                                for (typename storm::storage::SparseMatrix<T>::const_iterator successorEntryIt = transitionMatrix.begin(nondeterministicChoiceIndices[predecessorEntryIt->first]), successorEntryIte = transitionMatrix.begin(nondeterministicChoiceIndices[predecessorEntryIt->first + 1]); successorEntryIt != successorEntryIte; ++successorEntryIt) {
-                                    if (!currentStates.get(successorEntryIt->first)) {
+                                for (typename storm::storage::SparseMatrix<T>::const_iterator successorEntryIt = transitionMatrix.begin(nondeterministicChoiceIndices[predecessorEntryIt->getColumn()]), successorEntryIte = transitionMatrix.begin(nondeterministicChoiceIndices[predecessorEntryIt->getColumn() + 1]); successorEntryIt != successorEntryIte; ++successorEntryIt) {
+                                    if (!currentStates.get(successorEntryIt->getColumn())) {
                                         allSuccessorsInCurrentStatesForAllChoices = false;
                                         goto afterCheckLoop;
                                     }
@@ -517,8 +517,8 @@ namespace storm {
                                 // add it to the set of states for the next iteration and perform a backward search from
                                 // that state.
                                 if (allSuccessorsInCurrentStatesForAllChoices) {
-                                    nextStates.set(predecessorEntryIt->first, true);
-                                    stack.push_back(predecessorEntryIt->first);
+                                    nextStates.set(predecessorEntryIt->getColumn(), true);
+                                    stack.push_back(predecessorEntryIt->getColumn());
                                 }
                             }
                         }
diff --git a/src/utility/matrix.h b/src/utility/matrix.h
index 446fb413f..1027e7e42 100644
--- a/src/utility/matrix.h
+++ b/src/utility/matrix.h
@@ -32,7 +32,7 @@ namespace storm {
                         // If a valid choice for this state is defined, we copy over the corresponding entries.
                         typename storm::storage::SparseMatrix<T>::const_rows selectedRow = transitionMatrix.getRow(choice);
                         for (auto const& entry : selectedRow) {
-                            matrixBuilder.addNextValue(state, entry.first, entry.second);
+                            matrixBuilder.addNextValue(state, entry.getColumn(), entry.getValue());
                         }
                     } else {
                         // If no valid choice for the state is defined, we insert a self-loop.
diff --git a/test/functional/modelchecker/GmmxxDtmcPrctlModelCheckerTest.cpp b/test/functional/modelchecker/GmmxxDtmcPrctlModelCheckerTest.cpp
index fdff14a66..dc2d6d703 100644
--- a/test/functional/modelchecker/GmmxxDtmcPrctlModelCheckerTest.cpp
+++ b/test/functional/modelchecker/GmmxxDtmcPrctlModelCheckerTest.cpp
@@ -18,7 +18,7 @@ TEST(GmmxxDtmcPrctlModelCheckerTest, Die) {
 	std::shared_ptr<storm::models::Dtmc<double>> dtmc = abstractModel->as<storm::models::Dtmc<double>>();
 
 	ASSERT_EQ(dtmc->getNumberOfStates(), 13ull);
-	ASSERT_EQ(dtmc->getNumberOfTransitions(), 27ull);
+	ASSERT_EQ(dtmc->getNumberOfTransitions(), 20ull);
 
 	storm::modelchecker::prctl::SparseDtmcPrctlModelChecker<double> mc(*dtmc, new storm::solver::GmmxxLinearEquationSolver<double>());
 
@@ -74,7 +74,7 @@ TEST(GmmxxDtmcPrctlModelCheckerTest, Crowds) {
 	std::shared_ptr<storm::models::Dtmc<double>> dtmc = abstractModel->as<storm::models::Dtmc<double>>();
 
 	ASSERT_EQ(dtmc->getNumberOfStates(), 8607ull);
-	ASSERT_EQ(dtmc->getNumberOfTransitions(), 22460ull);
+	ASSERT_EQ(dtmc->getNumberOfTransitions(), 15113ull);
 
 	storm::modelchecker::prctl::SparseDtmcPrctlModelChecker<double> mc(*dtmc, new storm::solver::GmmxxLinearEquationSolver<double>());
 
@@ -119,7 +119,7 @@ TEST(GmmxxDtmcPrctlModelCheckerTest, SynchronousLeader) {
 	std::shared_ptr<storm::models::Dtmc<double>> dtmc = abstractModel->as<storm::models::Dtmc<double>>();
 
 	ASSERT_EQ(dtmc->getNumberOfStates(), 12400ull);
-	ASSERT_EQ(dtmc->getNumberOfTransitions(), 28894ull);
+	ASSERT_EQ(dtmc->getNumberOfTransitions(), 16495ull);
 
 	storm::modelchecker::prctl::SparseDtmcPrctlModelChecker<double> mc(*dtmc, new storm::solver::GmmxxLinearEquationSolver<double>());
 
diff --git a/test/functional/parser/AutoParserTest.cpp b/test/functional/parser/AutoParserTest.cpp
index 3c4927ecd..7c3fcb483 100644
--- a/test/functional/parser/AutoParserTest.cpp
+++ b/test/functional/parser/AutoParserTest.cpp
@@ -24,7 +24,7 @@ TEST(AutoParserTest, BasicParsing) {
 	// Test if parsed correctly.
 	ASSERT_EQ(storm::models::DTMC, modelPtr->getType());
 	ASSERT_EQ(12, modelPtr->getNumberOfStates());
-	ASSERT_EQ(32, modelPtr->getNumberOfTransitions());
+	ASSERT_EQ(26, modelPtr->getNumberOfTransitions());
 	ASSERT_EQ(1, modelPtr->getInitialStates().getNumberOfSetBits());
 	ASSERT_TRUE(modelPtr->hasAtomicProposition("three"));
 	ASSERT_FALSE(modelPtr->hasStateRewards());
@@ -56,21 +56,21 @@ TEST(AutoParserTest, Decision) {
 	std::shared_ptr<storm::models::AbstractModel<double>> modelPtr = storm::parser::AutoParser::parseModel(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/autoParser/dtmc.tra", STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/autoParser.lab");
 	ASSERT_EQ(storm::models::DTMC, modelPtr->getType());
 	ASSERT_EQ(12, modelPtr->getNumberOfStates());
-	ASSERT_EQ(32, modelPtr->getNumberOfTransitions());
+	ASSERT_EQ(26, modelPtr->getNumberOfTransitions());
 
 	// Ctmc
 	modelPtr.reset();
 	modelPtr = storm::parser::AutoParser::parseModel(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/autoParser/ctmc.tra", STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/autoParser.lab");
 	ASSERT_EQ(storm::models::CTMC, modelPtr->getType());
 	ASSERT_EQ(12, modelPtr->getNumberOfStates());
-	ASSERT_EQ(31, modelPtr->getNumberOfTransitions());
+	ASSERT_EQ(26, modelPtr->getNumberOfTransitions());
 
 	// Mdp
 	modelPtr.reset();
 	modelPtr = storm::parser::AutoParser::parseModel(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/autoParser/mdp.tra", STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/autoParser.lab");
 	ASSERT_EQ(storm::models::MDP, modelPtr->getType());
 	ASSERT_EQ(12, modelPtr->getNumberOfStates());
-	ASSERT_EQ(36, modelPtr->getNumberOfTransitions());
+	ASSERT_EQ(28, modelPtr->getNumberOfTransitions());
 
 	// Ctmdp
 	// Note: For now we use the Mdp from above just given the ctmdp hint, since the implementation of the Ctmdp model seems not Quite right yet.
@@ -80,12 +80,12 @@ TEST(AutoParserTest, Decision) {
 	modelPtr = storm::parser::AutoParser::parseModel(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/autoParser/ctmdp.tra", STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/autoParser.lab");
 	ASSERT_EQ(storm::models::CTMDP, modelPtr->getType());
 	ASSERT_EQ(12, modelPtr->getNumberOfStates());
-	ASSERT_EQ(36, modelPtr->getNumberOfTransitions());
+	ASSERT_EQ(28, modelPtr->getNumberOfTransitions());
 
 	// MA
 	modelPtr.reset();
 	modelPtr = storm::parser::AutoParser::parseModel(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/autoParser/ma.tra", STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/autoParser.lab");
 	ASSERT_EQ(storm::models::MA, modelPtr->getType());
 	ASSERT_EQ(12, modelPtr->getNumberOfStates());
-	ASSERT_EQ(35, modelPtr->getNumberOfTransitions());
+	ASSERT_EQ(27, modelPtr->getNumberOfTransitions());
 }
diff --git a/test/functional/parser/DeterministicModelParserTest.cpp b/test/functional/parser/DeterministicModelParserTest.cpp
index 4a62e5e6e..1b6fe71db 100644
--- a/test/functional/parser/DeterministicModelParserTest.cpp
+++ b/test/functional/parser/DeterministicModelParserTest.cpp
@@ -26,7 +26,7 @@ TEST(DeterministicModelParserTest, BasicDtmcParsing) {
 	storm::models::Dtmc<double> dtmc(storm::parser::DeterministicModelParser::parseDtmc(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/dtmc_general.tra", STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/dtmc_general.lab", STORM_CPP_TESTS_BASE_PATH "/functional/parser/rew_files/dtmc_general.state.rew", STORM_CPP_TESTS_BASE_PATH "/functional/parser/rew_files/dtmc_general.trans.rew"));
 
 	ASSERT_EQ(8, dtmc.getNumberOfStates());
-	ASSERT_EQ(21, dtmc.getNumberOfTransitions());
+	ASSERT_EQ(16, dtmc.getNumberOfTransitions());
 
 	ASSERT_EQ(2, dtmc.getInitialStates().getNumberOfSetBits());
 	ASSERT_TRUE(dtmc.getInitialStates().get(0));
@@ -58,7 +58,7 @@ TEST(DeterministicModelParserTest, BasicCtmcParsing) {
 	storm::models::Ctmc<double> ctmc(storm::parser::DeterministicModelParser::parseCtmc(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/dtmc_general.tra", STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/dtmc_general.lab", STORM_CPP_TESTS_BASE_PATH "/functional/parser/rew_files/dtmc_general.state.rew", STORM_CPP_TESTS_BASE_PATH "/functional/parser/rew_files/dtmc_general.trans.rew"));
 
 	ASSERT_EQ(8, ctmc.getNumberOfStates());
-	ASSERT_EQ(21, ctmc.getNumberOfTransitions());
+	ASSERT_EQ(16, ctmc.getNumberOfTransitions());
 
 	ASSERT_EQ(2, ctmc.getInitialStates().getNumberOfSetBits());
 	ASSERT_TRUE(ctmc.getInitialStates().get(0));
diff --git a/test/functional/parser/DeterministicSparseTransitionParserTest.cpp b/test/functional/parser/DeterministicSparseTransitionParserTest.cpp
index 2b301a1e7..89c5c9c6e 100644
--- a/test/functional/parser/DeterministicSparseTransitionParserTest.cpp
+++ b/test/functional/parser/DeterministicSparseTransitionParserTest.cpp
@@ -35,68 +35,68 @@ TEST(DeterministicSparseTransitionParserTest, BasicTransitionsParsing) {
 	// Test every entry of the matrix.
 	storm::storage::SparseMatrix<double>::const_iterator cIter = transitionMatrix.begin(0);
 
-	ASSERT_EQ(0, cIter->first);
-	ASSERT_EQ(0, cIter->second);
+	ASSERT_EQ(0, cIter->getColumn());
+	ASSERT_EQ(0, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(1, cIter->first);
-	ASSERT_EQ(1, cIter->second);
+	ASSERT_EQ(1, cIter->getColumn());
+	ASSERT_EQ(1, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(1, cIter->first);
-	ASSERT_EQ(0, cIter->second);
+	ASSERT_EQ(1, cIter->getColumn());
+	ASSERT_EQ(0, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(2, cIter->first);
-	ASSERT_EQ(0.5, cIter->second);
+	ASSERT_EQ(2, cIter->getColumn());
+	ASSERT_EQ(0.5, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(3, cIter->first);
-	ASSERT_EQ(0.5, cIter->second);
+	ASSERT_EQ(3, cIter->getColumn());
+	ASSERT_EQ(0.5, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(2, cIter->first);
-	ASSERT_EQ(0, cIter->second);
+	ASSERT_EQ(2, cIter->getColumn());
+	ASSERT_EQ(0, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(3, cIter->first);
-	ASSERT_EQ(0.4, cIter->second);
+	ASSERT_EQ(3, cIter->getColumn());
+	ASSERT_EQ(0.4, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(4, cIter->first);
-	ASSERT_EQ(0.4, cIter->second);
+	ASSERT_EQ(4, cIter->getColumn());
+	ASSERT_EQ(0.4, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(5, cIter->first);
-	ASSERT_EQ(0.2, cIter->second);
+	ASSERT_EQ(5, cIter->getColumn());
+	ASSERT_EQ(0.2, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(3, cIter->first);
-	ASSERT_EQ(1, cIter->second);
+	ASSERT_EQ(3, cIter->getColumn());
+	ASSERT_EQ(1, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(3, cIter->first);
-	ASSERT_EQ(1, cIter->second);
+	ASSERT_EQ(3, cIter->getColumn());
+	ASSERT_EQ(1, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(4, cIter->first);
-	ASSERT_EQ(0, cIter->second);
+	ASSERT_EQ(4, cIter->getColumn());
+	ASSERT_EQ(0, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(3, cIter->first);
-	ASSERT_EQ(0.1, cIter->second);
+	ASSERT_EQ(3, cIter->getColumn());
+	ASSERT_EQ(0.1, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(4, cIter->first);
-	ASSERT_EQ(0.1, cIter->second);
+	ASSERT_EQ(4, cIter->getColumn());
+	ASSERT_EQ(0.1, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(5, cIter->first);
-	ASSERT_EQ(0.1, cIter->second);
+	ASSERT_EQ(5, cIter->getColumn());
+	ASSERT_EQ(0.1, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(6, cIter->first);
-	ASSERT_EQ(0.7, cIter->second);
+	ASSERT_EQ(6, cIter->getColumn());
+	ASSERT_EQ(0.7, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(0, cIter->first);
-	ASSERT_EQ(0.9, cIter->second);
+	ASSERT_EQ(0, cIter->getColumn());
+	ASSERT_EQ(0.9, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(5, cIter->first);
-	ASSERT_EQ(0, cIter->second);
+	ASSERT_EQ(5, cIter->getColumn());
+	ASSERT_EQ(0, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(6, cIter->first);
-	ASSERT_EQ(0.1, cIter->second);
+	ASSERT_EQ(6, cIter->getColumn());
+	ASSERT_EQ(0.1, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(6, cIter->first);
-	ASSERT_EQ(0.224653, cIter->second);
+	ASSERT_EQ(6, cIter->getColumn());
+	ASSERT_EQ(0.224653, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(7, cIter->first);
-	ASSERT_EQ(0.775347, cIter->second);
+	ASSERT_EQ(7, cIter->getColumn());
+	ASSERT_EQ(0.775347, cIter->getValue());
 }
 
 TEST(DeterministicSparseTransitionParserTest, BasicTransitionsRewardsParsing) {
@@ -112,56 +112,56 @@ TEST(DeterministicSparseTransitionParserTest, BasicTransitionsRewardsParsing) {
 	// Test every entry of the matrix.
 	storm::storage::SparseMatrix<double>::const_iterator cIter = rewardMatrix.begin(0);
 
-	ASSERT_EQ(1, cIter->first);
-	ASSERT_EQ(10, cIter->second);
+	ASSERT_EQ(1, cIter->getColumn());
+	ASSERT_EQ(10, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(2, cIter->first);
-	ASSERT_EQ(5, cIter->second);
+	ASSERT_EQ(2, cIter->getColumn());
+	ASSERT_EQ(5, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(3, cIter->first);
-	ASSERT_EQ(5.5, cIter->second);
+	ASSERT_EQ(3, cIter->getColumn());
+	ASSERT_EQ(5.5, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(3, cIter->first);
-	ASSERT_EQ(21.4, cIter->second);
+	ASSERT_EQ(3, cIter->getColumn());
+	ASSERT_EQ(21.4, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(4, cIter->first);
-	ASSERT_EQ(4, cIter->second);
+	ASSERT_EQ(4, cIter->getColumn());
+	ASSERT_EQ(4, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(5, cIter->first);
-	ASSERT_EQ(2, cIter->second);
+	ASSERT_EQ(5, cIter->getColumn());
+	ASSERT_EQ(2, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(3, cIter->first);
-	ASSERT_EQ(1, cIter->second);
+	ASSERT_EQ(3, cIter->getColumn());
+	ASSERT_EQ(1, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(3, cIter->first);
-	ASSERT_EQ(1, cIter->second);
+	ASSERT_EQ(3, cIter->getColumn());
+	ASSERT_EQ(1, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(3, cIter->first);
-	ASSERT_EQ(0.1, cIter->second);
+	ASSERT_EQ(3, cIter->getColumn());
+	ASSERT_EQ(0.1, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(4, cIter->first);
-	ASSERT_EQ(1.1, cIter->second);
+	ASSERT_EQ(4, cIter->getColumn());
+	ASSERT_EQ(1.1, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(5, cIter->first);
-	ASSERT_EQ(9.5, cIter->second);
+	ASSERT_EQ(5, cIter->getColumn());
+	ASSERT_EQ(9.5, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(6, cIter->first);
-	ASSERT_EQ(6.7, cIter->second);
+	ASSERT_EQ(6, cIter->getColumn());
+	ASSERT_EQ(6.7, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(0, cIter->first);
-	ASSERT_EQ(1, cIter->second);
+	ASSERT_EQ(0, cIter->getColumn());
+	ASSERT_EQ(1, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(5, cIter->first);
-	ASSERT_EQ(0, cIter->second);
+	ASSERT_EQ(5, cIter->getColumn());
+	ASSERT_EQ(0, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(6, cIter->first);
-	ASSERT_EQ(12, cIter->second);
+	ASSERT_EQ(6, cIter->getColumn());
+	ASSERT_EQ(12, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(6, cIter->first);
-	ASSERT_EQ(35.224653, cIter->second);
+	ASSERT_EQ(6, cIter->getColumn());
+	ASSERT_EQ(35.224653, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(7, cIter->first);
-	ASSERT_EQ(9.875347, cIter->second);
+	ASSERT_EQ(7, cIter->getColumn());
+	ASSERT_EQ(9.875347, cIter->getValue());
 }
 
 
@@ -201,17 +201,17 @@ TEST(DeterministicSparseTransitionParserTest, FixDeadlocks) {
 	ASSERT_EQ(23, transitionMatrix.getEntryCount());
 
 	storm::storage::SparseMatrix<double>::const_iterator cIter = transitionMatrix.begin(7);
-	ASSERT_EQ(7, cIter->first);
-	ASSERT_EQ(1, cIter->second);
+	ASSERT_EQ(7, cIter->getColumn());
+	ASSERT_EQ(1, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(6, cIter->first);
-	ASSERT_EQ(0.224653, cIter->second);
+	ASSERT_EQ(6, cIter->getColumn());
+	ASSERT_EQ(0.224653, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(7, cIter->first);
-	ASSERT_EQ(0.775347, cIter->second);
+	ASSERT_EQ(7, cIter->getColumn());
+	ASSERT_EQ(0.775347, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(8, cIter->first);
-	ASSERT_EQ(0, cIter->second);
+	ASSERT_EQ(8, cIter->getColumn());
+	ASSERT_EQ(0, cIter->getValue());
 }
 
 TEST(DeterministicSparseTransitionParserTest, DontFixDeadlocks) {
diff --git a/test/functional/parser/MarkovAutomatonSparseTransitionParserTest.cpp b/test/functional/parser/MarkovAutomatonSparseTransitionParserTest.cpp
index 81c2ccfb8..38b152cd4 100644
--- a/test/functional/parser/MarkovAutomatonSparseTransitionParserTest.cpp
+++ b/test/functional/parser/MarkovAutomatonSparseTransitionParserTest.cpp
@@ -79,29 +79,29 @@ TEST(MarkovAutomatonSparseTransitionParserTest, BasicParsing) {
 	// Finally, test the transition matrix itself.
 	storm::storage::SparseMatrix<double>::const_iterator cIter = transitionMatrix.begin(0);
 
-	ASSERT_EQ(2, cIter->second);
+	ASSERT_EQ(2, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(1, cIter->second);
+	ASSERT_EQ(1, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(1, cIter->second);
+	ASSERT_EQ(1, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(2, cIter->second);
+	ASSERT_EQ(2, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(4, cIter->second);
+	ASSERT_EQ(4, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(8, cIter->second);
+	ASSERT_EQ(8, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(0.5, cIter->second);
+	ASSERT_EQ(0.5, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(0.5, cIter->second);
+	ASSERT_EQ(0.5, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(1, cIter->second);
+	ASSERT_EQ(1, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(0.5, cIter->second);
+	ASSERT_EQ(0.5, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(0.5, cIter->second);
+	ASSERT_EQ(0.5, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(1, cIter->second);
+	ASSERT_EQ(1, cIter->getValue());
 	cIter++;
 	ASSERT_EQ(transitionMatrix.end(), cIter);
 }
@@ -157,29 +157,29 @@ TEST(MarkovAutomatonSparseTransitionParserTest, Whitespaces) {
 	// Finally, test the transition matrix itself.
 	storm::storage::SparseMatrix<double>::const_iterator cIter = transitionMatrix.begin(0);
 
-	ASSERT_EQ(2, cIter->second);
+	ASSERT_EQ(2, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(1, cIter->second);
+	ASSERT_EQ(1, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(1, cIter->second);
+	ASSERT_EQ(1, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(2, cIter->second);
+	ASSERT_EQ(2, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(4, cIter->second);
+	ASSERT_EQ(4, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(8, cIter->second);
+	ASSERT_EQ(8, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(0.5, cIter->second);
+	ASSERT_EQ(0.5, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(0.5, cIter->second);
+	ASSERT_EQ(0.5, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(1, cIter->second);
+	ASSERT_EQ(1, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(0.5, cIter->second);
+	ASSERT_EQ(0.5, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(0.5, cIter->second);
+	ASSERT_EQ(0.5, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(1, cIter->second);
+	ASSERT_EQ(1, cIter->getValue());
 	cIter++;
 	ASSERT_EQ(transitionMatrix.end(), cIter);
 }
diff --git a/test/functional/parser/NondeterministicSparseTransitionParserTest.cpp b/test/functional/parser/NondeterministicSparseTransitionParserTest.cpp
index 21240a9dd..9494c983f 100644
--- a/test/functional/parser/NondeterministicSparseTransitionParserTest.cpp
+++ b/test/functional/parser/NondeterministicSparseTransitionParserTest.cpp
@@ -48,71 +48,71 @@ TEST(NondeterministicSparseTransitionParserTest, BasicTransitionsParsing) {
 	// Test every entry of the matrix.
 	storm::storage::SparseMatrix<double>::const_iterator cIter = result.begin(0);
 
-	ASSERT_EQ(0, cIter->first);
-	ASSERT_EQ(0.9, cIter->second);
+	ASSERT_EQ(0, cIter->getColumn());
+	ASSERT_EQ(0.9, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(1, cIter->first);
-	ASSERT_EQ(0.1, cIter->second);
+	ASSERT_EQ(1, cIter->getColumn());
+	ASSERT_EQ(0.1, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(1, cIter->first);
-	ASSERT_EQ(0.2, cIter->second);
+	ASSERT_EQ(1, cIter->getColumn());
+	ASSERT_EQ(0.2, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(2, cIter->first);
-	ASSERT_EQ(0.2, cIter->second);
+	ASSERT_EQ(2, cIter->getColumn());
+	ASSERT_EQ(0.2, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(3, cIter->first);
-	ASSERT_EQ(0.2, cIter->second);
+	ASSERT_EQ(3, cIter->getColumn());
+	ASSERT_EQ(0.2, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(4, cIter->first);
-	ASSERT_EQ(0.2, cIter->second);
+	ASSERT_EQ(4, cIter->getColumn());
+	ASSERT_EQ(0.2, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(5, cIter->first);
-	ASSERT_EQ(0.2, cIter->second);
+	ASSERT_EQ(5, cIter->getColumn());
+	ASSERT_EQ(0.2, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(5, cIter->first);
-	ASSERT_EQ(1, cIter->second);
+	ASSERT_EQ(5, cIter->getColumn());
+	ASSERT_EQ(1, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(0, cIter->first);
-	ASSERT_EQ(0.1, cIter->second);
+	ASSERT_EQ(0, cIter->getColumn());
+	ASSERT_EQ(0.1, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(4, cIter->first);
-	ASSERT_EQ(0.9, cIter->second);
+	ASSERT_EQ(4, cIter->getColumn());
+	ASSERT_EQ(0.9, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(2, cIter->first);
-	ASSERT_EQ(1, cIter->second);
+	ASSERT_EQ(2, cIter->getColumn());
+	ASSERT_EQ(1, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(2, cIter->first);
-	ASSERT_EQ(0.5, cIter->second);
+	ASSERT_EQ(2, cIter->getColumn());
+	ASSERT_EQ(0.5, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(3, cIter->first);
-	ASSERT_EQ(0.5, cIter->second);
+	ASSERT_EQ(3, cIter->getColumn());
+	ASSERT_EQ(0.5, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(2, cIter->first);
-	ASSERT_EQ(1, cIter->second);
+	ASSERT_EQ(2, cIter->getColumn());
+	ASSERT_EQ(1, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(2, cIter->first);
-	ASSERT_EQ(0.001, cIter->second);
+	ASSERT_EQ(2, cIter->getColumn());
+	ASSERT_EQ(0.001, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(3, cIter->first);
-	ASSERT_EQ(0.999, cIter->second);
+	ASSERT_EQ(3, cIter->getColumn());
+	ASSERT_EQ(0.999, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(1, cIter->first);
-	ASSERT_EQ(0.7, cIter->second);
+	ASSERT_EQ(1, cIter->getColumn());
+	ASSERT_EQ(0.7, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(4, cIter->first);
-	ASSERT_EQ(0.3, cIter->second);
+	ASSERT_EQ(4, cIter->getColumn());
+	ASSERT_EQ(0.3, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(1, cIter->first);
-	ASSERT_EQ(0.2, cIter->second);
+	ASSERT_EQ(1, cIter->getColumn());
+	ASSERT_EQ(0.2, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(4, cIter->first);
-	ASSERT_EQ(0.2, cIter->second);
+	ASSERT_EQ(4, cIter->getColumn());
+	ASSERT_EQ(0.2, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(5, cIter->first);
-	ASSERT_EQ(0.6, cIter->second);
+	ASSERT_EQ(5, cIter->getColumn());
+	ASSERT_EQ(0.6, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(5, cIter->first);
-	ASSERT_EQ(1, cIter->second);
+	ASSERT_EQ(5, cIter->getColumn());
+	ASSERT_EQ(1, cIter->getValue());
 }
 
 TEST(NondeterministicSparseTransitionParserTest, BasicTransitionsRewardsParsing) {
@@ -128,56 +128,56 @@ TEST(NondeterministicSparseTransitionParserTest, BasicTransitionsRewardsParsing)
 	// Test every entry of the matrix.
 	storm::storage::SparseMatrix<double>::const_iterator cIter = result.begin(0);
 
-	ASSERT_EQ(0, cIter->first);
-	ASSERT_EQ(1, cIter->second);
+	ASSERT_EQ(0, cIter->getColumn());
+	ASSERT_EQ(1, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(1, cIter->first);
-	ASSERT_EQ(30, cIter->second);
+	ASSERT_EQ(1, cIter->getColumn());
+	ASSERT_EQ(30, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(1, cIter->first);
-	ASSERT_EQ(15.2, cIter->second);
+	ASSERT_EQ(1, cIter->getColumn());
+	ASSERT_EQ(15.2, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(2, cIter->first);
-	ASSERT_EQ(75, cIter->second);
+	ASSERT_EQ(2, cIter->getColumn());
+	ASSERT_EQ(75, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(5, cIter->first);
-	ASSERT_EQ(2.45, cIter->second);
+	ASSERT_EQ(5, cIter->getColumn());
+	ASSERT_EQ(2.45, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(5, cIter->first);
-	ASSERT_EQ(1, cIter->second);
+	ASSERT_EQ(5, cIter->getColumn());
+	ASSERT_EQ(1, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(0, cIter->first);
-	ASSERT_EQ(0.114, cIter->second);
+	ASSERT_EQ(0, cIter->getColumn());
+	ASSERT_EQ(0.114, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(4, cIter->first);
-	ASSERT_EQ(90, cIter->second);
+	ASSERT_EQ(4, cIter->getColumn());
+	ASSERT_EQ(90, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(2, cIter->first);
-	ASSERT_EQ(1, cIter->second);
+	ASSERT_EQ(2, cIter->getColumn());
+	ASSERT_EQ(1, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(2, cIter->first);
-	ASSERT_EQ(55, cIter->second);
+	ASSERT_EQ(2, cIter->getColumn());
+	ASSERT_EQ(55, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(3, cIter->first);
-	ASSERT_EQ(87, cIter->second);
+	ASSERT_EQ(3, cIter->getColumn());
+	ASSERT_EQ(87, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(2, cIter->first);
-	ASSERT_EQ(13, cIter->second);
+	ASSERT_EQ(2, cIter->getColumn());
+	ASSERT_EQ(13, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(3, cIter->first);
-	ASSERT_EQ(999, cIter->second);
+	ASSERT_EQ(3, cIter->getColumn());
+	ASSERT_EQ(999, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(1, cIter->first);
-	ASSERT_EQ(0.7, cIter->second);
+	ASSERT_EQ(1, cIter->getColumn());
+	ASSERT_EQ(0.7, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(4, cIter->first);
-	ASSERT_EQ(0.3, cIter->second);
+	ASSERT_EQ(4, cIter->getColumn());
+	ASSERT_EQ(0.3, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(1, cIter->first);
-	ASSERT_EQ(0.1, cIter->second);
+	ASSERT_EQ(1, cIter->getColumn());
+	ASSERT_EQ(0.1, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(5, cIter->first);
-	ASSERT_EQ(6, cIter->second);
+	ASSERT_EQ(5, cIter->getColumn());
+	ASSERT_EQ(6, cIter->getValue());
 }
 
 TEST(NondeterministicSparseTransitionParserTest, Whitespaces) {
@@ -224,26 +224,26 @@ TEST(NondeterministicSparseTransitionParserTest, FixDeadlocks) {
 
 	storm::storage::SparseMatrix<double>::const_iterator cIter = result.begin(8);
 
-	ASSERT_EQ(1, cIter->first);
-	ASSERT_EQ(0.7, cIter->second);
+	ASSERT_EQ(1, cIter->getColumn());
+	ASSERT_EQ(0.7, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(4, cIter->first);
-	ASSERT_EQ(0.3, cIter->second);
+	ASSERT_EQ(4, cIter->getColumn());
+	ASSERT_EQ(0.3, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(5, cIter->first);
-	ASSERT_EQ(1, cIter->second);
+	ASSERT_EQ(5, cIter->getColumn());
+	ASSERT_EQ(1, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(1, cIter->first);
-	ASSERT_EQ(0.2, cIter->second);
+	ASSERT_EQ(1, cIter->getColumn());
+	ASSERT_EQ(0.2, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(4, cIter->first);
-	ASSERT_EQ(0.2, cIter->second);
+	ASSERT_EQ(4, cIter->getColumn());
+	ASSERT_EQ(0.2, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(5, cIter->first);
-	ASSERT_EQ(0.6, cIter->second);
+	ASSERT_EQ(5, cIter->getColumn());
+	ASSERT_EQ(0.6, cIter->getValue());
 	cIter++;
-	ASSERT_EQ(5, cIter->first);
-	ASSERT_EQ(1, cIter->second);
+	ASSERT_EQ(5, cIter->getColumn());
+	ASSERT_EQ(1, cIter->getValue());
 
 }
 
diff --git a/test/functional/solver/GlpkLpSolverTest.cpp b/test/functional/solver/GlpkLpSolverTest.cpp
index 30c3e10f9..a6410611d 100644
--- a/test/functional/solver/GlpkLpSolverTest.cpp
+++ b/test/functional/solver/GlpkLpSolverTest.cpp
@@ -3,34 +3,34 @@
 
 #include "src/solver/GlpkLpSolver.h"
 #include "src/exceptions/InvalidStateException.h"
+#include "src/exceptions/InvalidAccessException.h"
 #include "src/settings/Settings.h"
 
 TEST(GlpkLpSolver, LPOptimizeMax) {
 #ifdef STORM_HAVE_GLPK
-    storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::MAXIMIZE);
-    uint_fast64_t xIndex;
-    ASSERT_NO_THROW(xIndex = solver.createContinuousVariable("x", storm::solver::LpSolver::VariableType::BOUNDED, 0, 1, -1));
-    uint_fast64_t yIndex;
-    ASSERT_NO_THROW(yIndex = solver.createContinuousVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2));
-    uint_fast64_t zIndex;
-    ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 1));
-    
-    ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, 1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12));
-    ASSERT_NO_THROW(solver.addConstraint("", {yIndex, zIndex, xIndex}, {0.5, 1, -1}, storm::solver::LpSolver::BoundType::EQUAL, 5));
-    ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5));
-    
+    storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::ModelSense::Maximize);
+    ASSERT_NO_THROW(solver.addBoundedContinuousVariable("x", 0, 1, -1));
+    ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("y", 0, 2));
+    ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("z", 0, 1));
+    ASSERT_NO_THROW(solver.update());
+
+    ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12)));
+    ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleLiteral(0.5) * storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") - storm::expressions::Expression::createDoubleVariable("x") == storm::expressions::Expression::createDoubleLiteral(5)));
+    ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5)));
+    ASSERT_NO_THROW(solver.update());
+
     ASSERT_NO_THROW(solver.optimize());
     ASSERT_TRUE(solver.isOptimal());
     ASSERT_FALSE(solver.isUnbounded());
     ASSERT_FALSE(solver.isInfeasible());
     double xValue = 0;
-    ASSERT_NO_THROW(xValue = solver.getContinuousValue(xIndex));
+    ASSERT_NO_THROW(xValue = solver.getContinuousValue("x"));
     ASSERT_LT(std::abs(xValue - 1), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble());
     double yValue = 0;
-    ASSERT_NO_THROW(yValue = solver.getContinuousValue(yIndex));
+    ASSERT_NO_THROW(yValue = solver.getContinuousValue("y"));
     ASSERT_LT(std::abs(yValue - 6.5), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble());
     double zValue = 0;
-    ASSERT_NO_THROW(zValue = solver.getContinuousValue(zIndex));
+    ASSERT_NO_THROW(zValue = solver.getContinuousValue("z"));
     ASSERT_LT(std::abs(zValue - 2.75), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble());
     double objectiveValue = 0;
     ASSERT_NO_THROW(objectiveValue = solver.getObjectiveValue());
@@ -42,30 +42,29 @@ TEST(GlpkLpSolver, LPOptimizeMax) {
 
 TEST(GlpkLpSolver, LPOptimizeMin) {
 #ifdef STORM_HAVE_GLPK
-    storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::MINIMIZE);
-    uint_fast64_t xIndex;
-    ASSERT_NO_THROW(xIndex = solver.createContinuousVariable("x", storm::solver::LpSolver::VariableType::BOUNDED, 0, 1, -1));
-    uint_fast64_t yIndex;
-    ASSERT_NO_THROW(yIndex = solver.createContinuousVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2));
-    uint_fast64_t zIndex;
-    ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::BOUNDED, 1, 5.7, -1));
-    
-    ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, 1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12));
-    ASSERT_NO_THROW(solver.addConstraint("", {yIndex, zIndex, xIndex}, {0.5, 1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5));
-    ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5));
-    
+    storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::ModelSense::Minimize);
+    ASSERT_NO_THROW(solver.addBoundedContinuousVariable("x", 0, 1, -1));
+    ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("y", 0, 2));
+    ASSERT_NO_THROW(solver.addBoundedContinuousVariable("z", 1, 5.7, -1));
+    ASSERT_NO_THROW(solver.update());
+
+    ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12)));
+    ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleLiteral(0.5) * storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5)));
+    ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5)));
+    ASSERT_NO_THROW(solver.update());
+
     ASSERT_NO_THROW(solver.optimize());
     ASSERT_TRUE(solver.isOptimal());
     ASSERT_FALSE(solver.isUnbounded());
     ASSERT_FALSE(solver.isInfeasible());
     double xValue = 0;
-    ASSERT_NO_THROW(xValue = solver.getContinuousValue(xIndex));
+    ASSERT_NO_THROW(xValue = solver.getContinuousValue("x"));
     ASSERT_LT(std::abs(xValue - 1), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble());
     double yValue = 0;
-    ASSERT_NO_THROW(yValue = solver.getContinuousValue(yIndex));
+    ASSERT_NO_THROW(yValue = solver.getContinuousValue("y"));
     ASSERT_LT(std::abs(yValue - 0), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble());
     double zValue = 0;
-    ASSERT_NO_THROW(zValue = solver.getContinuousValue(zIndex));
+    ASSERT_NO_THROW(zValue = solver.getContinuousValue("z"));
     ASSERT_LT(std::abs(zValue - 5.7), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble());
     double objectiveValue = 0;
     ASSERT_NO_THROW(objectiveValue = solver.getObjectiveValue());
@@ -77,30 +76,29 @@ TEST(GlpkLpSolver, LPOptimizeMin) {
 
 TEST(GlpkLpSolver, MILPOptimizeMax) {
 #ifdef STORM_HAVE_GLPK
-    storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::MAXIMIZE);
-    uint_fast64_t xIndex;
-    ASSERT_NO_THROW(xIndex = solver.createBinaryVariable("x", -1));
-    uint_fast64_t yIndex;
-    ASSERT_NO_THROW(yIndex = solver.createIntegerVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2));
-    uint_fast64_t zIndex;
-    ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 1));
-    
-    ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, 1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12));
-    ASSERT_NO_THROW(solver.addConstraint("", {yIndex, zIndex, xIndex}, {0.5, 1, -1}, storm::solver::LpSolver::BoundType::EQUAL, 5));
-    ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5));
-    
+    storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::ModelSense::Maximize);
+    ASSERT_NO_THROW(solver.addBinaryVariable("x", -1));
+    ASSERT_NO_THROW(solver.addLowerBoundedIntegerVariable("y", 0, 2));
+    ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("z", 0, 1));
+    ASSERT_NO_THROW(solver.update());
+
+    ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12)));
+    ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleLiteral(0.5) * storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") - storm::expressions::Expression::createDoubleVariable("x") == storm::expressions::Expression::createDoubleLiteral(5)));
+    ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5)));
+    ASSERT_NO_THROW(solver.update());
+
     ASSERT_NO_THROW(solver.optimize());
     ASSERT_TRUE(solver.isOptimal());
     ASSERT_FALSE(solver.isUnbounded());
     ASSERT_FALSE(solver.isInfeasible());
     bool xValue = false;
-    ASSERT_NO_THROW(xValue = solver.getBinaryValue(xIndex));
+    ASSERT_NO_THROW(xValue = solver.getBinaryValue("x"));
     ASSERT_EQ(true, xValue);
     int_fast64_t yValue = 0;
-    ASSERT_NO_THROW(yValue = solver.getIntegerValue(yIndex));
+    ASSERT_NO_THROW(yValue = solver.getIntegerValue("y"));
     ASSERT_EQ(6, yValue);
     double zValue = 0;
-    ASSERT_NO_THROW(zValue = solver.getContinuousValue(zIndex));
+    ASSERT_NO_THROW(zValue = solver.getContinuousValue("z"));
     ASSERT_LT(std::abs(zValue - 3), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble());
     double objectiveValue = 0;
     ASSERT_NO_THROW(objectiveValue = solver.getObjectiveValue());
@@ -112,30 +110,29 @@ TEST(GlpkLpSolver, MILPOptimizeMax) {
 
 TEST(GlpkLpSolver, MILPOptimizeMin) {
 #ifdef STORM_HAVE_GLPK
-    storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::MINIMIZE);
-    uint_fast64_t xIndex;
-    ASSERT_NO_THROW(xIndex = solver.createBinaryVariable("x", -1));
-    uint_fast64_t yIndex;
-    ASSERT_NO_THROW(yIndex = solver.createContinuousVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2));
-    uint_fast64_t zIndex;
-    ASSERT_NO_THROW(zIndex = solver.createIntegerVariable("z", storm::solver::LpSolver::VariableType::BOUNDED, 0, 5, -1));
-    
-    ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, 1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12));
-    ASSERT_NO_THROW(solver.addConstraint("", {yIndex, zIndex, xIndex}, {0.5, 1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5));
-    ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5));
-    
+    storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::ModelSense::Minimize);
+    ASSERT_NO_THROW(solver.addBinaryVariable("x", -1));
+    ASSERT_NO_THROW(solver.addLowerBoundedIntegerVariable("y", 0, 2));
+    ASSERT_NO_THROW(solver.addBoundedContinuousVariable("z", 0, 5, -1));
+    ASSERT_NO_THROW(solver.update());
+
+    ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12)));
+    ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleLiteral(0.5) * storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5)));
+    ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5)));
+    ASSERT_NO_THROW(solver.update());
+
     ASSERT_NO_THROW(solver.optimize());
     ASSERT_TRUE(solver.isOptimal());
     ASSERT_FALSE(solver.isUnbounded());
     ASSERT_FALSE(solver.isInfeasible());
     bool xValue = false;
-    ASSERT_NO_THROW(xValue = solver.getBinaryValue(xIndex));
+    ASSERT_NO_THROW(xValue = solver.getBinaryValue("x"));
     ASSERT_EQ(true, xValue);
     int_fast64_t yValue = 0;
-    ASSERT_NO_THROW(yValue = solver.getIntegerValue(yIndex));
+    ASSERT_NO_THROW(yValue = solver.getIntegerValue("y"));
     ASSERT_EQ(0, yValue);
     double zValue = 0;
-    ASSERT_NO_THROW(zValue = solver.getContinuousValue(zIndex));
+    ASSERT_NO_THROW(zValue = solver.getContinuousValue("z"));
     ASSERT_LT(std::abs(zValue - 5), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble());
     double objectiveValue = 0;
     ASSERT_NO_THROW(objectiveValue = solver.getObjectiveValue());
@@ -147,31 +144,30 @@ TEST(GlpkLpSolver, MILPOptimizeMin) {
 
 TEST(GlpkLpSolver, LPInfeasible) {
 #ifdef STORM_HAVE_GLPK
-    storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::MAXIMIZE);
-    uint_fast64_t xIndex;
-    ASSERT_NO_THROW(xIndex = solver.createContinuousVariable("x", storm::solver::LpSolver::VariableType::BOUNDED, 0, 1, -1));
-    uint_fast64_t yIndex;
-    ASSERT_NO_THROW(yIndex = solver.createContinuousVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2));
-    uint_fast64_t zIndex;
-    ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 1));
-    
-    ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, 1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12));
-    ASSERT_NO_THROW(solver.addConstraint("", {yIndex, zIndex, xIndex}, {0.5, 1, -1}, storm::solver::LpSolver::BoundType::EQUAL, 5));
-    ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5));
-    ASSERT_NO_THROW(solver.addConstraint("", {yIndex}, {1}, storm::solver::LpSolver::BoundType::GREATER_EQUAL, 7));
-    
+    storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::ModelSense::Maximize);
+    ASSERT_NO_THROW(solver.addBoundedContinuousVariable("x", 0, 1, -1));
+    ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("y", 0, 2));
+    ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("z", 0, 1));
+    ASSERT_NO_THROW(solver.update());
+
+    ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12)));
+    ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleLiteral(0.5) * storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") - storm::expressions::Expression::createDoubleVariable("x") == storm::expressions::Expression::createDoubleLiteral(5)));
+    ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5)));
+    ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") > storm::expressions::Expression::createDoubleLiteral(7)));
+    ASSERT_NO_THROW(solver.update());
+
     ASSERT_NO_THROW(solver.optimize());
     ASSERT_FALSE(solver.isOptimal());
     ASSERT_FALSE(solver.isUnbounded());
     ASSERT_TRUE(solver.isInfeasible());
     double xValue = 0;
-    ASSERT_THROW(xValue = solver.getContinuousValue(xIndex), storm::exceptions::InvalidStateException);
+    ASSERT_THROW(xValue = solver.getContinuousValue("x"), storm::exceptions::InvalidAccessException);
     double yValue = 0;
-    ASSERT_THROW(yValue = solver.getContinuousValue(yIndex), storm::exceptions::InvalidStateException);
+    ASSERT_THROW(yValue = solver.getContinuousValue("y"), storm::exceptions::InvalidAccessException);
     double zValue = 0;
-    ASSERT_THROW(zValue = solver.getContinuousValue(zIndex), storm::exceptions::InvalidStateException);
+    ASSERT_THROW(zValue = solver.getContinuousValue("z"), storm::exceptions::InvalidAccessException);
     double objectiveValue = 0;
-    ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidStateException);
+    ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidAccessException);
 #else
     ASSERT_TRUE(false) << "StoRM built without glpk support.";
 #endif
@@ -179,31 +175,30 @@ TEST(GlpkLpSolver, LPInfeasible) {
 
 TEST(GlpkLpSolver, MILPInfeasible) {
 #ifdef STORM_HAVE_GLPK
-    storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::MAXIMIZE);
-    uint_fast64_t xIndex;
-    ASSERT_NO_THROW(xIndex = solver.createBinaryVariable("x", -1));
-    uint_fast64_t yIndex;
-    ASSERT_NO_THROW(yIndex = solver.createIntegerVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2));
-    uint_fast64_t zIndex;
-    ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 1));
-    
-    ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, 1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12));
-    ASSERT_NO_THROW(solver.addConstraint("", {yIndex, zIndex, xIndex}, {0.5, 1, -1}, storm::solver::LpSolver::BoundType::EQUAL, 5));
-    ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5));
-    ASSERT_NO_THROW(solver.addConstraint("", {yIndex}, {1}, storm::solver::LpSolver::BoundType::GREATER_EQUAL, 7));
-    
+    storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::ModelSense::Maximize);
+    ASSERT_NO_THROW(solver.addBinaryVariable("x", -1));
+    ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("y", 0, 2));
+    ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("z", 0, 1));
+    ASSERT_NO_THROW(solver.update());
+
+    ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12)));
+    ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleLiteral(0.5) * storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") - storm::expressions::Expression::createDoubleVariable("x") == storm::expressions::Expression::createDoubleLiteral(5)));
+    ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5)));
+    ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") > storm::expressions::Expression::createDoubleLiteral(7)));
+    ASSERT_NO_THROW(solver.update());
+
     ASSERT_NO_THROW(solver.optimize());
     ASSERT_FALSE(solver.isOptimal());
     ASSERT_FALSE(solver.isUnbounded());
     ASSERT_TRUE(solver.isInfeasible());
     bool xValue = false;
-    ASSERT_THROW(xValue = solver.getBinaryValue(xIndex), storm::exceptions::InvalidStateException);
+    ASSERT_THROW(xValue = solver.getBinaryValue("x"), storm::exceptions::InvalidAccessException);
     int_fast64_t yValue = 0;
-    ASSERT_THROW(yValue = solver.getIntegerValue(yIndex), storm::exceptions::InvalidStateException);
+    ASSERT_THROW(yValue = solver.getIntegerValue("y"), storm::exceptions::InvalidAccessException);
     double zValue = 0;
-    ASSERT_THROW(zValue = solver.getContinuousValue(zIndex), storm::exceptions::InvalidStateException);
+    ASSERT_THROW(zValue = solver.getContinuousValue("z"), storm::exceptions::InvalidAccessException);
     double objectiveValue = 0;
-    ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidStateException);
+    ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidAccessException);
 #else
     ASSERT_TRUE(false) << "StoRM built without glpk support.";
 #endif
@@ -211,29 +206,28 @@ TEST(GlpkLpSolver, MILPInfeasible) {
 
 TEST(GlpkLpSolver, LPUnbounded) {
 #ifdef STORM_HAVE_GLPK
-    storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::MAXIMIZE);
-    uint_fast64_t xIndex;
-    ASSERT_NO_THROW(xIndex = solver.createContinuousVariable("x", storm::solver::LpSolver::VariableType::BOUNDED, 0, 1, -1));
-    uint_fast64_t yIndex;
-    ASSERT_NO_THROW(yIndex = solver.createContinuousVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2));
-    uint_fast64_t zIndex;
-    ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 1));
-    
-    ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12));
-    ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5));
-    
+    storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::ModelSense::Maximize);
+    ASSERT_NO_THROW(solver.addBoundedContinuousVariable("x", 0, 1, -1));
+    ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("y", 0, 2));
+    ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("z", 0, 1));
+    ASSERT_NO_THROW(solver.update());
+
+    ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12)));
+    ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5)));
+    ASSERT_NO_THROW(solver.update());
+
     ASSERT_NO_THROW(solver.optimize());
     ASSERT_FALSE(solver.isOptimal());
     ASSERT_TRUE(solver.isUnbounded());
     ASSERT_FALSE(solver.isInfeasible());
     double xValue = 0;
-    ASSERT_THROW(xValue = solver.getContinuousValue(xIndex), storm::exceptions::InvalidStateException);
+    ASSERT_THROW(xValue = solver.getContinuousValue("x"), storm::exceptions::InvalidAccessException);
     double yValue = 0;
-    ASSERT_THROW(yValue = solver.getContinuousValue(yIndex), storm::exceptions::InvalidStateException);
+    ASSERT_THROW(yValue = solver.getContinuousValue("y"), storm::exceptions::InvalidAccessException);
     double zValue = 0;
-    ASSERT_THROW(zValue = solver.getContinuousValue(zIndex), storm::exceptions::InvalidStateException);
+    ASSERT_THROW(zValue = solver.getContinuousValue("z"), storm::exceptions::InvalidAccessException);
     double objectiveValue = 0;
-    ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidStateException);
+    ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidAccessException);
 #else
     ASSERT_TRUE(false) << "StoRM built without glpk support.";
 #endif
@@ -241,30 +235,29 @@ TEST(GlpkLpSolver, LPUnbounded) {
 
 TEST(GlpkLpSolver, MILPUnbounded) {
 #ifdef STORM_HAVE_GLPK
-    storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::MAXIMIZE);
-    uint_fast64_t xIndex;
-    ASSERT_NO_THROW(xIndex = solver.createBinaryVariable("x", -1));
-    uint_fast64_t yIndex;
-    ASSERT_NO_THROW(yIndex = solver.createIntegerVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2));
-    uint_fast64_t zIndex;
-    ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 1));
-    
-    ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12));
-    ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5));
-    
+    storm::solver::GlpkLpSolver solver(storm::solver::LpSolver::ModelSense::Maximize);
+    ASSERT_NO_THROW(solver.addBinaryVariable("x", -1));
+    ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("y", 0, 2));
+    ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("z", 0, 1));
+    ASSERT_NO_THROW(solver.update());
+
+    ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12)));
+    ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5)));
+    ASSERT_NO_THROW(solver.update());
+
     ASSERT_NO_THROW(solver.optimize());
     ASSERT_FALSE(solver.isOptimal());
     ASSERT_TRUE(solver.isUnbounded());
     ASSERT_FALSE(solver.isInfeasible());
     bool xValue = false;
-    ASSERT_THROW(xValue = solver.getBinaryValue(xIndex), storm::exceptions::InvalidStateException);
+    ASSERT_THROW(xValue = solver.getBinaryValue("x"), storm::exceptions::InvalidAccessException);
     int_fast64_t yValue = 0;
-    ASSERT_THROW(yValue = solver.getIntegerValue(yIndex), storm::exceptions::InvalidStateException);
+    ASSERT_THROW(yValue = solver.getIntegerValue("y"), storm::exceptions::InvalidAccessException);
     double zValue = 0;
-    ASSERT_THROW(zValue = solver.getContinuousValue(zIndex), storm::exceptions::InvalidStateException);
+    ASSERT_THROW(zValue = solver.getContinuousValue("z"), storm::exceptions::InvalidAccessException);
     double objectiveValue = 0;
-    ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidStateException);
+    ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidAccessException);
 #else
     ASSERT_TRUE(false) << "StoRM built without glpk support.";
 #endif
-}
\ No newline at end of file
+}
diff --git a/test/functional/solver/GurobiLpSolverTest.cpp b/test/functional/solver/GurobiLpSolverTest.cpp
index 1ca524d8e..ee0ebfa03 100644
--- a/test/functional/solver/GurobiLpSolverTest.cpp
+++ b/test/functional/solver/GurobiLpSolverTest.cpp
@@ -3,36 +3,34 @@
 
 #include "src/solver/GurobiLpSolver.h"
 #include "src/exceptions/InvalidStateException.h"
+#include "src/exceptions/InvalidAccessException.h"
 #include "src/settings/Settings.h"
 
 TEST(GurobiLpSolver, LPOptimizeMax) {
 #ifdef STORM_HAVE_GUROBI
-    storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::MAXIMIZE);
-    uint_fast64_t xIndex;
-    ASSERT_NO_THROW(xIndex = solver.createContinuousVariable("x", storm::solver::LpSolver::VariableType::BOUNDED, 0, 1, -1));
-    uint_fast64_t yIndex;
-    ASSERT_NO_THROW(yIndex = solver.createContinuousVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2));
-    uint_fast64_t zIndex;
-    ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 1));
-
-	ASSERT_NO_THROW(solver.update());
-
-    ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, 1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12));
-    ASSERT_NO_THROW(solver.addConstraint("", {yIndex, zIndex, xIndex}, {0.5, 1, -1}, storm::solver::LpSolver::BoundType::EQUAL, 5));
-    ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5));
+    storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::ModelSense::Maximize);
+    ASSERT_NO_THROW(solver.addBoundedContinuousVariable("x", 0, 1, -1));
+    ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("y", 0, 2));
+    ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("z", 0, 1));
+    ASSERT_NO_THROW(solver.update());
     
+    ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12)));
+    ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleLiteral(0.5) * storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") - storm::expressions::Expression::createDoubleVariable("x") == storm::expressions::Expression::createDoubleLiteral(5)));
+    ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5)));
+    ASSERT_NO_THROW(solver.update());
+
     ASSERT_NO_THROW(solver.optimize());
     ASSERT_TRUE(solver.isOptimal());
     ASSERT_FALSE(solver.isUnbounded());
     ASSERT_FALSE(solver.isInfeasible());
     double xValue = 0;
-    ASSERT_NO_THROW(xValue = solver.getContinuousValue(xIndex));
+    ASSERT_NO_THROW(xValue = solver.getContinuousValue("x"));
     ASSERT_LT(std::abs(xValue - 1), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble());
     double yValue = 0;
-    ASSERT_NO_THROW(yValue = solver.getContinuousValue(yIndex));
+    ASSERT_NO_THROW(yValue = solver.getContinuousValue("y"));
     ASSERT_LT(std::abs(yValue - 6.5), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble());
     double zValue = 0;
-    ASSERT_NO_THROW(zValue = solver.getContinuousValue(zIndex));
+    ASSERT_NO_THROW(zValue = solver.getContinuousValue("z"));
     ASSERT_LT(std::abs(zValue - 2.75), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble());
     double objectiveValue = 0;
     ASSERT_NO_THROW(objectiveValue = solver.getObjectiveValue());
@@ -44,32 +42,29 @@ TEST(GurobiLpSolver, LPOptimizeMax) {
 
 TEST(GurobiLpSolver, LPOptimizeMin) {
 #ifdef STORM_HAVE_GUROBI
-    storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::MINIMIZE);
-    uint_fast64_t xIndex;
-    ASSERT_NO_THROW(xIndex = solver.createContinuousVariable("x", storm::solver::LpSolver::VariableType::BOUNDED, 0, 1, -1));
-    uint_fast64_t yIndex;
-    ASSERT_NO_THROW(yIndex = solver.createContinuousVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2));
-    uint_fast64_t zIndex;
-    ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::BOUNDED, 1, 5.7, -1));
-    
-	ASSERT_NO_THROW(solver.update());
+    storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::ModelSense::Minimize);
+    ASSERT_NO_THROW(solver.addBoundedContinuousVariable("x", 0, 1, -1));
+    ASSERT_NO_THROW(solver.addLowerBoundedIntegerVariable("y", 0, 2));
+    ASSERT_NO_THROW(solver.addBoundedContinuousVariable("z", 1, 5.7, -1));
+    ASSERT_NO_THROW(solver.update());
+
+    ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12)));
+    ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleLiteral(0.5) * storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5)));
+    ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5)));
+    ASSERT_NO_THROW(solver.update());
 
-    ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, 1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12));
-    ASSERT_NO_THROW(solver.addConstraint("", {yIndex, zIndex, xIndex}, {0.5, 1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5));
-    ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5));
-    
     ASSERT_NO_THROW(solver.optimize());
     ASSERT_TRUE(solver.isOptimal());
     ASSERT_FALSE(solver.isUnbounded());
     ASSERT_FALSE(solver.isInfeasible());
     double xValue = 0;
-    ASSERT_NO_THROW(xValue = solver.getContinuousValue(xIndex));
+    ASSERT_NO_THROW(xValue = solver.getContinuousValue("x"));
     ASSERT_LT(std::abs(xValue - 1), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble());
     double yValue = 0;
-    ASSERT_NO_THROW(yValue = solver.getContinuousValue(yIndex));
+    ASSERT_NO_THROW(yValue = solver.getContinuousValue("y"));
     ASSERT_LT(std::abs(yValue - 0), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble());
     double zValue = 0;
-    ASSERT_NO_THROW(zValue = solver.getContinuousValue(zIndex));
+    ASSERT_NO_THROW(zValue = solver.getContinuousValue("z"));
     ASSERT_LT(std::abs(zValue - 5.7), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble());
     double objectiveValue = 0;
     ASSERT_NO_THROW(objectiveValue = solver.getObjectiveValue());
@@ -81,32 +76,29 @@ TEST(GurobiLpSolver, LPOptimizeMin) {
 
 TEST(GurobiLpSolver, MILPOptimizeMax) {
 #ifdef STORM_HAVE_GUROBI
-    storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::MAXIMIZE);
-    uint_fast64_t xIndex;
-    ASSERT_NO_THROW(xIndex = solver.createBinaryVariable("x", -1));
-    uint_fast64_t yIndex;
-    ASSERT_NO_THROW(yIndex = solver.createIntegerVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2));
-    uint_fast64_t zIndex;
-    ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 1));
-    
-	ASSERT_NO_THROW(solver.update());
+    storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::ModelSense::Maximize);
+    ASSERT_NO_THROW(solver.addBinaryVariable("x", -1));
+    ASSERT_NO_THROW(solver.addLowerBoundedIntegerVariable("y", 0, 2));
+    ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("z", 0, 1));
+    ASSERT_NO_THROW(solver.update());
+
+    ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12)));
+    ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleLiteral(0.5) * storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") - storm::expressions::Expression::createDoubleVariable("x") == storm::expressions::Expression::createDoubleLiteral(5)));
+    ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5)));
+    ASSERT_NO_THROW(solver.update());
 
-    ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, 1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12));
-    ASSERT_NO_THROW(solver.addConstraint("", {yIndex, zIndex, xIndex}, {0.5, 1, -1}, storm::solver::LpSolver::BoundType::EQUAL, 5));
-    ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5));
-    
     ASSERT_NO_THROW(solver.optimize());
     ASSERT_TRUE(solver.isOptimal());
     ASSERT_FALSE(solver.isUnbounded());
     ASSERT_FALSE(solver.isInfeasible());
     bool xValue = false;
-    ASSERT_NO_THROW(xValue = solver.getBinaryValue(xIndex));
+    ASSERT_NO_THROW(xValue = solver.getBinaryValue("x"));
     ASSERT_EQ(true, xValue);
     int_fast64_t yValue = 0;
-    ASSERT_NO_THROW(yValue = solver.getIntegerValue(yIndex));
+    ASSERT_NO_THROW(yValue = solver.getIntegerValue("y"));
     ASSERT_EQ(6, yValue);
     double zValue = 0;
-    ASSERT_NO_THROW(zValue = solver.getContinuousValue(zIndex));
+    ASSERT_NO_THROW(zValue = solver.getContinuousValue("z"));
     ASSERT_LT(std::abs(zValue - 3), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble());
     double objectiveValue = 0;
     ASSERT_NO_THROW(objectiveValue = solver.getObjectiveValue());
@@ -118,32 +110,29 @@ TEST(GurobiLpSolver, MILPOptimizeMax) {
 
 TEST(GurobiLpSolver, MILPOptimizeMin) {
 #ifdef STORM_HAVE_GUROBI
-    storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::MINIMIZE);
-    uint_fast64_t xIndex;
-    ASSERT_NO_THROW(xIndex = solver.createBinaryVariable("x", -1));
-    uint_fast64_t yIndex;
-    ASSERT_NO_THROW(yIndex = solver.createContinuousVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2));
-    uint_fast64_t zIndex;
-    ASSERT_NO_THROW(zIndex = solver.createIntegerVariable("z", storm::solver::LpSolver::VariableType::BOUNDED, 0, 5.7, -1));
-    
-	ASSERT_NO_THROW(solver.update());
+    storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::ModelSense::Minimize);
+    ASSERT_NO_THROW(solver.addBinaryVariable("x", -1));
+    ASSERT_NO_THROW(solver.addLowerBoundedIntegerVariable("y", 0, 2));
+    ASSERT_NO_THROW(solver.addBoundedContinuousVariable("z", 0, 5, -1));
+    ASSERT_NO_THROW(solver.update());
+
+    ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12)));
+    ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleLiteral(0.5) * storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5)));
+    ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5)));
+    ASSERT_NO_THROW(solver.update());
 
-    ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, 1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12));
-    ASSERT_NO_THROW(solver.addConstraint("", {yIndex, zIndex, xIndex}, {0.5, 1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5));
-    ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5));
-    
     ASSERT_NO_THROW(solver.optimize());
     ASSERT_TRUE(solver.isOptimal());
     ASSERT_FALSE(solver.isUnbounded());
     ASSERT_FALSE(solver.isInfeasible());
     bool xValue = false;
-    ASSERT_NO_THROW(xValue = solver.getBinaryValue(xIndex));
+    ASSERT_NO_THROW(xValue = solver.getBinaryValue("x"));
     ASSERT_EQ(true, xValue);
     int_fast64_t yValue = 0;
-    ASSERT_NO_THROW(yValue = solver.getIntegerValue(yIndex));
+    ASSERT_NO_THROW(yValue = solver.getIntegerValue("y"));
     ASSERT_EQ(0, yValue);
     double zValue = 0;
-    ASSERT_NO_THROW(zValue = solver.getContinuousValue(zIndex));
+    ASSERT_NO_THROW(zValue = solver.getContinuousValue("z"));
     ASSERT_LT(std::abs(zValue - 5), storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble());
     double objectiveValue = 0;
     ASSERT_NO_THROW(objectiveValue = solver.getObjectiveValue());
@@ -155,33 +144,30 @@ TEST(GurobiLpSolver, MILPOptimizeMin) {
 
 TEST(GurobiLpSolver, LPInfeasible) {
 #ifdef STORM_HAVE_GUROBI
-    storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::MAXIMIZE);
-    uint_fast64_t xIndex;
-    ASSERT_NO_THROW(xIndex = solver.createContinuousVariable("x", storm::solver::LpSolver::VariableType::BOUNDED, 0, 1, -1));
-    uint_fast64_t yIndex;
-    ASSERT_NO_THROW(yIndex = solver.createContinuousVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2));
-    uint_fast64_t zIndex;
-    ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 1));
-    
-	ASSERT_NO_THROW(solver.update());
+    storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::ModelSense::Maximize);
+    ASSERT_NO_THROW(solver.addBoundedContinuousVariable("x", 0, 1, -1));
+    ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("y", 0, 2));
+    ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("z", 0, 1));
+    ASSERT_NO_THROW(solver.update());
+
+    ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12)));
+    ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleLiteral(0.5) * storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") - storm::expressions::Expression::createDoubleVariable("x") == storm::expressions::Expression::createDoubleLiteral(5)));
+    ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5)));
+    ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") > storm::expressions::Expression::createDoubleLiteral(7)));
+    ASSERT_NO_THROW(solver.update());
 
-    ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, 1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12));
-    ASSERT_NO_THROW(solver.addConstraint("", {yIndex, zIndex, xIndex}, {0.5, 1, -1}, storm::solver::LpSolver::BoundType::EQUAL, 5));
-    ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5));
-    ASSERT_NO_THROW(solver.addConstraint("", {yIndex}, {1}, storm::solver::LpSolver::BoundType::GREATER_EQUAL, 7));
-    
     ASSERT_NO_THROW(solver.optimize());
     ASSERT_FALSE(solver.isOptimal());
     ASSERT_FALSE(solver.isUnbounded());
     ASSERT_TRUE(solver.isInfeasible());
     double xValue = 0;
-    ASSERT_THROW(xValue = solver.getContinuousValue(xIndex), storm::exceptions::InvalidStateException);
+    ASSERT_THROW(xValue = solver.getContinuousValue("x"), storm::exceptions::InvalidAccessException);
     double yValue = 0;
-    ASSERT_THROW(yValue = solver.getContinuousValue(yIndex), storm::exceptions::InvalidStateException);
+    ASSERT_THROW(yValue = solver.getContinuousValue("y"), storm::exceptions::InvalidAccessException);
     double zValue = 0;
-    ASSERT_THROW(zValue = solver.getContinuousValue(zIndex), storm::exceptions::InvalidStateException);
+    ASSERT_THROW(zValue = solver.getContinuousValue("z"), storm::exceptions::InvalidAccessException);
     double objectiveValue = 0;
-    ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidStateException);
+    ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidAccessException);
 #else
     ASSERT_TRUE(false) << "StoRM built without Gurobi support.";
 #endif
@@ -189,33 +175,30 @@ TEST(GurobiLpSolver, LPInfeasible) {
 
 TEST(GurobiLpSolver, MILPInfeasible) {
 #ifdef STORM_HAVE_GUROBI
-    storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::MAXIMIZE);
-    uint_fast64_t xIndex;
-    ASSERT_NO_THROW(xIndex = solver.createBinaryVariable("x", -1));
-    uint_fast64_t yIndex;
-    ASSERT_NO_THROW(yIndex = solver.createIntegerVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2));
-    uint_fast64_t zIndex;
-    ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 1));
-    
-	ASSERT_NO_THROW(solver.update());
+    storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::ModelSense::Maximize);
+    ASSERT_NO_THROW(solver.addBinaryVariable("x", -1));
+    ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("y", 0, 2));
+    ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("z", 0, 1));
+    ASSERT_NO_THROW(solver.update());
+
+    ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12)));
+    ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleLiteral(0.5) * storm::expressions::Expression::createDoubleVariable("y") + storm::expressions::Expression::createDoubleVariable("z") - storm::expressions::Expression::createDoubleVariable("x") == storm::expressions::Expression::createDoubleLiteral(5)));
+    ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5)));
+    ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") > storm::expressions::Expression::createDoubleLiteral(7)));
+    ASSERT_NO_THROW(solver.update());
 
-    ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, 1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12));
-    ASSERT_NO_THROW(solver.addConstraint("", {yIndex, zIndex, xIndex}, {0.5, 1, -1}, storm::solver::LpSolver::BoundType::EQUAL, 5));
-    ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5));
-    ASSERT_NO_THROW(solver.addConstraint("", {yIndex}, {1}, storm::solver::LpSolver::BoundType::GREATER_EQUAL, 7));
-    
     ASSERT_NO_THROW(solver.optimize());
     ASSERT_FALSE(solver.isOptimal());
     ASSERT_FALSE(solver.isUnbounded());
     ASSERT_TRUE(solver.isInfeasible());
     bool xValue = false;
-    ASSERT_THROW(xValue = solver.getBinaryValue(xIndex), storm::exceptions::InvalidStateException);
+    ASSERT_THROW(xValue = solver.getBinaryValue("x"), storm::exceptions::InvalidAccessException);
     int_fast64_t yValue = 0;
-    ASSERT_THROW(yValue = solver.getIntegerValue(yIndex), storm::exceptions::InvalidStateException);
+    ASSERT_THROW(yValue = solver.getIntegerValue("y"), storm::exceptions::InvalidAccessException);
     double zValue = 0;
-    ASSERT_THROW(zValue = solver.getContinuousValue(zIndex), storm::exceptions::InvalidStateException);
+    ASSERT_THROW(zValue = solver.getContinuousValue("z"), storm::exceptions::InvalidAccessException);
     double objectiveValue = 0;
-    ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidStateException);
+    ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidAccessException);
 #else
     ASSERT_TRUE(false) << "StoRM built without Gurobi support.";
 #endif
@@ -223,31 +206,28 @@ TEST(GurobiLpSolver, MILPInfeasible) {
 
 TEST(GurobiLpSolver, LPUnbounded) {
 #ifdef STORM_HAVE_GUROBI
-    storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::MAXIMIZE);
-    uint_fast64_t xIndex;
-    ASSERT_NO_THROW(xIndex = solver.createContinuousVariable("x", storm::solver::LpSolver::VariableType::BOUNDED, 0, 1, -1));
-    uint_fast64_t yIndex;
-    ASSERT_NO_THROW(yIndex = solver.createContinuousVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2));
-    uint_fast64_t zIndex;
-    ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 1));
-    
-	ASSERT_NO_THROW(solver.update());
+    storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::ModelSense::Maximize);
+    ASSERT_NO_THROW(solver.addBoundedContinuousVariable("x", 0, 1, -1));
+    ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("y", 0, 2));
+    ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("z", 0, 1));
+    ASSERT_NO_THROW(solver.update());
+
+    ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12)));
+    ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5)));
+    ASSERT_NO_THROW(solver.update());
 
-    ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12));
-    ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5));
-    
     ASSERT_NO_THROW(solver.optimize());
     ASSERT_FALSE(solver.isOptimal());
     ASSERT_TRUE(solver.isUnbounded());
     ASSERT_FALSE(solver.isInfeasible());
     double xValue = 0;
-    ASSERT_THROW(xValue = solver.getContinuousValue(xIndex), storm::exceptions::InvalidStateException);
+    ASSERT_THROW(xValue = solver.getContinuousValue("x"), storm::exceptions::InvalidAccessException);
     double yValue = 0;
-    ASSERT_THROW(yValue = solver.getContinuousValue(yIndex), storm::exceptions::InvalidStateException);
+    ASSERT_THROW(yValue = solver.getContinuousValue("y"), storm::exceptions::InvalidAccessException);
     double zValue = 0;
-    ASSERT_THROW(zValue = solver.getContinuousValue(zIndex), storm::exceptions::InvalidStateException);
+    ASSERT_THROW(zValue = solver.getContinuousValue("z"), storm::exceptions::InvalidAccessException);
     double objectiveValue = 0;
-    ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidStateException);
+    ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidAccessException);
 #else
     ASSERT_TRUE(false) << "StoRM built without Gurobi support.";
 #endif
@@ -255,32 +235,28 @@ TEST(GurobiLpSolver, LPUnbounded) {
 
 TEST(GurobiLpSolver, MILPUnbounded) {
 #ifdef STORM_HAVE_GUROBI
-    storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::MAXIMIZE);
-    uint_fast64_t xIndex;
-    ASSERT_NO_THROW(xIndex = solver.createBinaryVariable("x", -1));
-    uint_fast64_t yIndex;
-    ASSERT_NO_THROW(yIndex = solver.createIntegerVariable("y", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 2));
-    uint_fast64_t zIndex;
-    ASSERT_NO_THROW(zIndex = solver.createContinuousVariable("z", storm::solver::LpSolver::VariableType::LOWER_BOUND, 0, 0, 1));
-    
-	ASSERT_NO_THROW(solver.update());
+    storm::solver::GurobiLpSolver solver(storm::solver::LpSolver::ModelSense::Maximize);
+    ASSERT_NO_THROW(solver.addBinaryVariable("x", -1));
+    ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("y", 0, 2));
+    ASSERT_NO_THROW(solver.addLowerBoundedContinuousVariable("z", 0, 1));
+    ASSERT_NO_THROW(solver.update());
 
-    ASSERT_NO_THROW(solver.addConstraint("", {xIndex, yIndex, zIndex}, {1, 1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 12));
-    ASSERT_NO_THROW(solver.addConstraint("", {yIndex, xIndex}, {1, -1}, storm::solver::LpSolver::BoundType::LESS_EQUAL, 5.5));
+    ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("x") + storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("z") <= storm::expressions::Expression::createDoubleLiteral(12)));
+    ASSERT_NO_THROW(solver.addConstraint("", storm::expressions::Expression::createDoubleVariable("y") - storm::expressions::Expression::createDoubleVariable("x") <= storm::expressions::Expression::createDoubleLiteral(5.5)));
     
     ASSERT_NO_THROW(solver.optimize());
     ASSERT_FALSE(solver.isOptimal());
     ASSERT_TRUE(solver.isUnbounded());
     ASSERT_FALSE(solver.isInfeasible());
     bool xValue = false;
-    ASSERT_THROW(xValue = solver.getBinaryValue(xIndex), storm::exceptions::InvalidStateException);
+    ASSERT_THROW(xValue = solver.getBinaryValue("x"), storm::exceptions::InvalidAccessException);
     int_fast64_t yValue = 0;
-    ASSERT_THROW(yValue = solver.getIntegerValue(yIndex), storm::exceptions::InvalidStateException);
+    ASSERT_THROW(yValue = solver.getIntegerValue("y"), storm::exceptions::InvalidAccessException);
     double zValue = 0;
-    ASSERT_THROW(zValue = solver.getContinuousValue(zIndex), storm::exceptions::InvalidStateException);
+    ASSERT_THROW(zValue = solver.getContinuousValue("z"), storm::exceptions::InvalidAccessException);
     double objectiveValue = 0;
-    ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidStateException);
+    ASSERT_THROW(objectiveValue = solver.getObjectiveValue(), storm::exceptions::InvalidAccessException);
 #else
     ASSERT_TRUE(false) << "StoRM built without Gurobi support.";
 #endif
-}
\ No newline at end of file
+}
diff --git a/test/functional/storage/CuddDdTest.cpp b/test/functional/storage/CuddDdTest.cpp
index 91d14f202..ce40c94d1 100644
--- a/test/functional/storage/CuddDdTest.cpp
+++ b/test/functional/storage/CuddDdTest.cpp
@@ -307,4 +307,26 @@ TEST(CuddDd, ForwardIteratorTest) {
         ++numberOfValuations;
     }
     EXPECT_EQ(9, numberOfValuations);
+
+    dd = manager->getRange("x");
+    dd = dd.ite(manager->getOne(), manager->getOne());
+    ASSERT_NO_THROW(it = dd.begin());
+    ASSERT_NO_THROW(ite = dd.end());
+    numberOfValuations = 0;
+    while (it != ite) {
+        ASSERT_NO_THROW(valuationValuePair = *it);
+        ASSERT_NO_THROW(++it);
+        ++numberOfValuations;
+    }
+    EXPECT_EQ(16, numberOfValuations);
+    
+    ASSERT_NO_THROW(it = dd.begin(false));
+    ASSERT_NO_THROW(ite = dd.end());
+    numberOfValuations = 0;
+    while (it != ite) {
+        ASSERT_NO_THROW(valuationValuePair = *it);
+        ASSERT_NO_THROW(++it);
+        ++numberOfValuations;
+    }
+    EXPECT_EQ(1, numberOfValuations);
 }
diff --git a/test/functional/storage/ExpressionTest.cpp b/test/functional/storage/ExpressionTest.cpp
index 26822190d..d3a920d1d 100644
--- a/test/functional/storage/ExpressionTest.cpp
+++ b/test/functional/storage/ExpressionTest.cpp
@@ -3,6 +3,7 @@
 
 #include "gtest/gtest.h"
 #include "src/storage/expressions/Expression.h"
+#include "src/storage/expressions/LinearityCheckVisitor.h"
 #include "src/storage/expressions/SimpleValuation.h"
 #include "src/exceptions/InvalidTypeException.h"
 
@@ -332,4 +333,20 @@ TEST(Expression, SimpleEvaluationTest) {
     ASSERT_THROW(tempExpression.evaluateAsDouble(&valuation), storm::exceptions::InvalidTypeException);
     ASSERT_THROW(tempExpression.evaluateAsInt(&valuation), storm::exceptions::InvalidTypeException);
     EXPECT_FALSE(tempExpression.evaluateAsBool(&valuation));
+}
+
+TEST(Expression, VisitorTest) {
+    storm::expressions::Expression threeExpression;
+    storm::expressions::Expression piExpression;
+    storm::expressions::Expression intVarExpression;
+    storm::expressions::Expression doubleVarExpression;
+    
+    ASSERT_NO_THROW(threeExpression = storm::expressions::Expression::createIntegerLiteral(3));
+    ASSERT_NO_THROW(piExpression = storm::expressions::Expression::createDoubleLiteral(3.14));
+    ASSERT_NO_THROW(intVarExpression = storm::expressions::Expression::createIntegerVariable("y"));
+    ASSERT_NO_THROW(doubleVarExpression = storm::expressions::Expression::createDoubleVariable("z"));
+    
+    storm::expressions::Expression tempExpression = intVarExpression + doubleVarExpression * threeExpression;
+    storm::expressions::LinearityCheckVisitor visitor;
+    EXPECT_TRUE(visitor.check(tempExpression));
 }
\ No newline at end of file
diff --git a/test/functional/storage/SparseMatrixTest.cpp b/test/functional/storage/SparseMatrixTest.cpp
index 69517a6d4..de89f4950 100644
--- a/test/functional/storage/SparseMatrixTest.cpp
+++ b/test/functional/storage/SparseMatrixTest.cpp
@@ -147,7 +147,7 @@ TEST(SparseMatrix, Build) {
 }
 
 TEST(SparseMatrix, CreationWithMovingContents) {
-    std::vector<std::pair<uint_fast64_t, double>> columnsAndValues;
+    std::vector<storm::storage::MatrixEntry<double>> columnsAndValues;
     columnsAndValues.emplace_back(1, 1.0);
     columnsAndValues.emplace_back(2, 1.2);
     columnsAndValues.emplace_back(0, 0.5);
@@ -540,24 +540,24 @@ TEST(SparseMatrix, Iteration) {
     ASSERT_NO_THROW(matrix = matrixBuilder.build());
     
     for (auto const& entry : matrix.getRow(4)) {
-        if (entry.first == 0) {
-            ASSERT_EQ(0.1, entry.second);
-        } else if (entry.first == 1) {
-            ASSERT_EQ(0.2, entry.second);
-        } else if (entry.first == 3) {
-            ASSERT_EQ(0.3, entry.second);
+        if (entry.getColumn() == 0) {
+            ASSERT_EQ(0.1, entry.getValue());
+        } else if (entry.getColumn() == 1) {
+            ASSERT_EQ(0.2, entry.getValue());
+        } else if (entry.getColumn() == 3) {
+            ASSERT_EQ(0.3, entry.getValue());
         } else {
             ASSERT_TRUE(false);
         }
     }
     
     for (storm::storage::SparseMatrix<double>::iterator it = matrix.begin(4), ite = matrix.end(4); it != ite; ++it) {
-        if (it->first == 0) {
-            ASSERT_EQ(0.1, it->second);
-        } else if (it->first == 1) {
-            ASSERT_EQ(0.2, it->second);
-        } else if (it->first == 3) {
-            ASSERT_EQ(0.3, it->second);
+        if (it->getColumn() == 0) {
+            ASSERT_EQ(0.1, it->getValue());
+        } else if (it->getColumn() == 1) {
+            ASSERT_EQ(0.2, it->getValue());
+        } else if (it->getColumn() == 3) {
+            ASSERT_EQ(0.3, it->getValue());
         } else {
             ASSERT_TRUE(false);
         }
diff --git a/test/performance/modelchecker/GmmxxDtmcPrctModelCheckerTest.cpp b/test/performance/modelchecker/GmmxxDtmcPrctModelCheckerTest.cpp
index 809f602fb..2fddf8702 100644
--- a/test/performance/modelchecker/GmmxxDtmcPrctModelCheckerTest.cpp
+++ b/test/performance/modelchecker/GmmxxDtmcPrctModelCheckerTest.cpp
@@ -17,7 +17,7 @@ TEST(GmmxxDtmcPrctlModelCheckerTest, Crowds) {
 	std::shared_ptr<storm::models::Dtmc<double>> dtmc = abstractModel->as<storm::models::Dtmc<double>>();
 
 	ASSERT_EQ(dtmc->getNumberOfStates(), 2036647ull);
-	ASSERT_EQ(dtmc->getNumberOfTransitions(), 8973900ull);
+	ASSERT_EQ(dtmc->getNumberOfTransitions(), 7362293ull);
 
 	storm::modelchecker::prctl::SparseDtmcPrctlModelChecker<double> mc(*dtmc, new storm::solver::GmmxxLinearEquationSolver<double>());
 
@@ -71,7 +71,7 @@ TEST(GmmxxDtmcPrctlModelCheckerTest, SynchronousLeader) {
 	std::shared_ptr<storm::models::Dtmc<double>> dtmc = abstractModel->as<storm::models::Dtmc<double>>();
 
 	ASSERT_EQ(dtmc->getNumberOfStates(), 1312334ull);
-	ASSERT_EQ(dtmc->getNumberOfTransitions(), 2886810ull);
+	ASSERT_EQ(dtmc->getNumberOfTransitions(), 1574477ull);
 
 	storm::modelchecker::prctl::SparseDtmcPrctlModelChecker<double> mc(*dtmc, new storm::solver::GmmxxLinearEquationSolver<double>());
 
diff --git a/test/performance/storage/SparseMatrixTest.cpp b/test/performance/storage/SparseMatrixTest.cpp
index 367bd3401..b59d283af 100644
--- a/test/performance/storage/SparseMatrixTest.cpp
+++ b/test/performance/storage/SparseMatrixTest.cpp
@@ -15,7 +15,7 @@ TEST(SparseMatrix, Iteration) {
     for (uint_fast64_t row = 0; row < matrix.getRowCount(); ++row) {
         for (auto const& entry : matrix.getRow(row)) {
             // The following can never be true, but prevents the compiler from optimizing away the loop.
-            if (entry.first > matrix.getColumnCount()) {
+            if (entry.getColumn() > matrix.getColumnCount()) {
                 ASSERT_TRUE(false);
             }
         }