diff --git a/src/models/Ctmc.h b/src/models/Ctmc.h
index 229ccfe9e..b3c1f1ef1 100644
--- a/src/models/Ctmc.h
+++ b/src/models/Ctmc.h
@@ -36,8 +36,9 @@ public:
 	 * propositions to each state.
 	 */
 	Ctmc(storm::storage::SparseMatrix<T> const& rateMatrix, storm::models::AtomicPropositionsLabeling const& stateLabeling,
-				boost::optional<std::vector<T>> const& optionalStateRewardVector, boost::optional<storm::storage::SparseMatrix<T>> const& optionalTransitionRewardMatrix,
-                boost::optional<std::vector<boost::container::flat_set<uint_fast64_t>>> const& optionalChoiceLabeling)
+				boost::optional<std::vector<T>> const& optionalStateRewardVector = boost::optional<std::vector<T>>(),
+                boost::optional<storm::storage::SparseMatrix<T>> const& optionalTransitionRewardMatrix = boost::optional<storm::storage::SparseMatrix<T>>(),
+                boost::optional<std::vector<boost::container::flat_set<uint_fast64_t>>> const& optionalChoiceLabeling = boost::optional<std::vector<boost::container::flat_set<uint_fast64_t>>>())
 			: AbstractDeterministicModel<T>(rateMatrix, stateLabeling, optionalStateRewardVector, optionalTransitionRewardMatrix, optionalChoiceLabeling) {
 		// Intentionally left empty.
 	}
