@ -8,23 +8,30 @@
namespace storm {
namespace storage {
static in t globalId = 0 ;
static std : : size_ t globalId = 0 ;
template < typename ValueType >
BisimulationDecomposition2 < ValueType > : : Block : : Block ( storm : : storage : : sparse : : state_type begin , storm : : storage : : sparse : : state_type end , Block * prev , Block * next ) : originalBegin ( begin ) , begin ( begin ) , end ( end ) , prev ( prev ) , next ( next ) , numberOfStates ( end - begin ) , isMarked ( false ) , myId ( globalId + + ) {
// Intentionally left empty.
std : : cout < < " created new block from " < < begin < < " to " < < end < < std : : endl ;
BisimulationDecomposition2 < 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 ) , markedPosition ( begin ) , id ( globalId + + ) {
if ( next ! = nullptr ) {
next - > prev = this ;
}
if ( prev ! = nullptr ) {
prev - > next = this ;
}
}
template < typename ValueType >
void BisimulationDecomposition2 < ValueType > : : Block : : print ( Partition const & partition ) const {
std : : cout < < " block " < < this - > myId < < " and ptr " < < this < < std : : endl ;
std : : cout < < " begin: " < < this - > begin < < " and end: " < < this - > end < < " (number of states: " < < this - > numberOfStates < < " ) " < < std : : endl ;
std : : cout < < " next: " < < this - > next < < " and prev " < < this - > prev < < std : : endl ;
std : : cout < < " block " < < this - > getId ( ) < < " with marked position " < < this - > getMarkedPosition ( ) < < std : : endl ;
std : : cout < < " begin: " < < this - > begin < < " and end: " < < this - > end < < " (number of states: " < < this - > getNumberOfStates ( ) < < " ) " < < std : : endl ;
std : : cout < < " states: " < < std : : endl ;
for ( storm : : storage : : sparse : : state_type index = this - > begin ; index < this - > end ; + + index ) {
std : : cout < < partition . states [ index ] < < " " ;
}
std : : cout < < std : : endl < < " original: " < < std : : endl ;
for ( storm : : storage : : sparse : : state_type index = this - > getOriginalBegin ( ) ; index < this - > end ; + + index ) {
std : : cout < < partition . states [ index ] < < " " ;
}
std : : cout < < std : : endl < < " values: " < < std : : endl ;
for ( storm : : storage : : sparse : : state_type index = this - > begin ; index < this - > end ; + + index ) {
std : : cout < < std : : setprecision ( 3 ) < < partition . values [ index ] < < " " ;
@ -35,13 +42,187 @@ namespace storm {
template < typename ValueType >
void BisimulationDecomposition2 < ValueType > : : Block : : setBegin ( storm : : storage : : sparse : : state_type begin ) {
this - > begin = begin ;
this - > originalBegin = begin ;
this - > markedPosition = begin ;
}
template < typename ValueType >
void BisimulationDecomposition2 < ValueType > : : Block : : setEnd ( storm : : storage : : sparse : : state_type end ) {
this - > end = end ;
}
template < typename ValueType >
void BisimulationDecomposition2 < ValueType > : : Block : : incrementBegin ( ) {
+ + this - > begin ;
std : : cout < < " incremented begin to " < < this - > begin < < std : : endl ;
}
template < typename ValueType >
void BisimulationDecomposition2 < ValueType > : : Block : : decrementEnd ( ) {
+ + this - > begin ;
}
template < typename ValueType >
storm : : storage : : sparse : : state_type BisimulationDecomposition2 < ValueType > : : Block : : getBegin ( ) const {
return this - > begin ;
}
template < typename ValueType >
storm : : storage : : sparse : : state_type BisimulationDecomposition2 < ValueType > : : Block : : getOriginalBegin ( ) const {
if ( this - > hasPreviousBlock ( ) ) {
return this - > getPreviousBlock ( ) . getEnd ( ) ;
} else {
return 0 ;
}
}
template < typename ValueType >
storm : : storage : : sparse : : state_type BisimulationDecomposition2 < ValueType > : : Block : : getEnd ( ) const {
return this - > end ;
}
template < typename ValueType >
void BisimulationDecomposition2 < ValueType > : : Block : : setIterator ( const_iterator it ) {
this - > selfIt = it ;
}
template < typename ValueType >
typename BisimulationDecomposition2 < ValueType > : : Block : : const_iterator BisimulationDecomposition2 < ValueType > : : Block : : getIterator ( ) const {
return this - > selfIt ;
}
template < typename ValueType >
typename BisimulationDecomposition2 < ValueType > : : Block : : const_iterator BisimulationDecomposition2 < ValueType > : : Block : : getNextIterator ( ) const {
return this - > getNextBlock ( ) . getIterator ( ) ;
}
template < typename ValueType >
typename BisimulationDecomposition2 < ValueType > : : Block : : const_iterator BisimulationDecomposition2 < ValueType > : : Block : : getPreviousIterator ( ) const {
return this - > getPreviousBlock ( ) . getIterator ( ) ;
}
template < typename ValueType >
typename BisimulationDecomposition2 < ValueType > : : Block & BisimulationDecomposition2 < ValueType > : : Block : : getNextBlock ( ) {
return * this - > next ;
}
template < typename ValueType >
typename BisimulationDecomposition2 < ValueType > : : Block const & BisimulationDecomposition2 < ValueType > : : Block : : getNextBlock ( ) const {
return * this - > next ;
}
template < typename ValueType >
bool BisimulationDecomposition2 < ValueType > : : Block : : hasNextBlock ( ) const {
return this - > next ! = nullptr ;
}
template < typename ValueType >
typename BisimulationDecomposition2 < ValueType > : : Block & BisimulationDecomposition2 < ValueType > : : Block : : getPreviousBlock ( ) {
return * this - > prev ;
}
template < typename ValueType >
typename BisimulationDecomposition2 < ValueType > : : Block * BisimulationDecomposition2 < ValueType > : : Block : : getPreviousBlockPointer ( ) {
return this - > prev ;
}
template < typename ValueType >
typename BisimulationDecomposition2 < ValueType > : : Block * BisimulationDecomposition2 < ValueType > : : Block : : getNextBlockPointer ( ) {
return this - > next ;
}
template < typename ValueType >
typename BisimulationDecomposition2 < ValueType > : : Block const & BisimulationDecomposition2 < ValueType > : : Block : : getPreviousBlock ( ) const {
return * this - > prev ;
}
template < typename ValueType >
bool BisimulationDecomposition2 < ValueType > : : Block : : hasPreviousBlock ( ) const {
return this - > prev ! = nullptr ;
}
template < typename ValueType >
bool BisimulationDecomposition2 < ValueType > : : Block : : check ( ) const {
if ( this - > begin > = this - > end ) {
std : : cout < < " beg: " < < this - > begin < < " and end " < < this - > end < < std : : endl ;
assert ( false ) ;
}
if ( this - > prev ! = nullptr ) {
assert ( this - > prev - > next = = this ) ;
}
if ( this - > next ! = nullptr ) {
assert ( this - > next - > prev = = this ) ;
}
return true ;
}
template < typename ValueType >
std : : size_t BisimulationDecomposition2 < 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 BisimulationDecomposition2 < ValueType > : : Block : : isMarkedAsSplitter ( ) const {
return this - > markedAsSplitter ;
}
template < typename ValueType >
void BisimulationDecomposition2 < ValueType > : : Block : : markAsSplitter ( ) {
this - > markedAsSplitter = true ;
}
template < typename ValueType >
void BisimulationDecomposition2 < ValueType > : : Block : : unmarkAsSplitter ( ) {
this - > markedAsSplitter = false ;
}
template < typename ValueType >
std : : size_t BisimulationDecomposition2 < ValueType > : : Block : : getId ( ) const {
return this - > id ;
}
template < typename ValueType >
storm : : storage : : sparse : : state_type BisimulationDecomposition2 < ValueType > : : Block : : getMarkedPosition ( ) const {
return this - > markedPosition ;
}
template < typename ValueType >
void BisimulationDecomposition2 < ValueType > : : Block : : setMarkedPosition ( storm : : storage : : sparse : : state_type position ) {
this - > markedPosition = position ;
}
template < typename ValueType >
void BisimulationDecomposition2 < ValueType > : : Block : : resetMarkedPosition ( ) {
this - > markedPosition = this - > begin ;
}
template < typename ValueType >
void BisimulationDecomposition2 < ValueType > : : Block : : incrementMarkedPosition ( ) {
+ + this - > markedPosition ;
}
template < typename ValueType >
void BisimulationDecomposition2 < ValueType > : : Block : : markAsPredecessorBlock ( ) {
this - > markedAsPredecessorBlock = true ;
}
template < typename ValueType >
void BisimulationDecomposition2 < ValueType > : : Block : : unmarkAsPredecessorBlock ( ) {
this - > markedAsPredecessorBlock = false ;
}
template < typename ValueType >
bool BisimulationDecomposition2 < ValueType > : : Block : : isMarkedAsPredecessor ( ) const {
return markedAsPredecessorBlock ;
}
template < typename ValueType >
BisimulationDecomposition2 < 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 - > itToSelf = it ;
it - > setIterator ( it ) ;
// Set up the different parts of the internal structure.
for ( storm : : storage : : sparse : : state_type state = 0 ; state < numberOfStates ; + + state ) {
states [ state ] = state ;
positions [ state ] = state ;
@ -49,40 +230,192 @@ namespace storm {
}
}
template < typename ValueType >
void BisimulationDecomposition2 < ValueType > : : Partition : : swapStates ( storm : : storage : : sparse : : state_type state1 , storm : : storage : : sparse : : state_type state2 ) {
std : : cout < < " swapping states " < < state1 < < " and " < < state2 < < std : : endl ;
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 ] ) ;
assert ( this - > check ( ) ) ;
}
template < typename ValueType >
void BisimulationDecomposition2 < 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 ] ;
std : : cout < < " swapping states " < < state1 < < " and " < < state2 < < " at positions " < < position1 < < " and " < < position2 < < std : : endl ;
std : : swap ( this - > states [ position1 ] , this - > states [ position2 ] ) ;
std : : swap ( this - > values [ position1 ] , this - > values [ position2 ] ) ;
this - > positions [ state1 ] = position2 ;
this - > positions [ state2 ] = position1 ;
std : : cout < < " pos of " < < state1 < < " is now " < < position2 < < " and pos of " < < state2 < < " is now " < < position1 < < std : : endl ;
std : : cout < < this - > states [ position1 ] < < " vs " < < state2 < < " and " < < this - > states [ position2 ] < < " vs " < < state1 < < std : : endl ;
assert ( this - > check ( ) ) ;
}
template < typename ValueType >
storm : : storage : : sparse : : state_type const & BisimulationDecomposition2 < ValueType > : : Partition : : getPosition ( storm : : storage : : sparse : : state_type state ) const {
return this - > positions [ state ] ;
}
template < typename ValueType >
void BisimulationDecomposition2 < 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 & BisimulationDecomposition2 < ValueType > : : Partition : : getState ( storm : : storage : : sparse : : state_type position ) const {
return this - > states [ position ] ;
}
template < typename ValueType >
ValueType const & BisimulationDecomposition2 < ValueType > : : Partition : : getValue ( storm : : storage : : sparse : : state_type state ) const {
return this - > values [ this - > positions [ state ] ] ;
}
template < typename ValueType >
ValueType const & BisimulationDecomposition2 < ValueType > : : Partition : : getValueAtPosition ( storm : : storage : : sparse : : state_type position ) const {
return this - > values [ position ] ;
}
template < typename ValueType >
void BisimulationDecomposition2 < ValueType > : : Partition : : setValue ( storm : : storage : : sparse : : state_type state , ValueType value ) {
this - > values [ this - > positions [ state ] ] = value ;
}
template < typename ValueType >
std : : vector < ValueType > & BisimulationDecomposition2 < ValueType > : : Partition : : getValues ( ) {
return this - > values ;
}
template < typename ValueType >
void BisimulationDecomposition2 < ValueType > : : Partition : : increaseValue ( storm : : storage : : sparse : : state_type state , ValueType value ) {
this - > values [ this - > positions [ state ] ] + = value ;
}
template < typename ValueType >
void BisimulationDecomposition2 < 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 BisimulationDecomposition2 < ValueType > : : Block & BisimulationDecomposition2 < ValueType > : : Partition : : getBlock ( storm : : storage : : sparse : : state_type state ) {
return * this - > stateToBlockMapping [ state ] ;
}
template < typename ValueType >
std : : vector < storm : : storage : : sparse : : state_type > : : iterator BisimulationDecomposition2 < ValueType > : : Partition : : getBeginOfStates ( Block const & block ) {
return this - > states . begin ( ) + block . getBegin ( ) ;
}
template < typename ValueType >
std : : vector < storm : : storage : : sparse : : state_type > : : iterator BisimulationDecomposition2 < ValueType > : : Partition : : getEndOfStates ( Block const & block ) {
return this - > states . begin ( ) + block . getEnd ( ) ;
}
template < typename ValueType >
std : : vector < storm : : storage : : sparse : : state_type > : : const_iterator BisimulationDecomposition2 < 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 BisimulationDecomposition2 < ValueType > : : Partition : : getEndOfStates ( Block const & block ) const {
return this - > states . begin ( ) + block . getEnd ( ) ;
}
template < typename ValueType >
typename std : : vector < ValueType > : : iterator BisimulationDecomposition2 < ValueType > : : Partition : : getBeginOfValues ( Block const & block ) {
return this - > values . begin ( ) + block . getBegin ( ) ;
}
template < typename ValueType >
typename std : : vector < ValueType > : : iterator BisimulationDecomposition2 < ValueType > : : Partition : : getEndOfValues ( Block const & block ) {
return this - > values . begin ( ) + block . getEnd ( ) ;
}
template < typename ValueType >
typename BisimulationDecomposition2 < ValueType > : : Block & BisimulationDecomposition2 < 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.
std : : cout < < " splitting block ( " < < block . getBegin ( ) < < " , " < < block . getEnd ( ) < < " ) at position " < < position < < std : : endl ;
block . print ( * this ) ;
// In case one of the resulting blocks would be empty, we simply return the current block and do not create
// a new one.
if ( position = = block . getBegin ( ) | | position = = block . getEnd ( ) ) {
return block ;
}
// 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 ) ;
std : : cout < < " created new block from " < < block . getBegin ( ) < < " to " < < position < < std : : endl ;
selfIt - > setIterator ( selfIt ) ;
Block & newBlock = * selfIt ;
// Make the current block end at the given position.
block . setBegin ( position ) ;
std : : cout < < " old block: " < < std : : endl ;
block . print ( * this ) ;
// Update the mapping of the states in the newly created block.
for ( auto it = this - > getBeginOfStates ( newBlock ) , ite = this - > getEndOfStates ( newBlock ) ; it ! = ite ; + + it ) {
stateToBlockMapping [ * it ] = & newBlock ;
}
return newBlock ;
}
template < typename ValueType >
typename BisimulationDecomposition2 < ValueType > : : Block & BisimulationDecomposition2 < ValueType > : : Partition : : insertBlock ( Block & block ) {
// Find the beginning of the new block.
storm : : storage : : sparse : : state_type begin ;
if ( block . hasPreviousBlock ( ) ) {
begin = block . getPreviousBlock ( ) . getEnd ( ) ;
std : : cout < < " previous block ended at " < < begin < < std : : endl ;
block . getPreviousBlock ( ) . print ( * this ) ;
} else {
begin = 0 ;
}
// Actually insert the new block.
typename std : : list < Block > : : iterator it = this - > blocks . emplace ( block . getIterator ( ) , begin , block . getBegin ( ) , block . getPreviousBlockPointer ( ) , & block ) ;
Block & newBlock = * it ;
newBlock . setIterator ( it ) ;
// Update the mapping of the states in the newly created block.
for ( auto it = this - > getBeginOfStates ( newBlock ) , ite = this - > getEndOfStates ( newBlock ) ; it ! = ite ; + + it ) {
stateToBlockMapping [ * it ] = & newBlock ;
}
return * it ;
}
template < typename ValueType >
void BisimulationDecomposition2 < 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 ;
// Sort the range of the block such that all states that have the label are moved to the front.
std : : sort ( this - > states . begin ( ) + block . begin , this - > states . begin ( ) + block . end , [ & statesWithLabel ] ( storm : : storage : : sparse : : state_type const & a , storm : : storage : : sparse : : state_type const & b ) { return statesWithLabel . get ( a ) & & ! statesWithLabel . get ( b ) ; } ) ;
std : : sort ( this - > getBeginOfStates ( block ) , this - > getEndOfStates ( block ) , [ & statesWithLabel ] ( storm : : storage : : sparse : : state_type const & a , storm : : storage : : sparse : : state_type const & b ) { return statesWithLabel . get ( a ) & & ! statesWithLabel . get ( b ) ; } ) ;
// Update the positions vector.
storm : : storage : : sparse : : state_type position = block . begin ;
for ( auto stateIt = this - > states . begin ( ) + block . begin , stateIte = this - > states . begin ( ) + block . end ; stateIt ! = stateIte ; + + stateIt , + + position ) {
storm : : storage : : sparse : : state_type position = block . getBegin ( ) ;
for ( auto stateIt = this - > getBeginOfStates ( block ) , stateIte = this - > getEndOfStates ( block ) ; stateIt ! = stateIte ; + + stateIt , + + position ) {
this - > positions [ * stateIt ] = position ;
}
// Now we can find the first position in the block that does not have the label and create new blocks.
std : : vector < storm : : storage : : sparse : : state_type > : : iterator it = std : : find_if ( this - > states . begin ( ) + block . begin , this - > states . begin ( ) + block . end , [ & ] ( storm : : storage : : sparse : : state_type const & a ) { return ! statesWithLabel . get ( a ) ; } ) ;
std : : vector < storm : : storage : : sparse : : state_type > : : iterator it = std : : find_if ( this - > getBeginOfStates ( block ) , this - > getEndOfStates ( block ) , [ & ] ( storm : : storage : : sparse : : state_type const & a ) { return ! statesWithLabel . get ( a ) ; } ) ;
// If not all the states agreed on the validity of the label, we need to split the block.
if ( it ! = this - > states . begin ( ) + block . begin & & it ! = this - > states . begin ( ) + block . end ) {
if ( it ! = this - > getBeginOfStates ( block ) & & it ! = this - > getEndOfStates ( block ) ) {
auto cutPoint = std : : distance ( this - > states . begin ( ) , it ) ;
+ + blockIterator ;
auto newBlockIterator = this - > blocks . emplace ( blockIterator , cutPoint , block . end , & block , block . next ) ;
newBlockIterator - > itToSelf = newBlockIterator ;
// Make the old block end at the cut position and insert a new block after it.
block . end = cutPoint ;
block . next = & ( * newBlockIterator ) ;
block . numberOfStates = block . end - block . begin ;
// Update the block mapping for all states that we just removed from the block.
for ( auto it = this - > states . begin ( ) + newBlockIterator - > begin , ite = this - > states . begin ( ) + newBlockIterator - > end ; it ! = ite ; + + it ) {
stateToBlockMapping [ * it ] = & ( * newBlockIterator ) ;
}
this - > splitBlock ( block , cutPoint ) ;
} else {
// Otherwise, we simply proceed to the next block.
+ + blockIterator ;
@ -90,6 +423,40 @@ namespace storm {
}
}
template < typename ValueType >
std : : list < typename BisimulationDecomposition2 < ValueType > : : Block > const & BisimulationDecomposition2 < ValueType > : : Partition : : getBlocks ( ) const {
return this - > blocks ;
}
template < typename ValueType >
std : : vector < storm : : storage : : sparse : : state_type > & BisimulationDecomposition2 < ValueType > : : Partition : : getStates ( ) {
return this - > states ;
}
template < typename ValueType >
std : : list < typename BisimulationDecomposition2 < ValueType > : : Block > & BisimulationDecomposition2 < ValueType > : : Partition : : getBlocks ( ) {
return this - > blocks ;
}
template < typename ValueType >
bool BisimulationDecomposition2 < ValueType > : : Partition : : check ( ) const {
for ( uint_fast64_t state = 0 ; state < this - > positions . size ( ) ; + + state ) {
if ( this - > states [ this - > positions [ state ] ] ! = state ) {
assert ( false ) ;
}
}
for ( auto const & block : this - > blocks ) {
assert ( block . check ( ) ) ;
for ( auto stateIt = this - > getBeginOfStates ( block ) , stateIte = this - > getEndOfStates ( block ) ; stateIt ! = stateIte ; + + stateIt ) {
if ( this - > stateToBlockMapping [ * stateIt ] ! = & block ) {
std : : cout < < " state " < < * stateIt < < " has wrong block mapping " < < this - > stateToBlockMapping [ * stateIt ] < < " (should have " < < & block < < " ) " < < std : : endl ;
assert ( false ) ;
}
}
}
return true ;
}
template < typename ValueType >
void BisimulationDecomposition2 < ValueType > : : Partition : : print ( ) const {
for ( auto const & block : this - > blocks ) {
@ -109,6 +476,7 @@ namespace storm {
}
std : : cout < < std : : endl ;
std : : cout < < " size: " < < this - > blocks . size ( ) < < std : : endl ;
assert ( this - > check ( ) ) ;
}
template < typename ValueType >
@ -129,7 +497,7 @@ namespace storm {
template < typename ValueType >
void BisimulationDecomposition2 < ValueType > : : computeBisimulationEquivalenceClasses ( storm : : models : : Dtmc < ValueType > const & dtmc , bool weak ) {
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 ( ) ) ;
partition . print ( ) ;
@ -142,10 +510,11 @@ namespace storm {
std : : cout < < " initial partition: " < < std : : endl ;
partition . print ( ) ;
assert ( partition . check ( ) ) ;
// Initially, all blocks are potential splitter, so we insert them in the splitterQueue.
std : : deque < Block * > splitterQueue ;
std : : for_each ( partition . blocks . begin ( ) , partition . blocks . end ( ) , [ & ] ( Block & a ) { splitterQueue . push_back ( & a ) ; } ) ;
std : : for_each ( partition . getBlocks ( ) . begin ( ) , partition . getBlocks ( ) . end ( ) , [ & ] ( Block & a ) { splitterQueue . push_back ( & a ) ; } ) ;
storm : : storage : : SparseMatrix < ValueType > backwardTransitions = dtmc . getBackwardTransitions ( ) ;
@ -166,318 +535,213 @@ namespace storm {
}
template < typename ValueType >
std : : size_t BisimulationDecomposition2 < ValueType > : : splitBlockProbabilities ( Block * block , Partition & partition , std : : deque < Block * > & splitterQueue ) {
std : : size_t BisimulationDecomposition2 < ValueType > : : splitBlockProbabilities ( Block & block , Partition & partition , std : : deque < Block * > & splitterQueue ) {
std : : cout < < " part before split prob " < < std : : endl ;
partition . print ( ) ;
Block & currentBlock = * block ;
// Sort the states in the block based on their probabilities.
std : : sort ( partition . states . begin ( ) + currentBlock . begin , partition . states . begin ( ) + currentBlock . end , [ & partition ] ( storm : : storage : : sparse : : state_type const & a , storm : : storage : : sparse : : state_type const & b ) { return partition . values [ a ] < partition . values [ b ] ; } ) ;
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 ) ; } ) ;
// FIXME: This can probably be done more efficiently.
std : : sort ( partition . values . begin ( ) + currentBlock . begin , partition . values . begin ( ) + currentBlock . end ) ;
std : : sort ( partition . getBeginOfValues ( block ) , partition . getEndOfValues ( block ) ) ;
// Update the positions vector.
storm : : storage : : sparse : : state_type position = currentBlock . begin ;
for ( auto stateIt = partition . states . begin ( ) + currentBlock . begin , stateIte = partition . states . begin ( ) + currentBlock . end ; stateIt ! = stateIte ; + + stateIt , + + position ) {
partition . positions [ * stateIt ] = position ;
storm : : storage : : sparse : : state_type position = block . getBegin ( ) ;
for ( auto stateIt = partition . getBeginOfStates ( block ) , stateIte = partition . getEndOfStates ( block ) ; stateIt ! = stateIte ; + + stateIt , + + position ) {
partition . setPosition ( * stateIt , position ) ;
}
// Finally, we need to scan the ranges of states that agree on the probability.
storm : : storage : : sparse : : state_type beginIndex = currentBlock . begin ;
storm : : storage : : sparse : : state_type beginIndex = block . getBegin ( ) ;
storm : : storage : : sparse : : state_type currentIndex = beginIndex ;
storm : : storage : : sparse : : state_type endIndex = currentB lock. end ;
storm : : storage : : sparse : : state_type endIndex = b lock. g etE nd( ) ;
// 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.
// Note that this check requires the block to be non-empty.
if ( std : : abs ( partition . values [ beginIndex ] - partition . values [ endIndex - 1 ] ) < 1e-6 ) {
return 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.
ValueType currentValue = partition . values [ beginIndex ] ;
ValueType * valueIterator = & ( partition . values [ beginIndex ] ) + 1 ;
+ + currentIndex ;
while ( std : : abs ( * valueIterator - currentValue ) < 1e-6 ) {
std : : cout < < " prob: " < < * valueIterator < < std : : endl ;
+ + currentIndex ;
+ + valueIterator ;
}
// Now resize the block.
std : : cout < < " resizing block from " < < std : : endl ;
currentBlock . print ( partition ) ;
currentBlock . end = currentIndex ;
currentBlock . numberOfStates = currentBlock . end - currentBlock . begin ;
std : : cout < < " to " < < std : : endl ;
currentBlock . print ( partition ) ;
beginIndex = currentIndex ;
// If it is not already a splitter, we mark it as such.
if ( ! currentBlock . isMarked ) {
currentBlock . isMarked = true ;
}
Block * prevBlock = & currentBlock ;
// Now scan for new blocks.
std : : list < Block * > createdBlocks ;
std : : cout < < currentIndex < < " < " < < endIndex < < std : : endl ;
typename std : : list < Block > : : const_iterator insertPosition = currentBlock . itToSelf ;
+ + insertPosition ;
while ( currentIndex < endIndex ) {
ValueType currentValue = * ( partition . values . begin ( ) + currentIndex ) ;
std : : size_t createdBlocks = 0 ;
while ( std : : abs ( partition . getValueAtPosition ( beginIndex ) - partition . getValueAtPosition ( endIndex ) ) > = 1e-6 ) {
// 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.
ValueType const & currentValue = partition . getValueAtPosition ( beginIndex ) ;
typename std : : vector < ValueType > : : const_iterator valueIterator = partition . getValues ( ) . begin ( ) + beginIndex + 1 ;
+ + currentIndex ;
ValueType * nextValuePtr = & currentValue ;
while ( currentIndex < endIndex & & std : : abs ( currentValue - * nextValuePtr ) < 1e-6 ) {
while ( currentIndex < endIndex & & std : : abs ( * valueIterator - currentValue ) < = 1e-6 ) {
+ + valueIterator ;
+ + currentIndex ;
+ + nextValuePtr ;
}
// Create a new block from the states that agree on the values.
+ + insertPosition ;
typename std : : list < Block > : : iterator newBlockIterator = partition . blocks . emplace ( insertPosition , beginIndex , currentIndex , prevBlock , prevBlock - > next ) ;
newBlockIterator - > itToSelf = newBlockIterator ;
Block * newBlock = & ( * newBlockIterator ) ;
prevBlock - > next = newBlock ;
prevBlock = newBlock ;
if ( prevBlock - > numberOfStates > 1 ) {
createdBlocks . emplace_back ( prevBlock ) ;
// Now we split the block and mark it as a potential splitter.
+ + createdBlocks ;
Block & newBlock = partition . splitBlock ( block , currentIndex ) ;
if ( ! newBlock . isMarkedAsPredecessor ( ) ) {
newBlock . markAsSplitter ( ) ;
splitterQueue . push_back ( & newBlock ) ;
}
beginIndex = currentIndex ;
// Update the block mapping for the moved states.
for ( auto it = partition . states . begin ( ) + newBlock - > begin , ite = partition . states . begin ( ) + newBlock - > end ; it ! = ite ; + + it ) {
partition . stateToBlockMapping [ * it ] = newBlock ;
}
}
for ( auto block : createdBlocks ) {
splitterQueue . push_back ( block ) ;
block - > isMarked = true ;
}
std : : cout < < " created " < < createdBlocks . size ( ) < < " blocks " < < std : : endl ;
assert ( partition . check ( ) ) ;
return createdBlocks . size ( ) ;
return createdBlocks ;
}
template < typename ValueType >
std : : size_t BisimulationDecomposition2 < ValueType > : : splitPartition ( storm : : storage : : SparseMatrix < ValueType > const & backwardTransitions , Block const & splitter , Partition & partition , std : : deque < Block * > & splitterQueue ) {
std : : cout < < " getting block " < < splitter . myId < < " as splitter " < < std : : endl ;
std : : size_t BisimulationDecomposition2 < ValueType > : : splitPartition ( storm : : storage : : SparseMatrix < ValueType > const & backwardTransitions , Block & splitter , Partition & partition , std : : deque < Block * > & splitterQueue ) {
std : : cout < < " treating block " < < splitter . getId ( ) < < " as splitter " < < std : : endl ;
splitter . print ( partition ) ;
std : : list < Block * > predecessorBlocks ;
// Iterate over all states of the splitter and check its predecessors.
for ( auto stateIterator = partition . states . begin ( ) + splitter . begin , stateIte = partition . states . begin ( ) + splitter . end ; stateIterator ! = stateIte ; + + stateIterator ) {
storm : : storage : : sparse : : state_type & state = * stateIterator ;
storm : : storage : : sparse : : state_type currentPosition = splitter . getBegin ( ) ;
for ( auto stateIterator = partition . getBeginOfStates ( splitter ) , stateIte = partition . getEndOfStates ( splitter ) ; stateIterator ! = stateIte ; + + stateIterator , + + currentPosition ) {
std : : cout < < " states ----- " < < std : : endl ;
for ( auto stateIterator = partition . getStates ( ) . begin ( ) + splitter . getOriginalBegin ( ) , stateIte = partition . getStates ( ) . begin ( ) + splitter . getEnd ( ) ; stateIterator ! = stateIte ; + + stateIterator ) {
std : : cout < < * stateIterator < < " " ;
}
std : : cout < < std : : endl ;
storm : : storage : : sparse : : state_type currentState = * stateIterator ;
std : : cout < < " current state " < < currentState < < " at pos " < < currentPosition < < std : : endl ;
for ( auto const & predecessorEntry : backwardTransitions . getRow ( state ) ) {
uint_fast64_t elementsToSkip = 0 ;
for ( auto const & predecessorEntry : backwardTransitions . getRow ( currentState ) ) {
storm : : storage : : sparse : : state_type predecessor = predecessorEntry . getColumn ( ) ;
std : : cout < < " found predecessor " < < predecessor < < " of state " < < * stateIterator < < std : : endl ;
Block * predecessorBlock = partition . stateToBlockMapping [ predecessor ] ;
std : : cout < < " predecessor block " < < predecessorBlock - > myId < < std : : endl ;
std : : cout < < " found predecessor " < < predecessor < < " of state " < < currentState < < std : : endl ;
Block & predecessorBlock = partition . getBlock ( predecessor ) ;
std : : cout < < " predecessor block " < < predecessorBlock . getId ( ) < < std : : endl ;
// If the predecessor block has just one state, there is no point in splitting it.
if ( predecessorBlock - > numberOfStates < = 1 ) {
if ( predecessorBlock . getNumberOfStates ( ) < = 1 ) {
continue ;
}
storm : : storage : : sparse : : state_type predecessorPosition = partition . positions [ predecessor ] ;
storm : : storage : : sparse : : state_type predecessorPosition = partition . getPosition ( predecessor ) ;
// If we have not seen this predecessor before, we move it to a part near the beginning of the block.
std : : cout < < " predecessor position: " < < predecessorPosition < < " and begin " < < predecessorBlock - > begin < < std : : endl ;
if ( predecessorPosition > = predecessorBlock - > begin ) {
// We cannot directly move the states at this point, otherwise we would consider states multiple
// times while others are skipped.
// std::swap(partition.states[predecessorPosition], partition.states[predecessorBlock->begin]);
// std::cout << "swapping positions of " << predecessor << " and " << partition.states[predecessorPosition] << std::endl;
// Instead, we only virtually move the states by setting their positions and move them in place
// later.
storm : : storage : : sparse : : state_type tmp = partition . positions [ predecessor ] ;
partition . positions [ predecessor ] = predecessorBlock - > begin ;
std : : cout < < " moved " < < predecessor < < " to pos " < < predecessorBlock - > begin < < std : : endl ;
partition . positions [ partition . states [ predecessorBlock - > begin ] ] = tmp ;
// storm::storage::sparse::state_type tmp = partition.positions[partition.states[predecessorPosition]];
// partition.positions[partition.states[predecessorPosition]] = partition.positions[predecessor];
// partition.positions[predecessor] = tmp;
// std::swap(partition.positions[predecessor], partition.positions[predecessorBlock->begin]);
+ + predecessorBlock - > begin ;
std : : cout < < " incrementing begin, setting probability at " < < partition . positions [ predecessor ] < < " to " < < predecessorEntry . getValue ( ) < < " ... " < < std : : endl ;
partition . values [ partition . positions [ predecessor ] ] = predecessorEntry . getValue ( ) ;
assert ( partition . values [ partition . positions [ predecessor ] ] < = 1 ) ;
if ( predecessorPosition > = predecessorBlock . getBegin ( ) ) {
if ( & predecessorBlock = = & splitter ) {
// If the predecessor we just found was already processed (in terms of visiting its predecessors),
// we swap it with the state that is currently at the beginning of the block and move the start
// of the block one step further.
if ( predecessorPosition < = currentPosition ) {
partition . swapStates ( predecessor , partition . getState ( predecessorBlock . getBegin ( ) ) ) ;
predecessorBlock . incrementBegin ( ) ;
} else {
std : : cout < < " current position is " < < currentPosition < < std : : endl ;
// Otherwise, we need to move the predecessor, but we need to make sure that we explore its
// predecessors later.
if ( predecessorBlock . getMarkedPosition ( ) = = predecessorBlock . getBegin ( ) ) {
partition . swapStatesAtPositions ( predecessorBlock . getMarkedPosition ( ) , predecessorPosition ) ;
partition . swapStatesAtPositions ( predecessorPosition , currentPosition + elementsToSkip + 1 ) ;
} else {
partition . swapStatesAtPositions ( predecessorBlock . getMarkedPosition ( ) , predecessorPosition ) ;
partition . swapStatesAtPositions ( predecessorPosition , predecessorBlock . getBegin ( ) ) ;
partition . swapStatesAtPositions ( predecessorPosition , currentPosition + elementsToSkip + 1 ) ;
}
+ + elementsToSkip ;
predecessorBlock . incrementMarkedPosition ( ) ;
predecessorBlock . incrementBegin ( ) ;
}
} else {
partition . swapStates ( predecessor , partition . getState ( predecessorBlock . getBegin ( ) ) ) ;
predecessorBlock . incrementBegin ( ) ;
}
partition . setValue ( predecessor , predecessorEntry . getValue ( ) ) ;
} else {
// Otherwise, we just need to update the probability for this predecessor.
partition . values [ predecessorPosition ] + = predecessorEntry . getValue ( ) ;
std : : cout < < " updating probability " < < predecessorPosition < < " to " < < std : : setprecision ( 10 ) < < partition . values [ predecessorPosition ] < < std : : endl ;
assert ( partition . values [ predecessorPosition ] < = 1 + 1e-6 ) ;
partition . increaseValue ( predecessor , predecessorEntry . getValue ( ) ) ;
}
if ( ! predecessorBlock - > isMarked ) {
predecessorBlocks . emplace_back ( predecessorBlock ) ;
predecessorBlock - > isMarked = true ;
if ( ! predecessorBlock . isMarkedAsPredecessor ( ) ) {
predecessorBlocks . emplace_back ( & predecessorBlock ) ;
predecessorBlock . markAsPredecessorBlock ( ) ;
}
}
// If we had to move some elements beyond the current element, we may have to skip them.
if ( elementsToSkip > 0 ) {
std : : cout < < " skipping " < < elementsToSkip < < " elements " < < std : : endl ;
stateIterator + = elementsToSkip ;
currentPosition + = elementsToSkip ;
}
}
// Now that we have computed the new positions of the states we want to move, we need to actually move them.
for ( auto block : predecessorBlocks ) {
std : : cout < < " moving block " < < block - > myId < < std : : endl ;
for ( auto stateIterator = partition . states . begin ( ) + block - > originalBegin , stateIte = partition . states . begin ( ) + block - > end ; stateIterator ! = stateIte ; + + stateIterator ) {
std : : cout < < " swapping " < < * stateIterator < < " to " < < partition . positions [ * stateIterator ] < < std : : endl ;
std : : swap ( partition . states [ partition . positions [ * stateIterator ] ] , * stateIterator ) ;
// Now we can traverse the list of states of the splitter whose predecessors we have not yet explored.
for ( auto stateIterator = partition . getStates ( ) . begin ( ) + splitter . getOriginalBegin ( ) , stateIte = partition . getStates ( ) . begin ( ) + splitter . getMarkedPosition ( ) ; stateIterator ! = stateIte ; + + stateIterator ) {
storm : : storage : : sparse : : state_type currentState = * stateIterator ;
for ( auto const & predecessorEntry : backwardTransitions . getRow ( currentState ) ) {
storm : : storage : : sparse : : state_type predecessor = predecessorEntry . getColumn ( ) ;
Block & predecessorBlock = partition . getBlock ( predecessor ) ;
storm : : storage : : sparse : : state_type predecessorPosition = partition . getPosition ( predecessor ) ;
if ( predecessorPosition > = predecessorBlock . getBegin ( ) ) {
partition . swapStatesAtPositions ( predecessorPosition , predecessorBlock . getBegin ( ) ) ;
predecessorBlock . incrementBegin ( ) ;
partition . setValue ( predecessor , predecessorEntry . getValue ( ) ) ;
} else {
partition . increaseValue ( predecessor , predecessorEntry . getValue ( ) ) ;
}
if ( ! predecessorBlock . isMarkedAsPredecessor ( ) ) {
predecessorBlocks . emplace_back ( & predecessorBlock ) ;
predecessorBlock . markAsPredecessorBlock ( ) ;
}
}
// Set the original begin and begin to the same value.
block - > setBegin ( block - > begin ) ;
}
splitter . setMarkedPosition ( splitter . getBegin ( ) ) ;
std : : list < Block * > blocksToSplit ;
std : : cout < < " having " < < predecessorBlocks . size ( ) < < " pred blocks " < < std : : endl ;
// Now, we can iterate over the predecessor blocks and see whether we have to create a new block for
// predecessors of the splitter.
for ( auto block : predecessorBlocks ) {
block - > isMarked = false ;
for ( auto blockPtr : predecessorBlocks ) {
Block & block = * blockPtr ;
block . unmarkAsPredecessorBlock ( ) ;
block . resetMarkedPosition ( ) ;
// If we have moved the begin of the block to somewhere in the middle of the block, we need to split it.
if ( block - > begin ! = block - > end ) {
std : : cout < < " moved begin to " < < block - > begin < < " and end to " < < block - > end < < std : : endl ;
storm : : storage : : sparse : : state_type tmpBegin = block - > begin ;
storm : : storage : : sparse : : state_type tmpEnd = block - > end ;
block - > setBegin ( block - > prev ! = nullptr ? block - > prev - > end : 0 ) ;
block - > end = tmpBegin ;
block - > numberOfStates = block - > end - block - > begin ;
// Create a new block that holds all states that do not have a successor in the current splitter.
typename std : : list < Block > : : iterator it = partition . blocks . emplace ( block - > next ! = nullptr ? block - > next - > itToSelf : partition . blocks . end ( ) , tmpBegin , tmpEnd , block , block - > next ) ;
Block * newBlock = & ( * it ) ;
newBlock - > itToSelf = it ;
if ( block - > next ! = nullptr ) {
block - > next - > prev = newBlock ;
}
block - > next = newBlock ;
if ( block . getBegin ( ) ! = block . getEnd ( ) ) {
std : : cout < < " moved begin of block " < < block . getId ( ) < < " to " < < block . getBegin ( ) < < " and end to " < < block . getEnd ( ) < < std : : endl ;
Block & newBlock = partition . insertBlock ( block ) ;
std : : cout < < " created new block " < < std : : endl ;
newBlock - > print ( partition ) ;
// Update the block mapping in the partition.
for ( auto it = partition . states . begin ( ) + newBlock - > begin , ite = partition . states . begin ( ) + newBlock - > end ; it ! = ite ; + + it ) {
partition . stateToBlockMapping [ * it ] = newBlock ;
}
// Mark the half of the block that can be further refined using the probability information.
blocksToSplit . emplace_back ( block ) ;
block - > print ( partition ) ;
splitterQueue . push_back ( newBlock ) ;
newBlock . print ( partition ) ;
splitterQueue . push_back ( & newBlock ) ;
} else {
std : : cout < < " found block to split " < < std : : endl ;
std : : cout < < " all states are predecessors " < < std : : endl ;
// In this case, we can keep the block by setting its begin to the old value.
block - > setBegin ( ( block - > prev ! = nullptr ) ? block - > prev - > end : 0 ) ;
blocksToSplit . emplace_back ( block ) ;
block . setBegin ( block . getOriginalBegin ( ) ) ;
blocksToSplit . emplace_back ( & block ) ;
}
block - > setBegin ( block - > begin ) ;
}
assert ( partition . check ( ) ) ;
// Finally, we walk through the blocks that have a transition to the splitter and split them using
// probabilistic information.
for ( auto block : blocksToSplit ) {
if ( block - > numberOfStates < = 1 ) {
for ( auto blockPtr : blocksToSplit ) {
std : : cout < < " block to split: " < < blockPtr - > getId ( ) < < std : : endl ;
if ( blockPtr - > getNumberOfStates ( ) < = 1 ) {
continue ;
}
splitBlockProbabilities ( block , partition , splitterQueue ) ;
splitBlockProbabilities ( * blockPtr , partition , splitterQueue ) ;
}
return 0 ;
}
// template<typename ValueType>
// std::size_t BisimulationDecomposition2<ValueType>::splitPartition(storm::models::Dtmc<ValueType> const& dtmc, storm::storage::SparseMatrix<ValueType> const& backwardTransitions, std::size_t const& blockId, std::vector<std::size_t>& stateToBlockMapping, storm::storage::BitVector& blocksInSplitterQueue, std::deque<std::size_t>& splitterQueue, bool weakBisimulation) {
// std::chrono::high_resolution_clock::time_point totalStart = std::chrono::high_resolution_clock::now();
// std::unordered_map<storm::storage::Distribution<ValueType>, typename BisimulationDecomposition2<ValueType>::block_type> distributionToNewBlocks;
//
// // Traverse all states of the block and check whether they have different distributions.
// std::chrono::high_resolution_clock::time_point gatherStart = std::chrono::high_resolution_clock::now();
// for (auto const& state : this->blocks[blockId]) {
// // Now construct the distribution of this state wrt. to the current partitioning.
// storm::storage::Distribution<ValueType> distribution;
// for (auto const& successorEntry : dtmc.getTransitionMatrix().getRow(state)) {
// distribution.addProbability(static_cast<storm::storage::sparse::state_type>(stateToBlockMapping[successorEntry.getColumn()]), successorEntry.getValue());
// }
//
// // If we are requested to compute a weak bisimulation, we need to scale the distribution with the
// // self-loop probability.
// if (weakBisimulation) {
// distribution.scale(blockId);
// }
//
// // If the distribution already exists, we simply add the state. Otherwise, we open a new block.
// auto distributionIterator = distributionToNewBlocks.find(distribution);
// if (distributionIterator != distributionToNewBlocks.end()) {
// distributionIterator->second.insert(state);
// } else {
// distributionToNewBlocks[distribution].insert(state);
// }
// }
//
// std::chrono::high_resolution_clock::duration gatherTime = std::chrono::high_resolution_clock::now() - gatherStart;
// std::cout << "time to iterate over all states was " << std::chrono::duration_cast<std::chrono::milliseconds>(gatherTime).count() << "ms." << std::endl;
//
// // Now we are ready to split the block.
// if (distributionToNewBlocks.size() == 1) {
// // If there is just one behavior, we just set the distribution as the new one for this block.
// // distributions[blockId] = std::move(distributionToNewBlocks.begin()->first);
// } else {
// // In this case, we need to split the block.
// typename BisimulationDecomposition2<ValueType>::block_type tmpBlock;
//
// auto distributionIterator = distributionToNewBlocks.begin();
// tmpBlock = std::move(distributionIterator->second);
// std::swap(this->blocks[blockId], tmpBlock);
// ++distributionIterator;
//
// // Remember the number of blocks prior to splitting for later use.
// std::size_t beforeNumberOfBlocks = this->blocks.size();
//
// for (; distributionIterator != distributionToNewBlocks.end(); ++distributionIterator) {
// // In this case, we need to move the newly created block to the end of the list of actual blocks.
// this->blocks.emplace_back(std::move(distributionIterator->second));
//
// // Update the mapping of states to their blocks.
// std::size_t newBlockId = this->blocks.size() - 1;
// for (auto const& state : this->blocks.back()) {
// stateToBlockMapping[state] = newBlockId;
// }
// }
//
// // Insert blocks that possibly need a refinement into the queue.
// for (auto const& state : tmpBlock) {
// for (auto const& predecessor : backwardTransitions.getRow(state)) {
// if (!blocksInRefinementQueue.get(stateToBlockMapping[predecessor.getColumn()])) {
// blocksInRefinementQueue.set(stateToBlockMapping[predecessor.getColumn()]);
// refinementQueue.push_back(stateToBlockMapping[predecessor.getColumn()]);
// }
// }
// }
// }
//
// std::chrono::high_resolution_clock::duration totalTime = std::chrono::high_resolution_clock::now() - totalStart;
// std::cout << "refinement of block " << blockId << " took " << std::chrono::duration_cast<std::chrono::milliseconds>(totalTime).count() << "ms." << std::endl;
// return distributionToNewBlocks.size();
// }
template class BisimulationDecomposition2 < double > ;
}
}