@ -36,6 +36,24 @@ namespace storm {
template < storm : : dd : : DdType DdType , typename ValueType >
template < storm : : dd : : DdType DdType , typename ValueType >
class InternalSignatureRefiner ;
class InternalSignatureRefiner ;
struct InternalSignatureRefinerOptions {
InternalSignatureRefinerOptions ( ) : InternalSignatureRefinerOptions ( true ) {
// Intentionally left empty.
}
InternalSignatureRefinerOptions ( bool shiftStateVariables ) : shiftStateVariables ( shiftStateVariables ) {
auto const & bisimulationSettings = storm : : settings : : getModule < storm : : settings : : modules : : BisimulationSettings > ( ) ;
storm : : settings : : modules : : BisimulationSettings : : ReuseMode reuseMode = bisimulationSettings . getReuseMode ( ) ;
this - > reuseBlockNumbers = reuseMode = = storm : : settings : : modules : : BisimulationSettings : : ReuseMode : : All | | reuseMode = = storm : : settings : : modules : : BisimulationSettings : : ReuseMode : : BlockNumbers ;
this - > reuseSignatureResult = reuseMode = = storm : : settings : : modules : : BisimulationSettings : : ReuseMode : : All | | reuseMode = = storm : : settings : : modules : : BisimulationSettings : : ReuseMode : : SignatureResults ;
}
bool shiftStateVariables ;
bool reuseBlockNumbers ;
bool reuseSignatureResult ;
} ;
class ReuseWrapper {
class ReuseWrapper {
public :
public :
ReuseWrapper ( ) : ReuseWrapper ( false ) {
ReuseWrapper ( ) : ReuseWrapper ( false ) {
@ -61,7 +79,7 @@ namespace storm {
template < typename ValueType >
template < typename ValueType >
class InternalSignatureRefiner < storm : : dd : : DdType : : CUDD , ValueType > {
class InternalSignatureRefiner < storm : : dd : : DdType : : CUDD , ValueType > {
public :
public :
InternalSignatureRefiner ( storm : : dd : : DdManager < storm : : dd : : DdType : : CUDD > const & manager , storm : : expressions : : Variable const & blockVariable , storm : : dd : : Bdd < storm : : dd : : DdType : : CUDD > const & nondeterminismVariables , storm : : dd : : Bdd < storm : : dd : : DdType : : CUDD > const & nonBlockVariables , bool shiftStateVariables ) : manager ( manager ) , internalDdManager ( manager . getInternalDdManager ( ) ) , ddman ( internalDdManager . getCuddManager ( ) . getManager ( ) ) , blockVariable ( blockVariable ) , nondeterminismVariables ( nondeterminismVariables ) , nonBlockVariables ( nonBlockVariables ) , shiftStateVariables ( shiftStateVariable s) , nextFreeBlockIndex ( 0 ) , numberOfRefinements ( 0 ) , lastNumberOfVisitedNodes ( 10000 ) , signatureCache ( lastNumberOfVisitedNodes ) , reuseBlocksCache ( lastNumberOfVisitedNodes ) {
InternalSignatureRefiner ( storm : : dd : : DdManager < storm : : dd : : DdType : : CUDD > const & manager , storm : : expressions : : Variable const & blockVariable , storm : : dd : : Bdd < storm : : dd : : DdType : : CUDD > const & nondeterminismVariables , storm : : dd : : Bdd < storm : : dd : : DdType : : CUDD > const & nonBlockVariables , InternalSignatureRefinerOptions const & options = InternalSignatureRefinerOptions ( ) ) : manager ( manager ) , internalDdManager ( manager . getInternalDdManager ( ) ) , ddman ( internalDdManager . getCuddManager ( ) . getManager ( ) ) , blockVariable ( blockVariable ) , nondeterminismVariables ( nondeterminismVariables ) , nonBlockVariables ( nonBlockVariables ) , options ( option s) , nextFreeBlockIndex ( 0 ) , numberOfRefinements ( 0 ) , lastNumberOfVisitedNodes ( 10000 ) , signatureCache ( lastNumberOfVisitedNodes ) , reuseBlocksCache ( lastNumberOfVisitedNodes ) {
// Initialize precomputed data.
// Initialize precomputed data.
auto const & ddMetaVariable = manager . getMetaVariable ( blockVariable ) ;
auto const & ddMetaVariable = manager . getMetaVariable ( blockVariable ) ;
@ -72,27 +90,64 @@ namespace storm {
}
}
Partition < storm : : dd : : DdType : : CUDD , ValueType > refine ( Partition < storm : : dd : : DdType : : CUDD , ValueType > const & oldPartition , Signature < storm : : dd : : DdType : : CUDD , ValueType > const & signature ) {
Partition < storm : : dd : : DdType : : CUDD , ValueType > refine ( Partition < storm : : dd : : DdType : : CUDD , ValueType > const & oldPartition , Signature < storm : : dd : : DdType : : CUDD , ValueType > const & signature ) {
storm : : dd : : Add < storm : : dd : : DdType : : CUDD , ValueType > newPartitionAdd = refine ( oldPartition , signature . getSignatureAdd ( ) ) ;
storm : : dd : : Add < storm : : dd : : DdType : : CUDD , ValueType > newPartitionAdd ;
if ( options . reuseBlockNumbers & & ! options . reuseSignatureResult ) {
newPartitionAdd = refineReuseBlockNumber ( oldPartition , signature . getSignatureAdd ( ) ) ;
} else {
newPartitionAdd = refineReuseSignatureResults ( oldPartition , signature . getSignatureAdd ( ) ) ;
}
+ + numberOfRefinements ;
+ + numberOfRefinements ;
return oldPartition . replacePartition ( newPartitionAdd , nextFreeBlockIndex ) ;
uint64_t numberOfBlocks = nextFreeBlockIndex ;
if ( options . reuseSignatureResult ) {
std : : set < storm : : expressions : : Variable > blockVariableSet = { blockVariable } ;
std : : set < storm : : expressions : : Variable > nonBlockExpressionVariables ;
std : : set_difference ( oldPartition . asAdd ( ) . getContainedMetaVariables ( ) . begin ( ) , oldPartition . asAdd ( ) . getContainedMetaVariables ( ) . end ( ) , blockVariableSet . begin ( ) , blockVariableSet . end ( ) , std : : inserter ( nonBlockExpressionVariables , nonBlockExpressionVariables . begin ( ) ) ) ;
numberOfBlocks = newPartitionAdd . notZero ( ) . existsAbstract ( nonBlockExpressionVariables ) . getNonZeroCount ( ) ;
}
}
private :
storm : : dd : : Add < storm : : dd : : DdType : : CUDD , ValueType > refine ( Partition < storm : : dd : : DdType : : CUDD , ValueType > const & oldPartition , storm : : dd : : Add < storm : : dd : : DdType : : CUDD , ValueType > const & signatureAdd ) {
STORM_LOG_ASSERT ( oldPartition . storedAsAdd ( ) , " Expecting partition to be stored as ADD for CUDD. " ) ;
return oldPartition . replacePartition ( newPartitionAdd , numberOfBlocks , nextFreeBlockIndex ) ;
}
// Clear the caches.
private :
void clearCaches ( ) {
signatureCache . clear ( ) ;
signatureCache . clear ( ) ;
signatureCache2 . clear ( ) ;
reuseBlocksCache . clear ( ) ;
reuseBlocksCache . clear ( ) ;
}
storm : : dd : : Add < storm : : dd : : DdType : : CUDD , ValueType > refineReuseBlockNumber ( Partition < storm : : dd : : DdType : : CUDD , ValueType > const & oldPartition , storm : : dd : : Add < storm : : dd : : DdType : : CUDD , ValueType > const & signatureAdd ) {
STORM_LOG_ASSERT ( oldPartition . storedAsAdd ( ) , " Expecting partition to be stored as ADD for CUDD. " ) ;
nextFreeBlockIndex = oldPartition . getNextFreeBlockIndex ( ) ;
nextFreeBlockIndex = oldPartition . getNextFreeBlockIndex ( ) ;
// Perform the actual recursive refinement step.
// Perform the actual recursive refinement step.
DdNodePtr result = refine ( oldPartition . asAdd ( ) . getInternalAdd ( ) . getCuddDdNode ( ) , signatureAdd . getInternalAdd ( ) . getCuddDdNode ( ) , nondeterminismVariables . getInternalBdd ( ) . getCuddDdNode ( ) , nonBlockVariables . getInternalBdd ( ) . getCuddDdNode ( ) ) ;
DdNodePtr result = refineReuseBlockNumber ( oldPartition . asAdd ( ) . getInternalAdd ( ) . getCuddDdNode ( ) , signatureAdd . getInternalAdd ( ) . getCuddDdNode ( ) , nondeterminismVariables . getInternalBdd ( ) . getCuddDdNode ( ) , nonBlockVariables . getInternalBdd ( ) . getCuddDdNode ( ) ) ;
// Construct resulting ADD from the obtained node and the meta information.
// Construct resulting ADD from the obtained node and the meta information.
storm : : dd : : InternalAdd < storm : : dd : : DdType : : CUDD , ValueType > internalNewPartitionAdd ( & internalDdManager , cudd : : ADD ( internalDdManager . getCuddManager ( ) , result ) ) ;
storm : : dd : : InternalAdd < storm : : dd : : DdType : : CUDD , ValueType > internalNewPartitionAdd ( & internalDdManager , cudd : : ADD ( internalDdManager . getCuddManager ( ) , result ) ) ;
storm : : dd : : Add < storm : : dd : : DdType : : CUDD , ValueType > newPartitionAdd ( oldPartition . asAdd ( ) . getDdManager ( ) , internalNewPartitionAdd , oldPartition . asAdd ( ) . getContainedMetaVariables ( ) ) ;
storm : : dd : : Add < storm : : dd : : DdType : : CUDD , ValueType > newPartitionAdd ( oldPartition . asAdd ( ) . getDdManager ( ) , internalNewPartitionAdd , oldPartition . asAdd ( ) . getContainedMetaVariables ( ) ) ;
clearCaches ( ) ;
return newPartitionAdd ;
}
storm : : dd : : Add < storm : : dd : : DdType : : CUDD , ValueType > refineReuseSignatureResults ( Partition < storm : : dd : : DdType : : CUDD , ValueType > const & oldPartition , storm : : dd : : Add < storm : : dd : : DdType : : CUDD , ValueType > const & signatureAdd ) {
STORM_LOG_ASSERT ( oldPartition . storedAsAdd ( ) , " Expecting partition to be stored as ADD for CUDD. " ) ;
nextFreeBlockIndex = options . reuseSignatureResult ? oldPartition . getNextFreeBlockIndex ( ) : 0 ;
// Perform the actual recursive refinement step.
DdNodePtr result = refineReuseSignatureResults ( signatureAdd . getInternalAdd ( ) . getCuddDdNode ( ) , nondeterminismVariables . getInternalBdd ( ) . getCuddDdNode ( ) , nonBlockVariables . getInternalBdd ( ) . getCuddDdNode ( ) ) ;
// Construct resulting ADD from the obtained node and the meta information.
storm : : dd : : InternalAdd < storm : : dd : : DdType : : CUDD , ValueType > internalNewPartitionAdd ( & internalDdManager , cudd : : ADD ( internalDdManager . getCuddManager ( ) , result ) ) ;
storm : : dd : : Add < storm : : dd : : DdType : : CUDD , ValueType > newPartitionAdd ( oldPartition . asAdd ( ) . getDdManager ( ) , internalNewPartitionAdd , oldPartition . asAdd ( ) . getContainedMetaVariables ( ) ) ;
if ( options . reuseSignatureResult ) {
oldSignatureCache = signatureCache ;
}
clearCaches ( ) ;
return newPartitionAdd ;
return newPartitionAdd ;
}
}
@ -110,7 +165,102 @@ namespace storm {
return result ;
return result ;
}
}
DdNodePtr refine ( DdNode * partitionNode , DdNode * signatureNode , DdNode * nondeterminismVariablesNode , DdNode * nonBlockVariablesNode ) {
DdNodePtr refineReuseSignatureResults ( DdNode * signatureNode , DdNode * nondeterminismVariablesNode , DdNode * nonBlockVariablesNode ) {
// If we arrived at the constant zero node, then this was an illegal state encoding (we require
// all states to be non-deadlock).
if ( signatureNode = = Cudd_ReadZero ( ddman ) ) {
return signatureNode ;
}
// Check the cache whether we have seen the same node before.
auto it = signatureCache . find ( signatureNode ) ;
if ( it ! = signatureCache . end ( ) ) {
// If so, we return the corresponding result.
return it - > second ;
}
// If we are to reuse signature results, check the old cache.
if ( options . reuseSignatureResult ) {
it = oldSignatureCache . find ( signatureNode ) ;
if ( it ! = oldSignatureCache . end ( ) ) {
// If so, we return the corresponding result.
return it - > second ;
}
}
// If there are no more non-block variables, we hit the signature.
if ( Cudd_IsConstant ( nonBlockVariablesNode ) ) {
DdNode * result = encodeBlock ( nextFreeBlockIndex + + ) ;
signatureCache [ signatureNode ] = result ;
return result ;
} else {
// If there are more variables that belong to the non-block part of the encoding, we need to recursively descend.
bool skipped = true ;
DdNode * signatureThen ;
DdNode * signatureElse ;
short offset ;
bool isNondeterminismVariable = false ;
while ( skipped & & ! Cudd_IsConstant ( nonBlockVariablesNode ) ) {
// Remember an offset that indicates whether the top variable is a nondeterminism variable or not.
offset = options . shiftStateVariables ? 1 : 0 ;
if ( ! Cudd_IsConstant ( nondeterminismVariablesNode ) & & Cudd_NodeReadIndex ( nondeterminismVariablesNode ) = = Cudd_NodeReadIndex ( nonBlockVariablesNode ) ) {
offset = 0 ;
isNondeterminismVariable = true ;
}
if ( Cudd_NodeReadIndex ( signatureNode ) = = Cudd_NodeReadIndex ( nonBlockVariablesNode ) ) {
signatureThen = Cudd_T ( signatureNode ) ;
signatureElse = Cudd_E ( signatureNode ) ;
skipped = false ;
} else {
signatureThen = signatureElse = signatureNode ;
}
// If we skipped the next variable, we fast-forward.
if ( skipped ) {
// If the current variable is a nondeterminism variable, we need to advance both variable sets otherwise just the non-block variables.
nonBlockVariablesNode = Cudd_T ( nonBlockVariablesNode ) ;
if ( isNondeterminismVariable ) {
nondeterminismVariablesNode = Cudd_T ( nondeterminismVariablesNode ) ;
}
}
}
// If there are no more non-block variables remaining, make a recursive call to enter the base case.
if ( Cudd_IsConstant ( nonBlockVariablesNode ) ) {
return refineReuseSignatureResults ( signatureNode , nondeterminismVariablesNode , nonBlockVariablesNode ) ;
}
DdNode * thenResult = refineReuseSignatureResults ( signatureThen , isNondeterminismVariable ? Cudd_T ( nondeterminismVariablesNode ) : nondeterminismVariablesNode , Cudd_T ( nonBlockVariablesNode ) ) ;
Cudd_Ref ( thenResult ) ;
DdNode * elseResult = refineReuseSignatureResults ( signatureElse , isNondeterminismVariable ? Cudd_T ( nondeterminismVariablesNode ) : nondeterminismVariablesNode , Cudd_T ( nonBlockVariablesNode ) ) ;
Cudd_Ref ( elseResult ) ;
DdNode * result ;
if ( thenResult = = elseResult ) {
Cudd_Deref ( thenResult ) ;
Cudd_Deref ( elseResult ) ;
result = thenResult ;
} else {
// Get the node to connect the subresults.
bool complemented = Cudd_IsComplement ( thenResult ) ;
result = cuddUniqueInter ( ddman , Cudd_NodeReadIndex ( nonBlockVariablesNode ) + offset , Cudd_Regular ( thenResult ) , complemented ? Cudd_Not ( elseResult ) : elseResult ) ;
if ( complemented ) {
result = Cudd_Not ( result ) ;
}
Cudd_Deref ( thenResult ) ;
Cudd_Deref ( elseResult ) ;
}
// Store the result in the cache.
signatureCache [ signatureNode ] = result ;
return result ;
}
}
DdNodePtr refineReuseBlockNumber ( DdNode * partitionNode , DdNode * signatureNode , DdNode * nondeterminismVariablesNode , DdNode * nonBlockVariablesNode ) {
// If we arrived at the constant zero node, then this was an illegal state encoding (we require
// If we arrived at the constant zero node, then this was an illegal state encoding (we require
// all states to be non-deadlock).
// all states to be non-deadlock).
if ( partitionNode = = Cudd_ReadZero ( ddman ) ) {
if ( partitionNode = = Cudd_ReadZero ( ddman ) ) {
@ -119,8 +269,8 @@ namespace storm {
// Check the cache whether we have seen the same node before.
// Check the cache whether we have seen the same node before.
auto nodePair = std : : make_pair ( signatureNode , partitionNode ) ;
auto nodePair = std : : make_pair ( signatureNode , partitionNode ) ;
auto it = signatureCache . find ( nodePair ) ;
if ( it ! = signatureCache . end ( ) ) {
auto it = signatureCache2 . find ( nodePair ) ;
if ( it ! = signatureCache2 . end ( ) ) {
// If so, we return the corresponding result.
// If so, we return the corresponding result.
return it - > second ;
return it - > second ;
}
}
@ -132,11 +282,11 @@ namespace storm {
auto & reuseEntry = reuseBlocksCache [ partitionNode ] ;
auto & reuseEntry = reuseBlocksCache [ partitionNode ] ;
if ( ! reuseEntry . isReused ( ) ) {
if ( ! reuseEntry . isReused ( ) ) {
reuseEntry . setReused ( ) ;
reuseEntry . setReused ( ) ;
signatureCache [ nodePair ] = partitionNode ;
signatureCache2 [ nodePair ] = partitionNode ;
return partitionNode ;
return partitionNode ;
} else {
} else {
DdNode * result = encodeBlock ( nextFreeBlockIndex + + ) ;
DdNode * result = encodeBlock ( nextFreeBlockIndex + + ) ;
signatureCache [ nodePair ] = result ;
signatureCache2 [ nodePair ] = result ;
return result ;
return result ;
}
}
} else {
} else {
@ -151,7 +301,7 @@ namespace storm {
bool isNondeterminismVariable = false ;
bool isNondeterminismVariable = false ;
while ( skippedBoth & & ! Cudd_IsConstant ( nonBlockVariablesNode ) ) {
while ( skippedBoth & & ! Cudd_IsConstant ( nonBlockVariablesNode ) ) {
// Remember an offset that indicates whether the top variable is a nondeterminism variable or not.
// Remember an offset that indicates whether the top variable is a nondeterminism variable or not.
offset = shiftStateVariables ? 1 : 0 ;
offset = options . shiftStateVariables ? 1 : 0 ;
if ( ! Cudd_IsConstant ( nondeterminismVariablesNode ) & & Cudd_NodeReadIndex ( nondeterminismVariablesNode ) = = Cudd_NodeReadIndex ( nonBlockVariablesNode ) ) {
if ( ! Cudd_IsConstant ( nondeterminismVariablesNode ) & & Cudd_NodeReadIndex ( nondeterminismVariablesNode ) = = Cudd_NodeReadIndex ( nonBlockVariablesNode ) ) {
offset = 0 ;
offset = 0 ;
isNondeterminismVariable = true ;
isNondeterminismVariable = true ;
@ -185,12 +335,12 @@ namespace storm {
// If there are no more non-block variables remaining, make a recursive call to enter the base case.
// If there are no more non-block variables remaining, make a recursive call to enter the base case.
if ( Cudd_IsConstant ( nonBlockVariablesNode ) ) {
if ( Cudd_IsConstant ( nonBlockVariablesNode ) ) {
return refine ( partitionNode , signatureNode , nondeterminismVariablesNode , nonBlockVariablesNode ) ;
return refineReuseBlockNumber ( partitionNode , signatureNode , nondeterminismVariablesNode , nonBlockVariablesNode ) ;
}
}
DdNode * thenResult = refine ( partitionThen , signatureThen , isNondeterminismVariable ? Cudd_T ( nondeterminismVariablesNode ) : nondeterminismVariablesNode , Cudd_T ( nonBlockVariablesNode ) ) ;
DdNode * thenResult = refineReuseBlockNumber ( partitionThen , signatureThen , isNondeterminismVariable ? Cudd_T ( nondeterminismVariablesNode ) : nondeterminismVariablesNode , Cudd_T ( nonBlockVariablesNode ) ) ;
Cudd_Ref ( thenResult ) ;
Cudd_Ref ( thenResult ) ;
DdNode * elseResult = refine ( partitionElse , signatureElse , isNondeterminismVariable ? Cudd_T ( nondeterminismVariablesNode ) : nondeterminismVariablesNode , Cudd_T ( nonBlockVariablesNode ) ) ;
DdNode * elseResult = refineReuseBlockNumber ( partitionElse , signatureElse , isNondeterminismVariable ? Cudd_T ( nondeterminismVariablesNode ) : nondeterminismVariablesNode , Cudd_T ( nonBlockVariablesNode ) ) ;
Cudd_Ref ( elseResult ) ;
Cudd_Ref ( elseResult ) ;
DdNode * result ;
DdNode * result ;
@ -210,7 +360,7 @@ namespace storm {
}
}
// Store the result in the cache.
// Store the result in the cache.
signatureCache [ nodePair ] = result ;
signatureCache2 [ nodePair ] = result ;
return result ;
return result ;
}
}
@ -225,8 +375,8 @@ namespace storm {
storm : : dd : : Bdd < storm : : dd : : DdType : : CUDD > nondeterminismVariables ;
storm : : dd : : Bdd < storm : : dd : : DdType : : CUDD > nondeterminismVariables ;
storm : : dd : : Bdd < storm : : dd : : DdType : : CUDD > nonBlockVariables ;
storm : : dd : : Bdd < storm : : dd : : DdType : : CUDD > nonBlockVariables ;
// A flag indicating whether we are to shift the state variables by one .
bool shiftStateVariable s;
// The provided options .
InternalSignatureRefinerOptions option s;
// The indices of the DD variables associated with the block variable.
// The indices of the DD variables associated with the block variable.
std : : vector < uint64_t > blockDdVariableIndices ;
std : : vector < uint64_t > blockDdVariableIndices ;
@ -244,7 +394,9 @@ namespace storm {
uint64_t lastNumberOfVisitedNodes ;
uint64_t lastNumberOfVisitedNodes ;
// The cache used to identify states with identical signature.
// The cache used to identify states with identical signature.
spp : : sparse_hash_map < std : : pair < DdNode const * , DdNode const * > , DdNode * , CuddPointerPairHash > signatureCache ;
spp : : sparse_hash_map < DdNode const * , DdNode * > oldSignatureCache ;
spp : : sparse_hash_map < DdNode const * , DdNode * > signatureCache ;
spp : : sparse_hash_map < std : : pair < DdNode const * , DdNode const * > , DdNode * , CuddPointerPairHash > signatureCache2 ;
// The cache used to identify which old block numbers have already been reused.
// The cache used to identify which old block numbers have already been reused.
spp : : sparse_hash_map < DdNode const * , ReuseWrapper > reuseBlocksCache ;
spp : : sparse_hash_map < DdNode const * , ReuseWrapper > reuseBlocksCache ;
@ -253,64 +405,79 @@ namespace storm {
template < typename ValueType >
template < typename ValueType >
class InternalSignatureRefiner < storm : : dd : : DdType : : Sylvan , ValueType > {
class InternalSignatureRefiner < storm : : dd : : DdType : : Sylvan , ValueType > {
public :
public :
InternalSignatureRefiner ( storm : : dd : : DdManager < storm : : dd : : DdType : : Sylvan > const & manager , storm : : expressions : : Variable const & blockVariable , storm : : dd : : Bdd < storm : : dd : : DdType : : Sylvan > const & nondeterminismVariables , storm : : dd : : Bdd < storm : : dd : : DdType : : Sylvan > const & nonBlockVariables , bool shiftStateVariable s) : manager ( manager ) , internalDdManager ( manager . getInternalDdManager ( ) ) , blockVariable ( blockVariable ) , nondeterminismVariables ( nondeterminismVariables ) , nonBlockVariables ( nonBlockVariables ) , shiftStateVariables ( shiftStateVariable s) , numberOfBlockVariables ( manager . getMetaVariable ( blockVariable ) . getNumberOfDdVariables ( ) ) , blockCube ( manager . getMetaVariable ( blockVariable ) . getCube ( ) ) , nextFreeBlockIndex ( 0 ) , numberOfRefinements ( 0 ) , signatureCache ( ) {
InternalSignatureRefiner ( storm : : dd : : DdManager < storm : : dd : : DdType : : Sylvan > const & manager , storm : : expressions : : Variable const & blockVariable , storm : : dd : : Bdd < storm : : dd : : DdType : : Sylvan > const & nondeterminismVariables , storm : : dd : : Bdd < storm : : dd : : DdType : : Sylvan > const & nonBlockVariables , InternalSignatureRefinerOptions const & option s) : manager ( manager ) , internalDdManager ( manager . getInternalDdManager ( ) ) , blockVariable ( blockVariable ) , nondeterminismVariables ( nondeterminismVariables ) , nonBlockVariables ( nonBlockVariables ) , options ( option s) , numberOfBlockVariables ( manager . getMetaVariable ( blockVariable ) . getNumberOfDdVariables ( ) ) , blockCube ( manager . getMetaVariable ( blockVariable ) . getCube ( ) ) , nextFreeBlockIndex ( 0 ) , numberOfRefinements ( 0 ) , signatureCache ( ) {
// Perform garbage collection to clean up stuff not needed anymore.
// Perform garbage collection to clean up stuff not needed anymore.
LACE_ME ;
LACE_ME ;
sylvan_gc ( ) ;
sylvan_gc ( ) ;
}
}
Partition < storm : : dd : : DdType : : Sylvan , ValueType > refine ( Partition < storm : : dd : : DdType : : Sylvan , ValueType > const & oldPartition , Signature < storm : : dd : : DdType : : Sylvan , ValueType > const & signature ) {
Partition < storm : : dd : : DdType : : Sylvan , ValueType > refine ( Partition < storm : : dd : : DdType : : Sylvan , ValueType > const & oldPartition , Signature < storm : : dd : : DdType : : Sylvan , ValueType > const & signature ) {
storm : : dd : : Bdd < storm : : dd : : DdType : : Sylvan > newPartitionBdd = refine ( oldPartition , signature . getSignatureAdd ( ) ) ;
return oldPartition . replacePartition ( newPartitionBdd , nextFreeBlockIndex ) ;
storm : : dd : : Bdd < storm : : dd : : DdType : : Sylvan > newPartitionBdd ;
if ( options . reuseBlockNumbers & & ! options . reuseSignatureResult ) {
newPartitionBdd = refineReuseBlockNumber ( oldPartition , signature . getSignatureAdd ( ) ) ;
} else {
newPartitionBdd = refineReuseSignatureResults ( oldPartition , signature . getSignatureAdd ( ) ) ;
}
auto start = std : : chrono : : high_resolution_clock : : now ( ) ;
uint64_t numberOfBlocks = nextFreeBlockIndex ;
if ( options . reuseSignatureResult ) {
std : : set < storm : : expressions : : Variable > blockVariableSet = { blockVariable } ;
std : : set < storm : : expressions : : Variable > nonBlockExpressionVariables ;
std : : set_difference ( oldPartition . asBdd ( ) . getContainedMetaVariables ( ) . begin ( ) , oldPartition . asBdd ( ) . getContainedMetaVariables ( ) . end ( ) , blockVariableSet . begin ( ) , blockVariableSet . end ( ) , std : : inserter ( nonBlockExpressionVariables , nonBlockExpressionVariables . begin ( ) ) ) ;
numberOfBlocks = newPartitionBdd . existsAbstract ( nonBlockExpressionVariables ) . getNonZeroCount ( ) ;
}
auto end = std : : chrono : : high_resolution_clock : : now ( ) ;
std : : cout < < " took " < < std : : chrono : : duration_cast < std : : chrono : : milliseconds > ( end - start ) . count ( ) < < std : : endl ;
return oldPartition . replacePartition ( newPartitionBdd , numberOfBlocks , nextFreeBlockIndex ) ;
}
}
private :
private :
storm : : dd : : Bdd < storm : : dd : : DdType : : Sylvan > refine ( Partition < storm : : dd : : DdType : : Sylvan , ValueType > const & oldPartition , storm : : dd : : Add < storm : : dd : : DdType : : Sylvan , ValueType > const & signatureAdd ) {
STORM_LOG_ASSERT ( oldPartition . storedAsBdd ( ) , " Expecting partition to be stored as BDD for Sylvan. " ) ;
void clearCaches ( ) {
signatureCache . clear ( ) ;
signatureCache2 . clear ( ) ;
reuseBlocksCache . clear ( ) ;
}
LACE_ME ;
storm : : dd : : Bdd < storm : : dd : : DdType : : Sylvan > refineReuseBlockNumber ( Partition < storm : : dd : : DdType : : Sylvan , ValueType > const & oldPartition , storm : : dd : : Add < storm : : dd : : DdType : : Sylvan , ValueType > const & signatureAdd ) {
STORM_LOG_ASSERT ( oldPartition . storedAsBdd ( ) , " Expecting partition to be stored as BDD for Sylvan. " ) ;
// Set up next refinement.
// Set up next refinement.
+ + numberOfRefinements ;
+ + numberOfRefinements ;
// Clear the caches.
std : : size_t oldSize = signatureCache . size ( ) ;
signatureCache . clear ( ) ;
signatureCache . reserve ( 3 * oldSize ) ;
reuseBlocksCache . clear ( ) ;
reuseBlocksCache . reserve ( 3 * oldPartition . getNumberOfBlocks ( ) ) ;
nextFreeBlockIndex = oldPartition . getNextFreeBlockIndex ( ) ;
nextFreeBlockIndex = oldPartition . getNextFreeBlockIndex ( ) ;
// Clear performance counters.
// signatureCacheLookups = 0;
// signatureCacheHits = 0;
// numberOfVisitedNodes = 0;
// totalSignatureCacheLookupTime = std::chrono::high_resolution_clock::duration(0);
// totalSignatureCacheStoreTime = std::chrono::high_resolution_clock::duration(0);
// totalReuseBlocksLookupTime = std::chrono::high_resolution_clock::duration(0);
// totalLevelLookupTime = std::chrono::high_resolution_clock::duration(0);
// totalBlockEncodingTime = std::chrono::high_resolution_clock::duration(0);
// totalMakeNodeTime = std::chrono::high_resolution_clock::duration(0);
// Perform the actual recursive refinement step.
// Perform the actual recursive refinement step.
BDD result = refine ( oldPartition . asBdd ( ) . getInternalBdd ( ) . getSylvanBdd ( ) . GetBDD ( ) , signatureAdd . getInternalAdd ( ) . getSylvanMtbdd ( ) . GetMTBDD ( ) , nondeterminismVariables . getInternalBdd ( ) . getSylvanBdd ( ) . GetBDD ( ) , nonBlockVariables . getInternalBdd ( ) . getSylvanBdd ( ) . GetBDD ( ) ) ;
BDD result = refineReuseBlockNumber ( oldPartition . asBdd ( ) . getInternalBdd ( ) . getSylvanBdd ( ) . GetBDD ( ) , signatureAdd . getInternalAdd ( ) . getSylvanMtbdd ( ) . GetMTBDD ( ) , nondeterminismVariables . getInternalBdd ( ) . getSylvanBdd ( ) . GetBDD ( ) , nonBlockVariables . getInternalBdd ( ) . getSylvanBdd ( ) . GetBDD ( ) ) ;
// Construct resulting BDD from the obtained node and the meta information.
// Construct resulting BDD from the obtained node and the meta information.
storm : : dd : : InternalBdd < storm : : dd : : DdType : : Sylvan > internalNewPartitionBdd ( & internalDdManager , sylvan : : Bdd ( result ) ) ;
storm : : dd : : InternalBdd < storm : : dd : : DdType : : Sylvan > internalNewPartitionBdd ( & internalDdManager , sylvan : : Bdd ( result ) ) ;
storm : : dd : : Bdd < storm : : dd : : DdType : : Sylvan > newPartitionBdd ( oldPartition . asBdd ( ) . getDdManager ( ) , internalNewPartitionBdd , oldPartition . asBdd ( ) . getContainedMetaVariables ( ) ) ;
storm : : dd : : Bdd < storm : : dd : : DdType : : Sylvan > newPartitionBdd ( oldPartition . asBdd ( ) . getDdManager ( ) , internalNewPartitionBdd , oldPartition . asBdd ( ) . getContainedMetaVariables ( ) ) ;
// // Display some statistics.
// STORM_LOG_TRACE("Refinement visited " << numberOfVisitedNodes << " nodes.");
// STORM_LOG_TRACE("Current #nodes in table: " << llmsset_count_marked(nodes) << " of " << llmsset_get_size(nodes) << ", cache: " << cache_getused() << " of " << cache_getsize() << ".");
// STORM_LOG_TRACE("Signature cache hits: " << signatureCacheHits << ", misses: " << (signatureCacheLookups - signatureCacheHits) << ".");
// STORM_LOG_TRACE("Signature cache lookup time: " << std::chrono::duration_cast<std::chrono::milliseconds>(totalSignatureCacheLookupTime).count() << "ms");
// STORM_LOG_TRACE("Signature cache store time: " << std::chrono::duration_cast<std::chrono::milliseconds>(totalSignatureCacheStoreTime).count() << "ms");
// STORM_LOG_TRACE("Signature cache total time: " << std::chrono::duration_cast<std::chrono::milliseconds>(totalSignatureCacheStoreTime + totalSignatureCacheLookupTime).count() << "ms");
// STORM_LOG_TRACE("Reuse blocks lookup time: " << std::chrono::duration_cast<std::chrono::milliseconds>(totalReuseBlocksLookupTime).count() << "ms");
// STORM_LOG_TRACE("Level lookup time: " << std::chrono::duration_cast<std::chrono::milliseconds>(totalLevelLookupTime).count() << "ms");
// STORM_LOG_TRACE("Block encoding time: " << std::chrono::duration_cast<std::chrono::milliseconds>(totalBlockEncodingTime).count() << "ms");
// STORM_LOG_TRACE("Make node time: " << std::chrono::duration_cast<std::chrono::milliseconds>(totalMakeNodeTime).count() << "ms");
clearCaches ( ) ;
return newPartitionBdd ;
}
storm : : dd : : Bdd < storm : : dd : : DdType : : Sylvan > refineReuseSignatureResults ( Partition < storm : : dd : : DdType : : Sylvan , ValueType > const & oldPartition , storm : : dd : : Add < storm : : dd : : DdType : : Sylvan , ValueType > const & signatureAdd ) {
STORM_LOG_ASSERT ( oldPartition . storedAsBdd ( ) , " Expecting partition to be stored as BDD for Sylvan. " ) ;
// Set up next refinement.
+ + numberOfRefinements ;
nextFreeBlockIndex = options . reuseSignatureResult ? oldPartition . getNextFreeBlockIndex ( ) : 0 ;
// Perform the actual recursive refinement step.
BDD result = refineReuseSignatureResults ( signatureAdd . getInternalAdd ( ) . getSylvanMtbdd ( ) . GetMTBDD ( ) , nondeterminismVariables . getInternalBdd ( ) . getSylvanBdd ( ) . GetBDD ( ) , nonBlockVariables . getInternalBdd ( ) . getSylvanBdd ( ) . GetBDD ( ) ) ;
// Construct resulting BDD from the obtained node and the meta information.
storm : : dd : : InternalBdd < storm : : dd : : DdType : : Sylvan > internalNewPartitionBdd ( & internalDdManager , sylvan : : Bdd ( result ) ) ;
storm : : dd : : Bdd < storm : : dd : : DdType : : Sylvan > newPartitionBdd ( oldPartition . asBdd ( ) . getDdManager ( ) , internalNewPartitionBdd , oldPartition . asBdd ( ) . getContainedMetaVariables ( ) ) ;
if ( options . reuseSignatureResult ) {
oldSignatureCache = signatureCache ;
}
clearCaches ( ) ;
return newPartitionBdd ;
return newPartitionBdd ;
}
}
@ -323,7 +490,101 @@ namespace storm {
return sylvan_cube ( blockCube . getInternalBdd ( ) . getSylvanBdd ( ) . GetBDD ( ) , e . data ( ) ) ;
return sylvan_cube ( blockCube . getInternalBdd ( ) . getSylvanBdd ( ) . GetBDD ( ) , e . data ( ) ) ;
}
}
BDD refine ( BDD partitionNode , MTBDD signatureNode , BDD nondeterminismVariablesNode , BDD nonBlockVariablesNode ) {
BDD refineReuseSignatureResults ( MTBDD signatureNode , BDD nondeterminismVariablesNode , BDD nonBlockVariablesNode ) {
LACE_ME ;
// If we arrived at the constant zero node, then this was an illegal state encoding (we require
// all states to be non-deadlock).
if ( mtbdd_iszero ( signatureNode ) ) {
return sylvan_false ;
}
// Check the cache whether we have seen the same node before.
auto it = signatureCache . find ( signatureNode ) ;
if ( it ! = signatureCache . end ( ) ) {
// If so, we return the corresponding result.
return it - > second ;
}
// If we are to reuse signature results, check the old cache.
if ( options . reuseSignatureResult ) {
it = oldSignatureCache . find ( signatureNode ) ;
if ( it ! = oldSignatureCache . end ( ) ) {
// If so, we return the corresponding result.
return it - > second ;
}
}
sylvan_gc_test ( ) ;
// If there are no more non-block variables, we hit the signature.
if ( sylvan_isconst ( nonBlockVariablesNode ) ) {
BDD result = encodeBlock ( nextFreeBlockIndex + + ) ;
signatureCache [ signatureNode ] = result ;
return result ;
} else {
// If there are more variables that belong to the non-block part of the encoding, we need to recursively descend.
bool skipped = true ;
MTBDD signatureThen ;
MTBDD signatureElse ;
short offset ;
bool isNondeterminismVariable = false ;
while ( skipped & & ! sylvan_isconst ( nonBlockVariablesNode ) ) {
// Remember an offset that indicates whether the top variable is a nondeterminism variable or not.
offset = options . shiftStateVariables ? 1 : 0 ;
if ( ! sylvan_isconst ( nondeterminismVariablesNode ) & & sylvan_var ( nondeterminismVariablesNode ) = = sylvan_var ( nonBlockVariablesNode ) ) {
offset = 0 ;
isNondeterminismVariable = true ;
}
if ( storm : : dd : : InternalAdd < storm : : dd : : DdType : : Sylvan , ValueType > : : matchesVariableIndex ( signatureNode , sylvan_var ( nonBlockVariablesNode ) ) ) {
signatureThen = sylvan_high ( signatureNode ) ;
signatureElse = sylvan_low ( signatureNode ) ;
skipped = false ;
} else {
signatureThen = signatureElse = signatureNode ;
}
// If we skipped the next variable, we fast-forward.
if ( skipped ) {
// If the current variable is a nondeterminism variable, we need to advance both variable sets otherwise just the non-block variables.
nonBlockVariablesNode = sylvan_high ( nonBlockVariablesNode ) ;
if ( isNondeterminismVariable ) {
nondeterminismVariablesNode = sylvan_high ( nondeterminismVariablesNode ) ;
}
}
}
// If there are no more non-block variables remaining, make a recursive call to enter the base case.
if ( sylvan_isconst ( nonBlockVariablesNode ) ) {
return refineReuseSignatureResults ( signatureNode , nondeterminismVariablesNode , nonBlockVariablesNode ) ;
}
BDD thenResult = refineReuseSignatureResults ( signatureThen , isNondeterminismVariable ? sylvan_high ( nondeterminismVariablesNode ) : nondeterminismVariablesNode , sylvan_high ( nonBlockVariablesNode ) ) ;
bdd_refs_push ( thenResult ) ;
BDD elseResult = refineReuseSignatureResults ( signatureElse , isNondeterminismVariable ? sylvan_high ( nondeterminismVariablesNode ) : nondeterminismVariablesNode , sylvan_high ( nonBlockVariablesNode ) ) ;
bdd_refs_push ( elseResult ) ;
BDD result ;
if ( thenResult = = elseResult ) {
result = thenResult ;
} else {
// Get the node to connect the subresults.
result = sylvan_makenode ( sylvan_var ( nonBlockVariablesNode ) + offset , elseResult , thenResult ) ;
}
// Dispose of the intermediate results.
bdd_refs_pop ( 2 ) ;
// Store the result in the cache.
signatureCache [ signatureNode ] = result ;
return result ;
}
}
BDD refineReuseBlockNumber ( BDD partitionNode , MTBDD signatureNode , BDD nondeterminismVariablesNode , BDD nonBlockVariablesNode ) {
LACE_ME ;
LACE_ME ;
// If we arrived at the constant zero node, then this was an illegal state encoding (we require
// If we arrived at the constant zero node, then this was an illegal state encoding (we require
@ -336,8 +597,8 @@ namespace storm {
// Check the cache whether we have seen the same node before.
// Check the cache whether we have seen the same node before.
auto nodePair = std : : make_pair ( signatureNode , partitionNode ) ;
auto nodePair = std : : make_pair ( signatureNode , partitionNode ) ;
auto it = signatureCache . find ( nodePair ) ;
if ( it ! = signatureCache . end ( ) ) {
auto it = signatureCache2 . find ( nodePair ) ;
if ( it ! = signatureCache2 . end ( ) ) {
// If so, we return the corresponding result.
// If so, we return the corresponding result.
return it - > second ;
return it - > second ;
}
}
@ -353,11 +614,11 @@ namespace storm {
if ( ! reuseBlockEntry . isReused ( ) ) {
if ( ! reuseBlockEntry . isReused ( ) ) {
reuseBlockEntry . setReused ( ) ;
reuseBlockEntry . setReused ( ) ;
reuseBlocksCache . emplace ( partitionNode , true ) ;
reuseBlocksCache . emplace ( partitionNode , true ) ;
signatureCache [ nodePair ] = partitionNode ;
signatureCache2 [ nodePair ] = partitionNode ;
return partitionNode ;
return partitionNode ;
} else {
} else {
BDD result = encodeBlock ( nextFreeBlockIndex + + ) ;
BDD result = encodeBlock ( nextFreeBlockIndex + + ) ;
signatureCache [ nodePair ] = result ;
signatureCache2 [ nodePair ] = result ;
return result ;
return result ;
}
}
} else {
} else {
@ -372,7 +633,7 @@ namespace storm {
bool isNondeterminismVariable = false ;
bool isNondeterminismVariable = false ;
while ( skippedBoth & & ! sylvan_isconst ( nonBlockVariablesNode ) ) {
while ( skippedBoth & & ! sylvan_isconst ( nonBlockVariablesNode ) ) {
// Remember an offset that indicates whether the top variable is a nondeterminism variable or not.
// Remember an offset that indicates whether the top variable is a nondeterminism variable or not.
offset = shiftStateVariables ? 1 : 0 ;
offset = options . shiftStateVariables ? 1 : 0 ;
if ( ! sylvan_isconst ( nondeterminismVariablesNode ) & & sylvan_var ( nondeterminismVariablesNode ) = = sylvan_var ( nonBlockVariablesNode ) ) {
if ( ! sylvan_isconst ( nondeterminismVariablesNode ) & & sylvan_var ( nondeterminismVariablesNode ) = = sylvan_var ( nonBlockVariablesNode ) ) {
offset = 0 ;
offset = 0 ;
isNondeterminismVariable = true ;
isNondeterminismVariable = true ;
@ -406,12 +667,12 @@ namespace storm {
// If there are no more non-block variables remaining, make a recursive call to enter the base case.
// If there are no more non-block variables remaining, make a recursive call to enter the base case.
if ( sylvan_isconst ( nonBlockVariablesNode ) ) {
if ( sylvan_isconst ( nonBlockVariablesNode ) ) {
return refine ( partitionNode , signatureNode , nondeterminismVariablesNode , nonBlockVariablesNode ) ;
return refineReuseBlockNumber ( partitionNode , signatureNode , nondeterminismVariablesNode , nonBlockVariablesNode ) ;
}
}
BDD thenResult = refine ( partitionThen , signatureThen , isNondeterminismVariable ? sylvan_high ( nondeterminismVariablesNode ) : nondeterminismVariablesNode , sylvan_high ( nonBlockVariablesNode ) ) ;
BDD thenResult = refineReuseBlockNumber ( partitionThen , signatureThen , isNondeterminismVariable ? sylvan_high ( nondeterminismVariablesNode ) : nondeterminismVariablesNode , sylvan_high ( nonBlockVariablesNode ) ) ;
bdd_refs_push ( thenResult ) ;
bdd_refs_push ( thenResult ) ;
BDD elseResult = refine ( partitionElse , signatureElse , isNondeterminismVariable ? sylvan_high ( nondeterminismVariablesNode ) : nondeterminismVariablesNode , sylvan_high ( nonBlockVariablesNode ) ) ;
BDD elseResult = refineReuseBlockNumber ( partitionElse , signatureElse , isNondeterminismVariable ? sylvan_high ( nondeterminismVariablesNode ) : nondeterminismVariablesNode , sylvan_high ( nonBlockVariablesNode ) ) ;
bdd_refs_push ( elseResult ) ;
bdd_refs_push ( elseResult ) ;
BDD result ;
BDD result ;
@ -426,7 +687,7 @@ namespace storm {
bdd_refs_pop ( 2 ) ;
bdd_refs_pop ( 2 ) ;
// Store the result in the cache.
// Store the result in the cache.
signatureCache [ nodePair ] = result ;
signatureCache2 [ nodePair ] = result ;
return result ;
return result ;
}
}
@ -439,7 +700,8 @@ namespace storm {
storm : : dd : : Bdd < storm : : dd : : DdType : : Sylvan > nondeterminismVariables ;
storm : : dd : : Bdd < storm : : dd : : DdType : : Sylvan > nondeterminismVariables ;
storm : : dd : : Bdd < storm : : dd : : DdType : : Sylvan > nonBlockVariables ;
storm : : dd : : Bdd < storm : : dd : : DdType : : Sylvan > nonBlockVariables ;
bool shiftStateVariables ;
// The provided options.
InternalSignatureRefinerOptions options ;
uint64_t numberOfBlockVariables ;
uint64_t numberOfBlockVariables ;
@ -452,7 +714,9 @@ namespace storm {
uint64_t numberOfRefinements ;
uint64_t numberOfRefinements ;
// The cache used to identify states with identical signature.
// The cache used to identify states with identical signature.
spp : : sparse_hash_map < std : : pair < MTBDD , MTBDD > , MTBDD , SylvanMTBDDPairHash > signatureCache ;
spp : : sparse_hash_map < MTBDD , MTBDD > oldSignatureCache ;
spp : : sparse_hash_map < MTBDD , MTBDD > signatureCache ;
spp : : sparse_hash_map < std : : pair < MTBDD , MTBDD > , MTBDD , SylvanMTBDDPairHash > signatureCache2 ;
// The cache used to identify which old block numbers have already been reused.
// The cache used to identify which old block numbers have already been reused.
spp : : sparse_hash_map < MTBDD , ReuseWrapper > reuseBlocksCache ;
spp : : sparse_hash_map < MTBDD , ReuseWrapper > reuseBlocksCache ;
@ -467,7 +731,6 @@ namespace storm {
// std::chrono::high_resolution_clock::duration totalLevelLookupTime;
// std::chrono::high_resolution_clock::duration totalLevelLookupTime;
// std::chrono::high_resolution_clock::duration totalBlockEncodingTime;
// std::chrono::high_resolution_clock::duration totalBlockEncodingTime;
// std::chrono::high_resolution_clock::duration totalMakeNodeTime;
// std::chrono::high_resolution_clock::duration totalMakeNodeTime;
} ;
} ;
template < storm : : dd : : DdType DdType , typename ValueType >
template < storm : : dd : : DdType DdType , typename ValueType >
@ -485,7 +748,7 @@ namespace storm {
nonBlockVariablesCube & = cube ;
nonBlockVariablesCube & = cube ;
}
}
internalRefiner = std : : make_unique < InternalSignatureRefiner < DdType , ValueType > > ( manager , blockVariable , nondeterminismVariablesCube , nonBlockVariablesCube , shiftStateVariables ) ;
internalRefiner = std : : make_unique < InternalSignatureRefiner < DdType , ValueType > > ( manager , blockVariable , nondeterminismVariablesCube , nonBlockVariablesCube , InternalSignatureRefinerOptions ( shiftStateVariables ) ) ;
}
}
template < storm : : dd : : DdType DdType , typename ValueType >
template < storm : : dd : : DdType DdType , typename ValueType >