@@ -51,8 +52,9 @@ public:
 	 * propositions to each state.
 	 */
 	Ctmc(storm::storage::SparseMatrix<T>&& rateMatrix, storm::models::AtomicPropositionsLabeling&& stateLabeling,
-				boost::optional<std::vector<T>>&& optionalStateRewardVector, boost::optional<storm::storage::SparseMatrix<T>>&& optionalTransitionRewardMatrix,
-                boost::optional<std::vector<boost::container::flat_set<uint_fast64_t>>>&& optionalChoiceLabeling)
+				boost::optional<std::vector<T>>&& optionalStateRewardVector = boost::optional<std::vector<T>>(),
+                boost::optional<storm::storage::SparseMatrix<T>>&& optionalTransitionRewardMatrix = boost::optional<storm::storage::SparseMatrix<T>>(),
+                boost::optional<std::vector<boost::container::flat_set<uint_fast64_t>>>&& optionalChoiceLabeling = boost::optional<std::vector<boost::container::flat_set<uint_fast64_t>>>())
 			// The std::move call must be repeated here because otherwise this calls the copy constructor of the Base Class
 			: AbstractDeterministicModel<T>(std::move(rateMatrix), std::move(stateLabeling), std::move(optionalStateRewardVector), std::move(optionalTransitionRewardMatrix),
                                             std::move(optionalChoiceLabeling)) {
diff --git a/src/storage/DeterministicModelBisimulationDecomposition.cpp b/src/storage/DeterministicModelStrongBisimulationDecomposition.cpp
similarity index 57%
rename from src/storage/DeterministicModelBisimulationDecomposition.cpp
rename to src/storage/DeterministicModelStrongBisimulationDecomposition.cpp
index d9023d153..93433d19f 100644
--- a/src/storage/DeterministicModelBisimulationDecomposition.cpp
+++ b/src/storage/DeterministicModelStrongBisimulationDecomposition.cpp
@@ -1,19 +1,21 @@
-#include "src/storage/DeterministicModelBisimulationDecomposition.h"
+#include "src/storage/DeterministicModelStrongBisimulationDecomposition.h"
 
 #include <algorithm>
 #include <unordered_map>
 #include <chrono>
 #include <iomanip>
 
+#include "src/utility/graph.h"
 #include "src/exceptions/IllegalFunctionCallException.h"
 
 namespace storm {
     namespace storage {
         
-        static std::size_t globalId = 0;
+        template<typename ValueType>
+        std::size_t DeterministicModelStrongBisimulationDecomposition<ValueType>::Block::blockId = 0;
         
         template<typename ValueType>
-        DeterministicModelBisimulationDecomposition<ValueType>::Block::Block(storm::storage::sparse::state_type begin, storm::storage::sparse::state_type end, Block* prev, Block* next) : next(next), prev(prev), begin(begin), end(end), markedAsSplitter(false), markedAsPredecessorBlock(false), markedPosition(begin), id(globalId++) {
+        DeterministicModelStrongBisimulationDecomposition<ValueType>::Block::Block(storm::storage::sparse::state_type begin, storm::storage::sparse::state_type end, Block* prev, Block* next, std::shared_ptr<std::string> const& label) : next(next), prev(prev), begin(begin), end(end), markedAsSplitter(false), markedAsPredecessorBlock(false), markedPosition(begin), absorbing(false), id(blockId++), label(label) {
             if (next != nullptr) {
                 next->prev = this;
             }
@@ -23,7 +25,7 @@ namespace storm {
         }
         
         template<typename ValueType>
-        void DeterministicModelBisimulationDecomposition<ValueType>::Block::print(Partition const& partition) const {
+        void DeterministicModelStrongBisimulationDecomposition<ValueType>::Block::print(Partition const& partition) const {
             std::cout << "block " << this->getId() << " with marked position " << this->getMarkedPosition() << " and original begin " << this->getOriginalBegin() << std::endl;
             std::cout << "begin: " << this->begin << " and end: " << this->end << " (number of states: " << this->getNumberOfStates() << ")" << std::endl;
             std::cout << "states:" << std::endl;
@@ -42,33 +44,33 @@ namespace storm {
         }
         
         template<typename ValueType>
-        void DeterministicModelBisimulationDecomposition<ValueType>::Block::setBegin(storm::storage::sparse::state_type begin) {
+        void DeterministicModelStrongBisimulationDecomposition<ValueType>::Block::setBegin(storm::storage::sparse::state_type begin) {
             this->begin = begin;
             this->markedPosition = begin;
         }
         
         template<typename ValueType>
-        void DeterministicModelBisimulationDecomposition<ValueType>::Block::setEnd(storm::storage::sparse::state_type end) {
+        void DeterministicModelStrongBisimulationDecomposition<ValueType>::Block::setEnd(storm::storage::sparse::state_type end) {
             this->end = end;
         }
         
         template<typename ValueType>
-        void DeterministicModelBisimulationDecomposition<ValueType>::Block::incrementBegin() {
+        void DeterministicModelStrongBisimulationDecomposition<ValueType>::Block::incrementBegin() {
             ++this->begin;
         }
         
         template<typename ValueType>
-        void DeterministicModelBisimulationDecomposition<ValueType>::Block::decrementEnd() {
+        void DeterministicModelStrongBisimulationDecomposition<ValueType>::Block::decrementEnd() {
             ++this->begin;
         }
         
         template<typename ValueType>
-        storm::storage::sparse::state_type DeterministicModelBisimulationDecomposition<ValueType>::Block::getBegin() const {
+        storm::storage::sparse::state_type DeterministicModelStrongBisimulationDecomposition<ValueType>::Block::getBegin() const {
             return this->begin;
         }
         
         template<typename ValueType>
-        storm::storage::sparse::state_type DeterministicModelBisimulationDecomposition<ValueType>::Block::getOriginalBegin() const {
+        storm::storage::sparse::state_type DeterministicModelStrongBisimulationDecomposition<ValueType>::Block::getOriginalBegin() const {
             if (this->hasPreviousBlock()) {
                 return this->getPreviousBlock().getEnd();
             } else {
@@ -77,72 +79,72 @@ namespace storm {
         }
         
         template<typename ValueType>
-        storm::storage::sparse::state_type DeterministicModelBisimulationDecomposition<ValueType>::Block::getEnd() const {
+        storm::storage::sparse::state_type DeterministicModelStrongBisimulationDecomposition<ValueType>::Block::getEnd() const {
             return this->end;
         }
         
         template<typename ValueType>
-        void DeterministicModelBisimulationDecomposition<ValueType>::Block::setIterator(const_iterator it) {
+        void DeterministicModelStrongBisimulationDecomposition<ValueType>::Block::setIterator(const_iterator it) {
             this->selfIt = it;
         }
         
         template<typename ValueType>
-        typename DeterministicModelBisimulationDecomposition<ValueType>::Block::const_iterator DeterministicModelBisimulationDecomposition<ValueType>::Block::getIterator() const {
+        typename DeterministicModelStrongBisimulationDecomposition<ValueType>::Block::const_iterator DeterministicModelStrongBisimulationDecomposition<ValueType>::Block::getIterator() const {
             return this->selfIt;
         }
         
         template<typename ValueType>
-        typename DeterministicModelBisimulationDecomposition<ValueType>::Block::const_iterator DeterministicModelBisimulationDecomposition<ValueType>::Block::getNextIterator() const {
+        typename DeterministicModelStrongBisimulationDecomposition<ValueType>::Block::const_iterator DeterministicModelStrongBisimulationDecomposition<ValueType>::Block::getNextIterator() const {
             return this->getNextBlock().getIterator();
         }
         
         template<typename ValueType>
-        typename DeterministicModelBisimulationDecomposition<ValueType>::Block::const_iterator DeterministicModelBisimulationDecomposition<ValueType>::Block::getPreviousIterator() const {
+        typename DeterministicModelStrongBisimulationDecomposition<ValueType>::Block::const_iterator DeterministicModelStrongBisimulationDecomposition<ValueType>::Block::getPreviousIterator() const {
             return this->getPreviousBlock().getIterator();
         }
         
         template<typename ValueType>
-        typename DeterministicModelBisimulationDecomposition<ValueType>::Block& DeterministicModelBisimulationDecomposition<ValueType>::Block::getNextBlock() {
+        typename DeterministicModelStrongBisimulationDecomposition<ValueType>::Block& DeterministicModelStrongBisimulationDecomposition<ValueType>::Block::getNextBlock() {
             return *this->next;
         }
         
         template<typename ValueType>
-        typename DeterministicModelBisimulationDecomposition<ValueType>::Block const& DeterministicModelBisimulationDecomposition<ValueType>::Block::getNextBlock() const {
+        typename DeterministicModelStrongBisimulationDecomposition<ValueType>::Block const& DeterministicModelStrongBisimulationDecomposition<ValueType>::Block::getNextBlock() const {
             return *this->next;
         }
         
         template<typename ValueType>
-        bool DeterministicModelBisimulationDecomposition<ValueType>::Block::hasNextBlock() const {
+        bool DeterministicModelStrongBisimulationDecomposition<ValueType>::Block::hasNextBlock() const {
             return this->next != nullptr;
         }
         
         template<typename ValueType>
-        typename DeterministicModelBisimulationDecomposition<ValueType>::Block& DeterministicModelBisimulationDecomposition<ValueType>::Block::getPreviousBlock() {
+        typename DeterministicModelStrongBisimulationDecomposition<ValueType>::Block& DeterministicModelStrongBisimulationDecomposition<ValueType>::Block::getPreviousBlock() {
             return *this->prev;
         }
         
         template<typename ValueType>
-        typename DeterministicModelBisimulationDecomposition<ValueType>::Block* DeterministicModelBisimulationDecomposition<ValueType>::Block::getPreviousBlockPointer() {
+        typename DeterministicModelStrongBisimulationDecomposition<ValueType>::Block* DeterministicModelStrongBisimulationDecomposition<ValueType>::Block::getPreviousBlockPointer() {
             return this->prev;
         }
 
         template<typename ValueType>
-        typename DeterministicModelBisimulationDecomposition<ValueType>::Block* DeterministicModelBisimulationDecomposition<ValueType>::Block::getNextBlockPointer() {
+        typename DeterministicModelStrongBisimulationDecomposition<ValueType>::Block* DeterministicModelStrongBisimulationDecomposition<ValueType>::Block::getNextBlockPointer() {
             return this->next;
         }
 
         template<typename ValueType>
-        typename DeterministicModelBisimulationDecomposition<ValueType>::Block const& DeterministicModelBisimulationDecomposition<ValueType>::Block::getPreviousBlock() const {
+        typename DeterministicModelStrongBisimulationDecomposition<ValueType>::Block const& DeterministicModelStrongBisimulationDecomposition<ValueType>::Block::getPreviousBlock() const {
             return *this->prev;
         }
         
         template<typename ValueType>
-        bool DeterministicModelBisimulationDecomposition<ValueType>::Block::hasPreviousBlock() const {
+        bool DeterministicModelStrongBisimulationDecomposition<ValueType>::Block::hasPreviousBlock() const {
             return this->prev != nullptr;
         }
         
         template<typename ValueType>
-        bool DeterministicModelBisimulationDecomposition<ValueType>::Block::check() const {
+        bool DeterministicModelStrongBisimulationDecomposition<ValueType>::Block::check() const {
             if (this->begin >= this->end) {
                 assert(false);
             }
@@ -156,68 +158,94 @@ namespace storm {
         }
         
         template<typename ValueType>
-        std::size_t DeterministicModelBisimulationDecomposition<ValueType>::Block::getNumberOfStates() const {
+        std::size_t DeterministicModelStrongBisimulationDecomposition<ValueType>::Block::getNumberOfStates() const {
             // We need to take the original begin here, because the begin is temporarily moved.
             return (this->end - this->getOriginalBegin());
         }
         
         template<typename ValueType>
-        bool DeterministicModelBisimulationDecomposition<ValueType>::Block::isMarkedAsSplitter() const {
+        bool DeterministicModelStrongBisimulationDecomposition<ValueType>::Block::isMarkedAsSplitter() const {
             return this->markedAsSplitter;
         }
         
         template<typename ValueType>
-        void DeterministicModelBisimulationDecomposition<ValueType>::Block::markAsSplitter() {
+        void DeterministicModelStrongBisimulationDecomposition<ValueType>::Block::markAsSplitter() {
             this->markedAsSplitter = true;
         }
 
         template<typename ValueType>
-        void DeterministicModelBisimulationDecomposition<ValueType>::Block::unmarkAsSplitter() {
+        void DeterministicModelStrongBisimulationDecomposition<ValueType>::Block::unmarkAsSplitter() {
             this->markedAsSplitter = false;
         }
 
         template<typename ValueType>
-        std::size_t DeterministicModelBisimulationDecomposition<ValueType>::Block::getId() const {
+        std::size_t DeterministicModelStrongBisimulationDecomposition<ValueType>::Block::getId() const {
             return this->id;
         }
         
         template<typename ValueType>
-        storm::storage::sparse::state_type DeterministicModelBisimulationDecomposition<ValueType>::Block::getMarkedPosition() const {
+        void DeterministicModelStrongBisimulationDecomposition<ValueType>::Block::setAbsorbing(bool absorbing) {
+            this->absorbing = absorbing;
+        }
+        
+        template<typename ValueType>
+        bool DeterministicModelStrongBisimulationDecomposition<ValueType>::Block::isAbsorbing() const {
+            return this->absorbing;
+        }
+        
+        template<typename ValueType>
+        storm::storage::sparse::state_type DeterministicModelStrongBisimulationDecomposition<ValueType>::Block::getMarkedPosition() const {
             return this->markedPosition;
         }
         
         template<typename ValueType>
-        void DeterministicModelBisimulationDecomposition<ValueType>::Block::setMarkedPosition(storm::storage::sparse::state_type position) {
+        void DeterministicModelStrongBisimulationDecomposition<ValueType>::Block::setMarkedPosition(storm::storage::sparse::state_type position) {
             this->markedPosition = position;
         }
         
         template<typename ValueType>
-        void DeterministicModelBisimulationDecomposition<ValueType>::Block::resetMarkedPosition() {
+        void DeterministicModelStrongBisimulationDecomposition<ValueType>::Block::resetMarkedPosition() {
             this->markedPosition = this->begin;
         }
         
         template<typename ValueType>
-        void DeterministicModelBisimulationDecomposition<ValueType>::Block::incrementMarkedPosition() {
+        void DeterministicModelStrongBisimulationDecomposition<ValueType>::Block::incrementMarkedPosition() {
             ++this->markedPosition;
         }
         
         template<typename ValueType>
-        void DeterministicModelBisimulationDecomposition<ValueType>::Block::markAsPredecessorBlock() {
+        void DeterministicModelStrongBisimulationDecomposition<ValueType>::Block::markAsPredecessorBlock() {
             this->markedAsPredecessorBlock = true;
         }
         
         template<typename ValueType>
-        void DeterministicModelBisimulationDecomposition<ValueType>::Block::unmarkAsPredecessorBlock() {
+        void DeterministicModelStrongBisimulationDecomposition<ValueType>::Block::unmarkAsPredecessorBlock() {
             this->markedAsPredecessorBlock = false;
         }
         
         template<typename ValueType>
-        bool DeterministicModelBisimulationDecomposition<ValueType>::Block::isMarkedAsPredecessor() const {
+        bool DeterministicModelStrongBisimulationDecomposition<ValueType>::Block::isMarkedAsPredecessor() const {
             return markedAsPredecessorBlock;
         }
         
         template<typename ValueType>
-        DeterministicModelBisimulationDecomposition<ValueType>::Partition::Partition(std::size_t numberOfStates) : stateToBlockMapping(numberOfStates), states(numberOfStates), positions(numberOfStates), values(numberOfStates) {
+        bool DeterministicModelStrongBisimulationDecomposition<ValueType>::Block::hasLabel() const {
+            return this->label != nullptr;
+        }
+        
+        template<typename ValueType>
+        std::string const& DeterministicModelStrongBisimulationDecomposition<ValueType>::Block::getLabel() const {
+            STORM_LOG_THROW(this->label != nullptr, storm::exceptions::IllegalFunctionCallException, "Unable to retrieve label of block that has none.");
+            return *this->label;
+        }
+        
+        template<typename ValueType>
+        std::shared_ptr<std::string> const& DeterministicModelStrongBisimulationDecomposition<ValueType>::Block::getLabelPtr() const {
+            return this->label;
+        }
+        
+        template<typename ValueType>
+        DeterministicModelStrongBisimulationDecomposition<ValueType>::Partition::Partition(std::size_t numberOfStates) : stateToBlockMapping(numberOfStates), states(numberOfStates), positions(numberOfStates), values(numberOfStates) {
             // Create the block and give it an iterator to itself.
             typename std::list<Block>::iterator it = blocks.emplace(this->blocks.end(), 0, numberOfStates, nullptr, nullptr);
             it->setIterator(it);
@@ -231,14 +259,54 @@ namespace storm {
         }
         
         template<typename ValueType>
-        void DeterministicModelBisimulationDecomposition<ValueType>::Partition::swapStates(storm::storage::sparse::state_type state1, storm::storage::sparse::state_type state2) {
+        DeterministicModelStrongBisimulationDecomposition<ValueType>::Partition::Partition(std::size_t numberOfStates, storm::storage::BitVector const& prob0States, storm::storage::BitVector const& prob1States, std::string const& otherLabel, std::string const& prob1Label) : stateToBlockMapping(numberOfStates), states(numberOfStates), positions(numberOfStates), values(numberOfStates) {
+            typename std::list<Block>::iterator firstIt = blocks.emplace(this->blocks.end(), 0, prob0States.getNumberOfSetBits(), nullptr, nullptr);
+            Block& firstBlock = *firstIt;
+            firstBlock.setIterator(firstIt);
+
+            storm::storage::sparse::state_type position = 0;
+            for (auto state : prob0States) {
+                states[position] = state;
+                positions[state] = position;
+                stateToBlockMapping[state] = &firstBlock;
+                ++position;
+            }
+            firstBlock.setAbsorbing(true);
+
+            typename std::list<Block>::iterator secondIt = blocks.emplace(this->blocks.end(), position, position + prob1States.getNumberOfSetBits(), &firstBlock, nullptr, std::shared_ptr<std::string>(new std::string(prob1Label)));
+            Block& secondBlock = *secondIt;
+            secondBlock.setIterator(secondIt);
+            
+            for (auto state : prob1States) {
+                states[position] = state;
+                positions[state] = position;
+                stateToBlockMapping[state] = &secondBlock;
+                ++position;
+            }
+            secondBlock.setAbsorbing(true);
+            
+            typename std::list<Block>::iterator thirdIt = blocks.emplace(this->blocks.end(), position, numberOfStates, &secondBlock, nullptr, otherLabel == "true" ? std::shared_ptr<std::string>(nullptr) : std::shared_ptr<std::string>(new std::string(otherLabel)));
+            Block& thirdBlock = *thirdIt;
+            thirdBlock.setIterator(thirdIt);
+            
+            storm::storage::BitVector otherStates = ~(prob0States | prob1States);
+            for (auto state : otherStates) {
+                states[position] = state;
+                positions[state] = position;
+                stateToBlockMapping[state] = &thirdBlock;
+                ++position;
+            }
+        }
+        
+        template<typename ValueType>
+        void DeterministicModelStrongBisimulationDecomposition<ValueType>::Partition::swapStates(storm::storage::sparse::state_type state1, storm::storage::sparse::state_type state2) {
             std::swap(this->states[this->positions[state1]], this->states[this->positions[state2]]);
             std::swap(this->values[this->positions[state1]], this->values[this->positions[state2]]);
             std::swap(this->positions[state1], this->positions[state2]);
         }
         
         template<typename ValueType>
-        void DeterministicModelBisimulationDecomposition<ValueType>::Partition::swapStatesAtPositions(storm::storage::sparse::state_type position1, storm::storage::sparse::state_type position2) {
+        void DeterministicModelStrongBisimulationDecomposition<ValueType>::Partition::swapStatesAtPositions(storm::storage::sparse::state_type position1, storm::storage::sparse::state_type position2) {
             storm::storage::sparse::state_type state1 = this->states[position1];
             storm::storage::sparse::state_type state2 = this->states[position2];
             
@@ -250,94 +318,99 @@ namespace storm {
         }
         
         template<typename ValueType>
-        storm::storage::sparse::state_type const& DeterministicModelBisimulationDecomposition<ValueType>::Partition::getPosition(storm::storage::sparse::state_type state) const {
+        storm::storage::sparse::state_type const& DeterministicModelStrongBisimulationDecomposition<ValueType>::Partition::getPosition(storm::storage::sparse::state_type state) const {
             return this->positions[state];
         }
         
         template<typename ValueType>
-        void DeterministicModelBisimulationDecomposition<ValueType>::Partition::setPosition(storm::storage::sparse::state_type state, storm::storage::sparse::state_type position) {
+        void DeterministicModelStrongBisimulationDecomposition<ValueType>::Partition::setPosition(storm::storage::sparse::state_type state, storm::storage::sparse::state_type position) {
             this->positions[state] = position;
         }
         
         template<typename ValueType>
-        storm::storage::sparse::state_type const& DeterministicModelBisimulationDecomposition<ValueType>::Partition::getState(storm::storage::sparse::state_type position) const {
+        storm::storage::sparse::state_type const& DeterministicModelStrongBisimulationDecomposition<ValueType>::Partition::getState(storm::storage::sparse::state_type position) const {
             return this->states[position];
         }
         
         template<typename ValueType>
-        ValueType const& DeterministicModelBisimulationDecomposition<ValueType>::Partition::getValue(storm::storage::sparse::state_type state) const {
+        ValueType const& DeterministicModelStrongBisimulationDecomposition<ValueType>::Partition::getValue(storm::storage::sparse::state_type state) const {
             return this->values[this->positions[state]];
         }
         
         template<typename ValueType>
-        ValueType const& DeterministicModelBisimulationDecomposition<ValueType>::Partition::getValueAtPosition(storm::storage::sparse::state_type position) const {
+        ValueType const& DeterministicModelStrongBisimulationDecomposition<ValueType>::Partition::getValueAtPosition(storm::storage::sparse::state_type position) const {
             return this->values[position];
         }
         
         template<typename ValueType>
-        void DeterministicModelBisimulationDecomposition<ValueType>::Partition::setValue(storm::storage::sparse::state_type state, ValueType value) {
+        void DeterministicModelStrongBisimulationDecomposition<ValueType>::Partition::setValue(storm::storage::sparse::state_type state, ValueType value) {
             this->values[this->positions[state]] = value;
         }
         
         template<typename ValueType>
-        std::vector<ValueType>& DeterministicModelBisimulationDecomposition<ValueType>::Partition::getValues() {
+        std::vector<ValueType>& DeterministicModelStrongBisimulationDecomposition<ValueType>::Partition::getValues() {
             return this->values;
         }
         
         template<typename ValueType>
-        void DeterministicModelBisimulationDecomposition<ValueType>::Partition::increaseValue(storm::storage::sparse::state_type state, ValueType value) {
+        void DeterministicModelStrongBisimulationDecomposition<ValueType>::Partition::increaseValue(storm::storage::sparse::state_type state, ValueType value) {
             this->values[this->positions[state]] += value;
         }
 
         template<typename ValueType>
-        void DeterministicModelBisimulationDecomposition<ValueType>::Partition::updateBlockMapping(Block& block, std::vector<storm::storage::sparse::state_type>::iterator first, std::vector<storm::storage::sparse::state_type>::iterator last) {
+        void DeterministicModelStrongBisimulationDecomposition<ValueType>::Partition::updateBlockMapping(Block& block, std::vector<storm::storage::sparse::state_type>::iterator first, std::vector<storm::storage::sparse::state_type>::iterator last) {
             for (; first != last; ++first) {
                 this->stateToBlockMapping[*first] = &block;
             }
         }
         
         template<typename ValueType>
-        typename DeterministicModelBisimulationDecomposition<ValueType>::Block& DeterministicModelBisimulationDecomposition<ValueType>::Partition::getBlock(storm::storage::sparse::state_type state) {
+        typename DeterministicModelStrongBisimulationDecomposition<ValueType>::Block& DeterministicModelStrongBisimulationDecomposition<ValueType>::Partition::getFirstBlock() {
+            return *this->blocks.begin();
+        }
+        
+        template<typename ValueType>
+        typename DeterministicModelStrongBisimulationDecomposition<ValueType>::Block& DeterministicModelStrongBisimulationDecomposition<ValueType>::Partition::getBlock(storm::storage::sparse::state_type state) {
             return *this->stateToBlockMapping[state];
         }
 
         template<typename ValueType>
-        typename DeterministicModelBisimulationDecomposition<ValueType>::Block const& DeterministicModelBisimulationDecomposition<ValueType>::Partition::getBlock(storm::storage::sparse::state_type state) const {
+        typename DeterministicModelStrongBisimulationDecomposition<ValueType>::Block const& DeterministicModelStrongBisimulationDecomposition<ValueType>::Partition::getBlock(storm::storage::sparse::state_type state) const {
             return *this->stateToBlockMapping[state];
         }
 
         template<typename ValueType>
-        std::vector<storm::storage::sparse::state_type>::iterator DeterministicModelBisimulationDecomposition<ValueType>::Partition::getBeginOfStates(Block const& block) {
+        std::vector<storm::storage::sparse::state_type>::iterator DeterministicModelStrongBisimulationDecomposition<ValueType>::Partition::getBeginOfStates(Block const& block) {
             return this->states.begin() + block.getBegin();
         }
         
         template<typename ValueType>
-        std::vector<storm::storage::sparse::state_type>::iterator DeterministicModelBisimulationDecomposition<ValueType>::Partition::getEndOfStates(Block const& block) {
+        std::vector<storm::storage::sparse::state_type>::iterator DeterministicModelStrongBisimulationDecomposition<ValueType>::Partition::getEndOfStates(Block const& block) {
             return this->states.begin() + block.getEnd();
         }
 
         template<typename ValueType>
-        std::vector<storm::storage::sparse::state_type>::const_iterator DeterministicModelBisimulationDecomposition<ValueType>::Partition::getBeginOfStates(Block const& block) const {
+        std::vector<storm::storage::sparse::state_type>::const_iterator DeterministicModelStrongBisimulationDecomposition<ValueType>::Partition::getBeginOfStates(Block const& block) const {
             return this->states.begin() + block.getBegin();
         }
         
         template<typename ValueType>
-        std::vector<storm::storage::sparse::state_type>::const_iterator DeterministicModelBisimulationDecomposition<ValueType>::Partition::getEndOfStates(Block const& block) const {
+        std::vector<storm::storage::sparse::state_type>::const_iterator DeterministicModelStrongBisimulationDecomposition<ValueType>::Partition::getEndOfStates(Block const& block) const {
             return this->states.begin() + block.getEnd();
         }
         
         template<typename ValueType>
-        typename std::vector<ValueType>::iterator DeterministicModelBisimulationDecomposition<ValueType>::Partition::getBeginOfValues(Block const& block) {
+        typename std::vector<ValueType>::iterator DeterministicModelStrongBisimulationDecomposition<ValueType>::Partition::getBeginOfValues(Block const& block) {
             return this->values.begin() + block.getBegin();
         }
         
         template<typename ValueType>
-        typename std::vector<ValueType>::iterator DeterministicModelBisimulationDecomposition<ValueType>::Partition::getEndOfValues(Block const& block) {
+        typename std::vector<ValueType>::iterator DeterministicModelStrongBisimulationDecomposition<ValueType>::Partition::getEndOfValues(Block const& block) {
             return this->values.begin() + block.getEnd();
         }
         
         template<typename ValueType>
-        typename DeterministicModelBisimulationDecomposition<ValueType>::Block& DeterministicModelBisimulationDecomposition<ValueType>::Partition::splitBlock(Block& block, storm::storage::sparse::state_type position) {
+        typename DeterministicModelStrongBisimulationDecomposition<ValueType>::Block& DeterministicModelStrongBisimulationDecomposition<ValueType>::Partition::splitBlock(Block& block, storm::storage::sparse::state_type position) {
             // FIXME: this could be improved by splitting off the smaller of the two resulting blocks.
             
             // In case one of the resulting blocks would be empty, we simply return the current block and do not create
@@ -347,7 +420,7 @@ namespace storm {
             }
             
             // Actually create the new block and insert it at the correct position.
-            typename std::list<Block>::iterator selfIt = this->blocks.emplace(block.getIterator(), block.getBegin(), position, block.getPreviousBlockPointer(), &block);
+            typename std::list<Block>::iterator selfIt = this->blocks.emplace(block.getIterator(), block.getBegin(), position, block.getPreviousBlockPointer(), &block, block.getLabelPtr());
             selfIt->setIterator(selfIt);
             Block& newBlock = *selfIt;
             
@@ -363,7 +436,7 @@ namespace storm {
         }
         
         template<typename ValueType>
-        typename DeterministicModelBisimulationDecomposition<ValueType>::Block& DeterministicModelBisimulationDecomposition<ValueType>::Partition::insertBlock(Block& block) {
+        typename DeterministicModelStrongBisimulationDecomposition<ValueType>::Block& DeterministicModelStrongBisimulationDecomposition<ValueType>::Partition::insertBlock(Block& block) {
             // Find the beginning of the new block.
             storm::storage::sparse::state_type begin;
             if (block.hasPreviousBlock()) {
@@ -386,7 +459,7 @@ namespace storm {
         }
         
         template<typename ValueType>
-        void DeterministicModelBisimulationDecomposition<ValueType>::Partition::splitLabel(storm::storage::BitVector const& statesWithLabel) {
+        void DeterministicModelStrongBisimulationDecomposition<ValueType>::Partition::splitLabel(storm::storage::BitVector const& statesWithLabel) {
             for (auto blockIterator = this->blocks.begin(), ite = this->blocks.end(); blockIterator != ite; ) { // The update of the loop was intentionally moved to the bottom of the loop.
                 Block& block = *blockIterator;
                 
@@ -414,22 +487,22 @@ namespace storm {
         }
         
         template<typename ValueType>
-        std::list<typename DeterministicModelBisimulationDecomposition<ValueType>::Block> const& DeterministicModelBisimulationDecomposition<ValueType>::Partition::getBlocks() const {
+        std::list<typename DeterministicModelStrongBisimulationDecomposition<ValueType>::Block> const& DeterministicModelStrongBisimulationDecomposition<ValueType>::Partition::getBlocks() const {
             return this->blocks;
         }
         
         template<typename ValueType>
-        std::vector<storm::storage::sparse::state_type>& DeterministicModelBisimulationDecomposition<ValueType>::Partition::getStates() {
+        std::vector<storm::storage::sparse::state_type>& DeterministicModelStrongBisimulationDecomposition<ValueType>::Partition::getStates() {
             return this->states;
         }
         
         template<typename ValueType>
-        std::list<typename DeterministicModelBisimulationDecomposition<ValueType>::Block>& DeterministicModelBisimulationDecomposition<ValueType>::Partition::getBlocks() {
+        std::list<typename DeterministicModelStrongBisimulationDecomposition<ValueType>::Block>& DeterministicModelStrongBisimulationDecomposition<ValueType>::Partition::getBlocks() {
             return this->blocks;
         }
         
         template<typename ValueType>
-        bool DeterministicModelBisimulationDecomposition<ValueType>::Partition::check() const {
+        bool DeterministicModelStrongBisimulationDecomposition<ValueType>::Partition::check() const {
             for (uint_fast64_t state = 0; state < this->positions.size(); ++state) {
                 if (this->states[this->positions[state]] != state) {
                     assert(false);
@@ -447,7 +520,7 @@ namespace storm {
         }
         
         template<typename ValueType>
-        void DeterministicModelBisimulationDecomposition<ValueType>::Partition::print() const {
+        void DeterministicModelStrongBisimulationDecomposition<ValueType>::Partition::print() const {
             for (auto const& block : this->blocks) {
                 block.print(*this);
             }
@@ -469,24 +542,49 @@ namespace storm {
         }
         
         template<typename ValueType>
-        std::size_t DeterministicModelBisimulationDecomposition<ValueType>::Partition::size() const {
+        std::size_t DeterministicModelStrongBisimulationDecomposition<ValueType>::Partition::size() const {
             return this->blocks.size();
         }
         
         template<typename ValueType>
-        DeterministicModelBisimulationDecomposition<ValueType>::DeterministicModelBisimulationDecomposition(storm::models::Dtmc<ValueType> const& dtmc, bool weak, bool buildQuotient) {
-            STORM_LOG_THROW(!dtmc.hasStateRewards() && !dtmc.hasTransitionRewards(), storm::exceptions::IllegalFunctionCallException, "Bisimulation is currently only supported for models without reward structures.");
-            computeBisimulationEquivalenceClasses(dtmc, weak, buildQuotient);
+        DeterministicModelStrongBisimulationDecomposition<ValueType>::DeterministicModelStrongBisimulationDecomposition(storm::models::Dtmc<ValueType> const& model, bool buildQuotient) {
+            STORM_LOG_THROW(!model.hasStateRewards() && !model.hasTransitionRewards(), storm::exceptions::IllegalFunctionCallException, "Bisimulation is currently only supported for models without reward structures.");
+            Partition initialPartition = getLabelBasedInitialPartition(model);
+            partitionRefinement(model, model.getBackwardTransitions(), initialPartition, buildQuotient);
+        }
+
+        template<typename ValueType>
+        DeterministicModelStrongBisimulationDecomposition<ValueType>::DeterministicModelStrongBisimulationDecomposition(storm::models::Ctmc<ValueType> const& model, bool buildQuotient) {
+            STORM_LOG_THROW(!model.hasStateRewards() && !model.hasTransitionRewards(), storm::exceptions::IllegalFunctionCallException, "Bisimulation is currently only supported for models without reward structures.");
+            Partition initialPartition = getLabelBasedInitialPartition(model);
+            partitionRefinement(model, model.getBackwardTransitions(), initialPartition, buildQuotient);
+        }
+        
+        template<typename ValueType>
+        DeterministicModelStrongBisimulationDecomposition<ValueType>::DeterministicModelStrongBisimulationDecomposition(storm::models::Dtmc<ValueType> const& model, std::string const& phiLabel, std::string const& psiLabel, bool bounded, bool buildQuotient) {
+            STORM_LOG_THROW(!model.hasStateRewards() && !model.hasTransitionRewards(), storm::exceptions::IllegalFunctionCallException, "Bisimulation is currently only supported for models without reward structures.");
+            storm::storage::SparseMatrix<ValueType> backwardTransitions = model.getBackwardTransitions();
+            Partition initialPartition = getMeasureDrivenInitialPartition(model, backwardTransitions, phiLabel, psiLabel, bounded);
+            partitionRefinement(model, model.getBackwardTransitions(), initialPartition, buildQuotient);
+        }
+        
+        template<typename ValueType>
+        DeterministicModelStrongBisimulationDecomposition<ValueType>::DeterministicModelStrongBisimulationDecomposition(storm::models::Ctmc<ValueType> const& model, std::string const& phiLabel, std::string const& psiLabel, bool bounded, bool buildQuotient) {
+            STORM_LOG_THROW(!model.hasStateRewards() && !model.hasTransitionRewards(), storm::exceptions::IllegalFunctionCallException, "Bisimulation is currently only supported for models without reward structures.");
+            storm::storage::SparseMatrix<ValueType> backwardTransitions = model.getBackwardTransitions();
+            Partition initialPartition = getMeasureDrivenInitialPartition(model, backwardTransitions, phiLabel, psiLabel, bounded);
+            partitionRefinement(model, model.getBackwardTransitions(), initialPartition, buildQuotient);
         }
         
         template<typename ValueType>
-        std::shared_ptr<storm::models::AbstractDeterministicModel<ValueType>> DeterministicModelBisimulationDecomposition<ValueType>::getQuotient() const {
+        std::shared_ptr<storm::models::AbstractDeterministicModel<ValueType>> DeterministicModelStrongBisimulationDecomposition<ValueType>::getQuotient() const {
             STORM_LOG_THROW(this->quotient != nullptr, storm::exceptions::IllegalFunctionCallException, "Unable to retrieve quotient model from bisimulation decomposition, because it was not built.");
             return this->quotient;
         }
         
         template<typename ValueType>
-        void DeterministicModelBisimulationDecomposition<ValueType>::buildQuotient(storm::models::Dtmc<ValueType> const& dtmc, Partition const& partition) {
+        template<typename ModelType>
+        void DeterministicModelStrongBisimulationDecomposition<ValueType>::buildQuotient(ModelType const& model, Partition const& partition) {
             // In order to create the quotient model, we need to construct
             // (a) the new transition matrix,
             // (b) the new labeling,
@@ -496,8 +594,8 @@ namespace storm {
             storm::storage::SparseMatrixBuilder<ValueType> builder(this->size(), this->size());
             
             // Prepare the new state labeling for (b).
-            storm::models::AtomicPropositionsLabeling newLabeling(this->size(), dtmc.getStateLabeling().getNumberOfAtomicPropositions());
-            std::set<std::string> atomicPropositionsSet = dtmc.getStateLabeling().getAtomicPropositions();
+            storm::models::AtomicPropositionsLabeling newLabeling(this->size(), model.getStateLabeling().getNumberOfAtomicPropositions());
+            std::set<std::string> atomicPropositionsSet = model.getStateLabeling().getAtomicPropositions();
             std::vector<std::string> atomicPropositions = std::vector<std::string>(atomicPropositionsSet.begin(), atomicPropositionsSet.end());
             for (auto const& ap : atomicPropositions) {
                 newLabeling.addAtomicProposition(ap);
@@ -510,33 +608,50 @@ namespace storm {
                 // Pick one representative state. It doesn't matter which state it is, because they all behave equally.
                 storm::storage::sparse::state_type representativeState = *block.begin();
                 
-                // Compute the outgoing transitions of the block.
-                std::map<storm::storage::sparse::state_type, ValueType> blockProbability;
-                for (auto const& entry : dtmc.getTransitionMatrix().getRow(representativeState)) {
-                    storm::storage::sparse::state_type targetBlock = partition.getBlock(entry.getColumn()).getId();
-                    auto probIterator = blockProbability.find(targetBlock);
-                    if (probIterator != blockProbability.end()) {
-                        probIterator->second += entry.getValue();
-                    } else {
-                        blockProbability[targetBlock] = entry.getValue();
-                    }
-                }
-                
-                // Now add them to the actual matrix.
-                for (auto const& probabilityEntry : blockProbability) {
-                    builder.addNextValue(blockIndex, probabilityEntry.first, probabilityEntry.second);
-                }
+                Block const& oldBlock = partition.getBlock(representativeState);
                 
-                // Add all atomic propositions to the equivalence class that the representative state satisfies.
-                for (auto const& ap : atomicPropositions) {
-                    if (dtmc.getStateLabeling().getStateHasAtomicProposition(ap, representativeState)) {
-                        newLabeling.addAtomicPropositionToState(ap, blockIndex);
+                // If the block is absorbing, we simply add a self-loop.
+                if (oldBlock.isAbsorbing()) {
+                    builder.addNextValue(blockIndex, blockIndex, storm::utility::constantOne<ValueType>());
+                    
+                    if (oldBlock.hasLabel()) {
+                        newLabeling.addAtomicPropositionToState(oldBlock.getLabel(), blockIndex);
+                    }
+                } else {
+                    // Compute the outgoing transitions of the block.
+                    std::map<storm::storage::sparse::state_type, ValueType> blockProbability;
+                    for (auto const& entry : model.getTransitionMatrix().getRow(representativeState)) {
+                        storm::storage::sparse::state_type targetBlock = partition.getBlock(entry.getColumn()).getId();
+                        auto probIterator = blockProbability.find(targetBlock);
+                        if (probIterator != blockProbability.end()) {
+                            probIterator->second += entry.getValue();
+                        } else {
+                            blockProbability[targetBlock] = entry.getValue();
+                        }
+                    }
+                    
+                    // Now add them to the actual matrix.
+                    for (auto const& probabilityEntry : blockProbability) {
+                        builder.addNextValue(blockIndex, probabilityEntry.first, probabilityEntry.second);
+                    }
+                    
+                    // If the block has a special label, we only add that to the block.
+                    if (oldBlock.hasLabel()) {
+                        newLabeling.addAtomicPropositionToState(oldBlock.getLabel(), blockIndex);
+                    } else {
+                        // Otherwise add all atomic propositions to the equivalence class that the representative state
+                        // satisfies.
+                        for (auto const& ap : atomicPropositions) {
+                            if (model.getStateLabeling().getStateHasAtomicProposition(ap, representativeState)) {
+                                newLabeling.addAtomicPropositionToState(ap, blockIndex);
+                            }
+                        }
                     }
                 }
             }
             
             // Now check which of the blocks of the partition contain at least one initial state.
-            for (auto initialState : dtmc.getInitialStates()) {
+            for (auto initialState : model.getInitialStates()) {
                 Block const& initialBlock = partition.getBlock(initialState);
                 newLabeling.addAtomicPropositionToState("init", initialBlock.getId());
             }
@@ -545,33 +660,21 @@ namespace storm {
             // If reward structures are allowed, the quotient structures need to be built here.
             
             // Finally construct the quotient model.
-            this->quotient = std::shared_ptr<storm::models::AbstractDeterministicModel<ValueType>>(new storm::models::Dtmc<ValueType>(builder.build(), std::move(newLabeling)));
+            this->quotient = std::shared_ptr<storm::models::AbstractDeterministicModel<ValueType>>(new ModelType(builder.build(), std::move(newLabeling)));
         }
         
         template<typename ValueType>
-        void DeterministicModelBisimulationDecomposition<ValueType>::computeBisimulationEquivalenceClasses(storm::models::Dtmc<ValueType> const& dtmc, bool weak, bool buildQuotient) {
+        template<typename ModelType>
+        void DeterministicModelStrongBisimulationDecomposition<ValueType>::partitionRefinement(ModelType const& model, storm::storage::SparseMatrix<ValueType> const& backwardTransitions, Partition& partition, bool buildQuotient) {
             std::chrono::high_resolution_clock::time_point totalStart = std::chrono::high_resolution_clock::now();
             
-            // We start by computing the initial partition.
-            Partition partition(dtmc.getNumberOfStates());
-            for (auto const& label : dtmc.getStateLabeling().getAtomicPropositions()) {
-                if (label == "init") {
-                    continue;
-                }
-                partition.splitLabel(dtmc.getLabeledStates(label));
-            }
-            
             // Initially, all blocks are potential splitter, so we insert them in the splitterQueue.
             std::deque<Block*> splitterQueue;
             std::for_each(partition.getBlocks().begin(), partition.getBlocks().end(), [&] (Block& a) { splitterQueue.push_back(&a); });
             
-            // FIXME: if weak is set, we want to eliminate the self-loops before doing bisimulation.
-            
-            storm::storage::SparseMatrix<ValueType> backwardTransitions = dtmc.getBackwardTransitions();
-            
             // Then perform the actual splitting until there are no more splitters.
             while (!splitterQueue.empty()) {
-                splitPartition(backwardTransitions, *splitterQueue.front(), partition, splitterQueue);
+                refinePartition(backwardTransitions, *splitterQueue.front(), partition, splitterQueue);
                 splitterQueue.pop_front();
             }
             
@@ -579,12 +682,14 @@ namespace storm {
             // a way that maintains the block IDs as indices.
             this->blocks.resize(partition.size());
             for (auto const& block : partition.getBlocks()) {
+                // We need to sort the states to allow for rapid construction of the blocks.
+                std::sort(partition.getBeginOfStates(block), partition.getEndOfStates(block));
                 this->blocks[block.getId()] = block_type(partition.getBeginOfStates(block), partition.getEndOfStates(block), true);
             }
 
             // If we are required to build the quotient model, do so now.
             if (buildQuotient) {
-                this->buildQuotient(dtmc, partition);
+                this->buildQuotient(model, partition);
             }
             
             std::chrono::high_resolution_clock::duration totalTime = std::chrono::high_resolution_clock::now() - totalStart;
@@ -595,7 +700,7 @@ namespace storm {
         }
         
         template<typename ValueType>
-        std::size_t DeterministicModelBisimulationDecomposition<ValueType>::splitBlockProbabilities(Block& block, Partition& partition, std::deque<Block*>& splitterQueue) {
+        void DeterministicModelStrongBisimulationDecomposition<ValueType>::refineBlockProbabilities(Block& block, Partition& partition, std::deque<Block*>& splitterQueue) {
             // Sort the states in the block based on their probabilities.
             std::sort(partition.getBeginOfStates(block), partition.getEndOfStates(block), [&partition] (storm::storage::sparse::state_type const& a, storm::storage::sparse::state_type const& b) { return partition.getValue(a) < partition.getValue(b); } );
             
@@ -615,8 +720,7 @@ namespace storm {
             
             // Now we can check whether the block needs to be split, which is the case iff the probabilities for the
             // first and the last state are different.
-            std::size_t createdBlocks = 0;
-            while (std::abs(partition.getValueAtPosition(beginIndex) - partition.getValueAtPosition(endIndex - 1)) >= 1e-6) {
+            while (!comparator.isEqual(partition.getValueAtPosition(beginIndex), partition.getValueAtPosition(endIndex - 1))) {
                 // Now we scan for the first state in the block that disagrees on the probability value.
                 // Note that we do not have to check currentIndex for staying within bounds, because we know the matching
                 // state is within bounds.
@@ -624,13 +728,12 @@ namespace storm {
                 
                 typename std::vector<ValueType>::const_iterator valueIterator = partition.getValues().begin() + beginIndex + 1;
                 ++currentIndex;
-                while (currentIndex < endIndex && std::abs(*valueIterator - currentValue) <= 1e-6) {
+                while (currentIndex < endIndex && comparator.isEqual(*valueIterator, currentValue)) {
                     ++valueIterator;
                     ++currentIndex;
                 }
                 
                 // Now we split the block and mark it as a potential splitter.
-                ++createdBlocks;
                 Block& newBlock = partition.splitBlock(block, currentIndex);
                 if (!newBlock.isMarkedAsSplitter()) {
                     splitterQueue.push_back(&newBlock);
@@ -638,12 +741,10 @@ namespace storm {
                 }
                 beginIndex = currentIndex;
             }
-            
-            return createdBlocks;
         }
         
         template<typename ValueType>
-        std::size_t DeterministicModelBisimulationDecomposition<ValueType>::splitPartition(storm::storage::SparseMatrix<ValueType> const& backwardTransitions, Block& splitter, Partition& partition, std::deque<Block*>& splitterQueue) {
+        void DeterministicModelStrongBisimulationDecomposition<ValueType>::refinePartition(storm::storage::SparseMatrix<ValueType> const& backwardTransitions, Block& splitter, Partition& partition, std::deque<Block*>& splitterQueue) {
             std::list<Block*> predecessorBlocks;
             
             // Iterate over all states of the splitter and check its predecessors.
@@ -658,7 +759,7 @@ namespace storm {
                     Block& predecessorBlock = partition.getBlock(predecessor);
                     
                     // If the predecessor block has just one state, there is no point in splitting it.
-                    if (predecessorBlock.getNumberOfStates() <= 1) {
+                    if (predecessorBlock.getNumberOfStates() <= 1 || predecessorBlock.isAbsorbing()) {
                         continue;
                     }
                     
@@ -773,12 +874,31 @@ namespace storm {
                     continue;
                 }
                 
-                splitBlockProbabilities(*blockPtr, partition, splitterQueue);
+                refineBlockProbabilities(*blockPtr, partition, splitterQueue);
             }
-            
-            return 0;
         }
         
-        template class DeterministicModelBisimulationDecomposition<double>;
+        template<typename ValueType>
+        template<typename ModelType>
+        typename DeterministicModelStrongBisimulationDecomposition<ValueType>::Partition DeterministicModelStrongBisimulationDecomposition<ValueType>::getMeasureDrivenInitialPartition(ModelType const& model, storm::storage::SparseMatrix<ValueType> const& backwardTransitions, std::string const& phiLabel, std::string const& psiLabel, bool bounded) {
+            std::pair<storm::storage::BitVector, storm::storage::BitVector> statesWithProbability01 = storm::utility::graph::performProb01(backwardTransitions, phiLabel == "true" ? storm::storage::BitVector(model.getNumberOfStates(), true) : model.getLabeledStates(phiLabel), model.getLabeledStates(psiLabel));
+            Partition partition(model.getNumberOfStates(), statesWithProbability01.first, bounded ? model.getLabeledStates(psiLabel) : statesWithProbability01.second, phiLabel, psiLabel);
+            return partition;
+        }
+        
+        template<typename ValueType>
+        template<typename ModelType>
+        typename DeterministicModelStrongBisimulationDecomposition<ValueType>::Partition DeterministicModelStrongBisimulationDecomposition<ValueType>::getLabelBasedInitialPartition(ModelType const& model) {
+            Partition partition(model.getNumberOfStates());
+            for (auto const& label : model.getStateLabeling().getAtomicPropositions()) {
+                if (label == "init") {
+                    continue;
+                }
+                partition.splitLabel(model.getLabeledStates(label));
+            }
+            return partition;
+        }
+        
+        template class DeterministicModelStrongBisimulationDecomposition<double>;
     }
 }
\ No newline at end of file
diff --git a/src/storage/DeterministicModelBisimulationDecomposition.h b/src/storage/DeterministicModelStrongBisimulationDecomposition.h
similarity index 56%
rename from src/storage/DeterministicModelBisimulationDecomposition.h
rename to src/storage/DeterministicModelStrongBisimulationDecomposition.h
index c1ca37c18..e5ac547db 100644
--- a/src/storage/DeterministicModelBisimulationDecomposition.h
+++ b/src/storage/DeterministicModelStrongBisimulationDecomposition.h
@@ -1,5 +1,5 @@
-#ifndef STORM_STORAGE_DETERMINISTICMODELBISIMULATIONDECOMPOSITION_H_
-#define STORM_STORAGE_DETERMINISTICMODELBISIMULATIONDECOMPOSITION_H_
+#ifndef STORM_STORAGE_DETERMINISTICMODELSTRONGBISIMULATIONDECOMPOSITION_H_
+#define STORM_STORAGE_DETERMINISTICMODELSTRONGBISIMULATIONDECOMPOSITION_H_
 
 #include <queue>
 #include <deque>
@@ -7,21 +7,59 @@
 #include "src/storage/sparse/StateType.h"
 #include "src/storage/Decomposition.h"
 #include "src/models/Dtmc.h"
+#include "src/models/Ctmc.h"
 #include "src/storage/Distribution.h"
+#include "src/utility/ConstantsComparator.h"
+#include "src/utility/OsDetection.h"
 
 namespace storm {
     namespace storage {
         
         /*!
-         * This class represents the decomposition model into its bisimulation quotient.
+         * This class represents the decomposition model into its (strong) bisimulation quotient.
          */
         template <typename ValueType>
-        class DeterministicModelBisimulationDecomposition : public Decomposition<StateBlock> {
+        class DeterministicModelStrongBisimulationDecomposition : public Decomposition<StateBlock> {
         public:
             /*!
-             * Decomposes the given DTMC into equivalence classes under weak or strong bisimulation.
+             * Decomposes the given DTMC into equivalence classes under strong bisimulation.
+             *
+             * @param model The model to decompose.
+             * @param buildQuotient Sets whether or not the quotient model is to be built.
+             */
+            DeterministicModelStrongBisimulationDecomposition(storm::models::Dtmc<ValueType> const& model, bool buildQuotient = false);
+
+            /*!
+             * Decomposes the given CTMC into equivalence classes under strong bisimulation.
+             *
+             * @param model The model to decompose.
+             * @param buildQuotient Sets whether or not the quotient model is to be built.
+             */
+            DeterministicModelStrongBisimulationDecomposition(storm::models::Ctmc<ValueType> const& model, bool buildQuotient = false);
+            
+            /*!
+             * Decomposes the given DTMC into equivalence classes under strong bisimulation in a way that onle safely
+             * preserves formulas of the form phi until psi.
+             *
+             * @param model The model to decompose.
+             * @param phiLabel The label that all phi states carry in the model.
+             * @param psiLabel The label that all psi states carry in the model.
+             * @param bounded If set to true, also bounded until formulas are preserved.
+             * @param buildQuotient Sets whether or not the quotient model is to be built.
              */
-            DeterministicModelBisimulationDecomposition(storm::models::Dtmc<ValueType> const& model, bool weak = false, bool buildQuotient = false);
+            DeterministicModelStrongBisimulationDecomposition(storm::models::Dtmc<ValueType> const& model, std::string const& phiLabel, std::string const& psiLabel, bool bounded, bool buildQuotient = false);
+            
+            /*!
+             * Decomposes the given CTMC into equivalence classes under strong bisimulation in a way that onle safely
+             * preserves formulas of the form phi until psi.
+             *
+             * @param model The model to decompose.
+             * @param phiLabel The label that all phi states carry in the model.
+             * @param psiLabel The label that all psi states carry in the model.
+             * @param bounded If set to true, also bounded until formulas are preserved.
+             * @param buildQuotient Sets whether or not the quotient model is to be built.
+             */
+            DeterministicModelStrongBisimulationDecomposition(storm::models::Ctmc<ValueType> const& model, std::string const& phiLabel, std::string const& psiLabel, bool bounded, bool buildQuotient = false);
             
             /*!
              * Retrieves the quotient of the model under the previously computed bisimulation.
@@ -37,7 +75,7 @@ namespace storm {
             public:
                 typedef typename std::list<Block>::const_iterator const_iterator;
                 
-                Block(storm::storage::sparse::state_type begin, storm::storage::sparse::state_type end, Block* prev, Block* next);
+                Block(storm::storage::sparse::state_type begin, storm::storage::sparse::state_type end, Block* prev, Block* next, std::shared_ptr<std::string> const& label = nullptr);
                 
                 // Prints the block.
                 void print(Partition const& partition) const;
@@ -138,6 +176,21 @@ namespace storm {
                 // Removes the marking.
                 void unmarkAsPredecessorBlock();
                 
+                // Sets whether or not the block is to be interpreted as absorbing.
+                void setAbsorbing(bool absorbing);
+                
+                // Retrieves whether the block is to be interpreted as absorbing.
+                bool isAbsorbing() const;
+                
+                // Retrieves whether the block has a special label.
+                bool hasLabel() const;
+                
+                // Retrieves the special label of the block if it has one.
+                std::string const& getLabel() const;
+                
+                // Retrieves a pointer to the label of the block (which is the nullptr if there is none).
+                std::shared_ptr<std::string> const& getLabelPtr() const;
+                
             private:
                 // An iterator to itself. This is needed to conveniently insert elements in the overall list of blocks
                 // kept by the partition.
@@ -160,8 +213,17 @@ namespace storm {
                 // A position that can be used to store a certain position within the block.
                 storm::storage::sparse::state_type markedPosition;
                 
+                // A flag indicating whether the block is to be interpreted as absorbing or not.
+                bool absorbing;
+                
                 // The ID of the block. This is only used for debugging purposes.
                 std::size_t id;
+                
+                // The label of the block or nullptr if it has none.
+                std::shared_ptr<std::string> label;
+                
+                // A counter for the IDs of the blocks.
+                static std::size_t blockId;
             };
             
             class Partition {
@@ -170,8 +232,31 @@ namespace storm {
                 
                 /*!
                  * Creates a partition with one block consisting of all the states.
+                 *
+                 * @param numberOfStates The number of states the partition holds.
                  */
                 Partition(std::size_t numberOfStates);
+
+                /*!
+                 * Creates a partition with three blocks: one with all phi states, one with all psi states and one with
+                 * all other states. The former two blocks are marked as being absorbing, because their outgoing
+                 * transitions shall not be taken into account for future refinement.
+                 *
+                 * @param numberOfStates The number of states the partition holds.
+                 * @param prob0States The states which have probability 0 of satisfying phi until psi.
+                 * @param prob1States The states which have probability 1 of satisfying phi until psi.
+                 * @param otherLabel The label that is to be attached to all other states.
+                 * @param prob1Label The label that is to be attached to all states with probability 1.
+                 */
+                Partition(std::size_t numberOfStates, storm::storage::BitVector const& prob0States, storm::storage::BitVector const& prob1States, std::string const& otherLabel, std::string const& prob1Label);
+                
+                Partition() = default;
+                Partition(Partition const& other) = default;
+                Partition& operator=(Partition const& other) = default;
+#ifndef WINDOWS
+                Partition(Partition&& other) = default;
+                Partition& operator=(Partition&& other) = default;
+#endif
                 
                 /*!
                  * Splits all blocks of the partition such that afterwards all blocks contain only states with the label
@@ -262,6 +347,8 @@ namespace storm {
                 // Updates the block mapping for the given range of states to the specified block.
                 void updateBlockMapping(Block& block, std::vector<storm::storage::sparse::state_type>::iterator first, std::vector<storm::storage::sparse::state_type>::iterator end);
                 
+                // Retrieves the first block of the partition.
+                Block& getFirstBlock();
             private:
                 // The list of blocks in the partition.
                 std::list<Block> blocks;
@@ -280,18 +367,86 @@ namespace storm {
                 std::vector<ValueType> values;
             };
             
-            void computeBisimulationEquivalenceClasses(storm::models::Dtmc<ValueType> const& model, bool weak, bool buildQuotient);
+            /*!
+             * Performs the partition refinement on the model and thereby computes the equivalence classes under strong
+             * bisimulation equivalence. If required, the quotient model is built and may be retrieved using
+             * getQuotient().
+             *
+             * @param model The model on whose state space to compute the coarses strong bisimulation relation.
+             * @param backwardTransitions The backward transitions of the model.
+             * @param The initial partition.
+             * @param buildQuotient If set, the quotient model is built and may be retrieved using the getQuotient()
+             * method.
+             */
+            template<typename ModelType>
+            void partitionRefinement(ModelType const& model, storm::storage::SparseMatrix<ValueType> const& backwardTransitions, Partition& partition, bool buildQuotient);
             
-            std::size_t splitPartition(storm::storage::SparseMatrix<ValueType> const& backwardTransitions, Block& splitter, Partition& partition, std::deque<Block*>& splitterQueue);
+            /*!
+             * Refines the partition based on the provided splitter. After calling this method all blocks are stable
+             * with respect to the splitter.
+             *
+             * @param backwardTransitions A matrix that can be used to retrieve the predecessors (and their
+             * probabilities).
+             * @param splitter The splitter to use.
+             * @param partition The partition to split.
+             * @param splitterQueue A queue into which all blocks that were split are inserted so they can be treated
+             * as splitters in the future.
+             */
+            void refinePartition(storm::storage::SparseMatrix<ValueType> const& backwardTransitions, Block& splitter, Partition& partition, std::deque<Block*>& splitterQueue);
             
-            std::size_t splitBlockProbabilities(Block& block, Partition& partition, std::deque<Block*>& splitterQueue);
+            /*!
+             * Refines the block based on their probability values (leading into the splitter).
+             *
+             * @param block The block to refine.
+             * @param partition The partition that contains the block.
+             * @param splitterQueue A queue into which all blocks that were split are inserted so they can be treated
+             * as splitters in the future.
+             */
+            void refineBlockProbabilities(Block& block, Partition& partition, std::deque<Block*>& splitterQueue);
             
-            void buildQuotient(storm::models::Dtmc<ValueType> const& dtmc, Partition const& partition);
+            /*!
+             * Builds the quotient model based on the previously computed equivalence classes (stored in the blocks
+             * of the decomposition.
+             *
+             * @param model The model whose state space was used for computing the equivalence classes. This is used for
+             * determining the transitions of each equivalence class.
+             * @param partition The previously computed partition. This is used for quickly retrieving the block of a
+             * state.
+             */
+            template<typename ModelType>
+            void buildQuotient(ModelType const& model, Partition const& partition);
 
+            /*!
+             * Creates the measure-driven initial partition for reaching psi states from phi states.
+             *
+             * @param model The model whose state space is partitioned based on reachability of psi states from phi
+             * states.
+             * @param backwardTransitions The backward transitions of the model.
+             * @param phiLabel The label that all phi states carry in the model.
+             * @param psiLabel The label that all psi states carry in the model.
+             * @param bounded If set to true, the initial partition will be chosen in such a way that preserves bounded
+             * reachability queries.
+             * @return The resulting partition.
+             */
+            template<typename ModelType>
+            Partition getMeasureDrivenInitialPartition(ModelType const& model, storm::storage::SparseMatrix<ValueType> const& backwardTransitions, std::string const& phiLabel, std::string const& psiLabel, bool bounded = false);
+            
+            /*!
+             * Creates the initial partition based on all the labels in the given model.
+             *
+             * @param model The model whose state space is partitioned based on its labels.
+             * @return The resulting partition.
+             */
+            template<typename ModelType>
+            Partition getLabelBasedInitialPartition(ModelType const& model);
+            
             // If required, a quotient model is built and stored in this member.
             std::shared_ptr<storm::models::AbstractDeterministicModel<ValueType>> quotient;
+            
+            // A comparator that is used for determining whether two probabilities are considered to be equal.
+            storm::utility::ConstantsComparator<ValueType> comparator;
         };
     }
 }
 
-#endif /* STORM_STORAGE_DETERMINISTICMODELBISIMULATIONDECOMPOSITION_H_ */
\ No newline at end of file
+#endif /* STORM_STORAGE_DETERMINISTICMODELSTRONGBISIMULATIONDECOMPOSITION_H_ */
\ No newline at end of file
diff --git a/src/utility/ConstantsComparator.h b/src/utility/ConstantsComparator.h
new file mode 100644
index 000000000..fc46b251f
--- /dev/null
+++ b/src/utility/ConstantsComparator.h
@@ -0,0 +1,84 @@
+#ifndef STORM_UTILITY_CONSTANTSCOMPARATOR_H_
+#define STORM_UTILITY_CONSTANTSCOMPARATOR_H_
+
+#ifdef max
+#	undef max
+#endif
+
+#ifdef min
+#	undef min
+#endif
+
+#include <limits>
+#include <cstdint>
+
+#include "src/settings/SettingsManager.h"
+
+namespace storm {
+    namespace utility {
+        
+        template<typename ValueType>
+        ValueType one() {
+            return ValueType(1);
+        }
+        
+        template<typename ValueType>
+        ValueType zero() {
+            return ValueType(0);
+        }
+        
+        template<typename ValueType>
+        ValueType infinity() {
+            return std::numeric_limits<ValueType>::infinity();
+        }
+        
+        // A class that can be used for comparing constants.
+        template<typename ValueType>
+        class ConstantsComparator {
+        public:
+            bool isOne(ValueType const& value) {
+                return value == one<ValueType>();
+            }
+            
+            bool isZero(ValueType const& value) {
+                return value == zero<ValueType>();
+            }
+            
+            bool isEqual(ValueType const& value1, ValueType const& value2) {
+                return value1 == value2;
+            }
+        };
+        
+        // For doubles we specialize this class and consider the comparison modulo some predefined precision.
+        template<>
+        class ConstantsComparator<double> {
+        public:
+            ConstantsComparator() : precision(storm::settings::generalSettings().getPrecision()) {
+                // Intentionally left empty.
+            }
+
+            ConstantsComparator(double precision) : precision(precision) {
+                // Intentionally left empty.
+            }
+            
+            bool isOne(double const& value) {
+                return std::abs(value - one<double>()) <= precision;
+            }
+            
+            bool isZero(double const& value) {
+                return std::abs(value) <= precision;
+            }
+            
+            bool isEqual(double const& value1, double const& value2) {
+                return std::abs(value1 - value2) <= precision;
+            }
+            
+        private:
+            // The precision used for comparisons.
+            double precision;
+        };
+        
+    }
+}
+
+#endif /* STORM_UTILITY_CONSTANTSCOMPARATOR_H_ */
\ No newline at end of file
diff --git a/src/utility/cli.h b/src/utility/cli.h
index 813a6fa74..908d7dc52 100644
--- a/src/utility/cli.h
+++ b/src/utility/cli.h
@@ -47,7 +47,7 @@ log4cplus::Logger logger;
 
 // Headers for model processing.
 #include "src/storage/NaiveDeterministicModelBisimulationDecomposition.h"
-#include "src/storage/DeterministicModelBisimulationDecomposition.h"
+#include "src/storage/DeterministicModelStrongBisimulationDecomposition.h"
 
 // Headers for counterexample generation.
 #include "src/counterexamples/MILPMinimalLabelSetGenerator.h"
@@ -267,7 +267,7 @@ namespace storm {
                     std::shared_ptr<storm::models::Dtmc<double>> dtmc = result->template as<storm::models::Dtmc<double>>();
                     
                     STORM_PRINT(std::endl << "Performing bisimulation minimization..." << std::endl);
-                    storm::storage::DeterministicModelBisimulationDecomposition<double> bisimulationDecomposition(*dtmc, false, true);
+                    storm::storage::DeterministicModelStrongBisimulationDecomposition<double> bisimulationDecomposition(*dtmc, true);
                     
                     result = bisimulationDecomposition.getQuotient();
                     
@@ -285,7 +285,7 @@ namespace storm {
                     
                     std::shared_ptr<storm::models::Mdp<double>> mdp = model->as<storm::models::Mdp<double>>();
 
-                    // FIXME: re-parse the program.
+                    // FIXME: do not re-parse the program.
                     std::string const programFile = storm::settings::generalSettings().getSymbolicModelFilename();
                     std::string const constants = storm::settings::generalSettings().getConstantDefinitionString();
                     storm::prism::Program program = storm::parser::PrismParser::parse(programFile);
diff --git a/src/utility/graph.h b/src/utility/graph.h
index 21972adbc..3af235fe3 100644
--- a/src/utility/graph.h
+++ b/src/utility/graph.h
@@ -204,6 +204,25 @@ namespace storm {
                 return result;
             }
             
+            /*!
+             * Computes the sets of states that have probability 0 or 1, respectively, of satisfying phi until psi in a
+             * deterministic model.
+             *
+             * @param backwardTransitions The backward transitions of the model whose graph structure to search.
+             * @param phiStates The set of all states satisfying phi.
+             * @param psiStates The set of all states satisfying psi.
+             * @return A pair of bit vectors such that the first bit vector stores the indices of all states
+             * with probability 0 and the second stores all indices of states with probability 1.
+             */
+            template <typename T>
+            static std::pair<storm::storage::BitVector, storm::storage::BitVector> performProb01(storm::storage::SparseMatrix<T> backwardTransitions, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates) {
+                std::pair<storm::storage::BitVector, storm::storage::BitVector> result;
+                result.first = performProbGreater0(backwardTransitions, phiStates, psiStates);
+                result.second = performProb1(backwardTransitions, phiStates, psiStates, result.first);
+                result.first.complement();
+                return result;
+            }
+            
             /*!
              * Computes the sets of states that have probability greater 0 of satisfying phi until psi under at least
              * one possible resolution of non-determinism in a non-deterministic model. Stated differently,