@ -1,5 +1,7 @@
# include "src/builder/DdPrismModelBuilder.h"
# include "src/builder/DdPrismModelBuilder.h"
# include <boost/algorithm/string/join.hpp>
# include "src/models/symbolic/Dtmc.h"
# include "src/models/symbolic/Dtmc.h"
# include "src/models/symbolic/Ctmc.h"
# include "src/models/symbolic/Ctmc.h"
# include "src/models/symbolic/Mdp.h"
# include "src/models/symbolic/Mdp.h"
@ -9,12 +11,13 @@
# include "src/settings/SettingsManager.h"
# include "src/settings/SettingsManager.h"
# include "src/exceptions/InvalidStateException.h"
# include "src/exceptions/InvalidStateException.h"
# include "src/exceptions/NotSupportedException.h"
# include "src/exceptions/InvalidArgumentException.h"
# include "src/exceptions/InvalidArgumentException.h"
# include "src/utility/prism.h"
# include "src/utility/prism.h"
# include "src/utility/math.h"
# include "src/utility/math.h"
# include "src/storage/prism/Program.h"
# include "src/storage/prism/Program.h"
# include "src/storage/prism/Compositions.h"
# include "src/storage/dd/Add.h"
# include "src/storage/dd/Add.h"
# include "src/storage/dd/cudd/CuddAddIterator.h"
# include "src/storage/dd/cudd/CuddAddIterator.h"
@ -189,6 +192,150 @@ namespace storm {
}
}
} ;
} ;
template < storm : : dd : : DdType Type , typename ValueType >
class ModuleComposer : public storm : : prism : : CompositionVisitor {
public :
ModuleComposer ( typename DdPrismModelBuilder < Type , ValueType > : : GenerationInformation & generationInfo ) : generationInfo ( generationInfo ) {
for ( auto const & actionIndex : generationInfo . program . getSynchronizingActionIndices ( ) ) {
synchronizingActionToOffsetMap [ actionIndex ] = 0 ;
}
}
typename DdPrismModelBuilder < Type , ValueType > : : ModuleDecisionDiagram compose ( storm : : prism : : Composition const & composition ) {
std : : cout < < " composing the system " < < composition < < std : : endl ;
return boost : : any_cast < typename DdPrismModelBuilder < Type , ValueType > : : ModuleDecisionDiagram > ( composition . accept ( * this ) ) ;
}
virtual boost : : any visit ( storm : : prism : : ModuleComposition const & composition ) override {
STORM_LOG_TRACE ( " Translating module ' " < < composition . getModuleName ( ) < < " '. " ) ;
typename DdPrismModelBuilder < Type , ValueType > : : ModuleDecisionDiagram result = DdPrismModelBuilder < Type , ValueType > : : createModuleDecisionDiagram ( generationInfo , generationInfo . program . getModule ( composition . getModuleName ( ) ) , synchronizingActionToOffsetMap ) ;
// Update the offset indices.
for ( auto const & actionIndex : generationInfo . program . getSynchronizingActionIndices ( ) ) {
if ( result . hasSynchronizingAction ( actionIndex ) ) {
synchronizingActionToOffsetMap [ actionIndex ] = result . synchronizingActionToDecisionDiagramMap [ actionIndex ] . numberOfUsedNondeterminismVariables ;
}
}
return result ;
}
virtual boost : : any visit ( storm : : prism : : RenamingComposition const & composition ) override {
STORM_LOG_THROW ( false , storm : : exceptions : : NotSupportedException , " Renaming is currently not supported in symbolic model building. " ) ;
}
virtual boost : : any visit ( storm : : prism : : HidingComposition const & composition ) override {
STORM_LOG_THROW ( false , storm : : exceptions : : NotSupportedException , " Hiding is currently not supported in symbolic model building. " ) ;
}
virtual boost : : any visit ( storm : : prism : : SynchronizingParallelComposition const & composition ) override {
// First, we translate the subcompositions.
typename DdPrismModelBuilder < Type , ValueType > : : ModuleDecisionDiagram left = boost : : any_cast < typename DdPrismModelBuilder < Type , ValueType > : : ModuleDecisionDiagram > ( composition . getLeftSubcomposition ( ) . accept ( * this ) ) ;
typename DdPrismModelBuilder < Type , ValueType > : : ModuleDecisionDiagram right = boost : : any_cast < typename DdPrismModelBuilder < Type , ValueType > : : ModuleDecisionDiagram > ( composition . getRightSubcomposition ( ) . accept ( * this ) ) ;
// Then, determine the action indices on which we need to synchronize.
std : : set < uint_fast64_t > leftSynchronizationActionIndices = left . getSynchronizingActionIndices ( ) ;
for ( auto const & entry : leftSynchronizationActionIndices ) {
std : : cout < < " entry 1: " < < entry < < std : : endl ;
}
std : : set < uint_fast64_t > rightSynchronizationActionIndices = right . getSynchronizingActionIndices ( ) ;
for ( auto const & entry : rightSynchronizationActionIndices ) {
std : : cout < < " entry 2: " < < entry < < std : : endl ;
}
std : : set < uint_fast64_t > synchronizationActionIndices ;
std : : set_intersection ( leftSynchronizationActionIndices . begin ( ) , leftSynchronizationActionIndices . end ( ) , rightSynchronizationActionIndices . begin ( ) , rightSynchronizationActionIndices . end ( ) , std : : inserter ( synchronizationActionIndices , synchronizationActionIndices . begin ( ) ) ) ;
// Finally, we compose the subcompositions to create the result. For this, we modify the left
// subcomposition in place and later return it.
composeInParallel ( left , right , synchronizationActionIndices ) ;
return left ;
}
virtual boost : : any visit ( storm : : prism : : InterleavingParallelComposition const & composition ) override {
STORM_LOG_THROW ( false , storm : : exceptions : : NotSupportedException , " Interleaving is currently not supported in symbolic model building. " ) ;
}
virtual boost : : any visit ( storm : : prism : : RestrictedParallelComposition const & composition ) override {
STORM_LOG_THROW ( false , storm : : exceptions : : NotSupportedException , " Restricted parallel composition is currently not supported in symbolic model building. " ) ;
}
private :
/*!
* Composes the given modules while synchronizing over the provided action indices . As a result , the first
* module is modified in place and will contain the composition after a call to this method .
*/
void composeInParallel ( typename DdPrismModelBuilder < Type , ValueType > : : ModuleDecisionDiagram & left , typename DdPrismModelBuilder < Type , ValueType > : : ModuleDecisionDiagram & right , std : : set < uint_fast64_t > const & synchronizationActionIndices ) {
std : : vector < std : : string > actionNames ;
for ( auto const & actionIndex : synchronizationActionIndices ) {
actionNames . push_back ( generationInfo . program . getActionName ( actionIndex ) ) ;
}
STORM_LOG_TRACE ( " Composing two modules over the actions ' " < < boost : : join ( actionNames , " , " ) < < " '. " ) ;
// Combine the tau action.
uint_fast64_t numberOfUsedNondeterminismVariables = right . independentAction . numberOfUsedNondeterminismVariables ;
left . independentAction = DdPrismModelBuilder < Type , ValueType > : : combineUnsynchronizedActions ( generationInfo , left . independentAction , right . independentAction , left . identity , right . identity ) ;
numberOfUsedNondeterminismVariables = std : : max ( numberOfUsedNondeterminismVariables , left . independentAction . numberOfUsedNondeterminismVariables ) ;
// Create an empty action for the case where one of the modules does not have a certain action.
typename DdPrismModelBuilder < Type , ValueType > : : ActionDecisionDiagram emptyAction ( * generationInfo . manager ) ;
// Treat all non-tau actions of the left module.
for ( auto & action : left . synchronizingActionToDecisionDiagramMap ) {
// If we need to synchronize over this action index, we try to do so now.
if ( synchronizationActionIndices . find ( action . first ) ! = synchronizationActionIndices . end ( ) ) {
// If we are to synchronize over an action that does not exist in the second module, the result
// is that the synchronization is the empty action.
if ( right . hasSynchronizingAction ( action . first ) ) {
action . second = emptyAction ;
} else {
// Otherwise, the actions of the modules are synchronized.
action . second = DdPrismModelBuilder < Type , ValueType > : : combineSynchronizingActions ( generationInfo , action . second , right . synchronizingActionToDecisionDiagramMap [ action . first ] ) ;
}
} else {
// If we don't synchronize over this action, we need to construct the interleaving.
// If both modules contain the action, we need to mutually multiply the other identity.
if ( right . hasSynchronizingAction ( action . first ) ) {
action . second = DdPrismModelBuilder < Type , ValueType > : : combineUnsynchronizedActions ( generationInfo , action . second , right . synchronizingActionToDecisionDiagramMap [ action . first ] , left . identity , right . identity ) ;
} else {
// If only the first module has this action, we need to use a dummy action decision diagram
// for the second module.
action . second = DdPrismModelBuilder < Type , ValueType > : : combineUnsynchronizedActions ( generationInfo , action . second , emptyAction , left . identity , right . identity ) ;
}
}
numberOfUsedNondeterminismVariables = std : : max ( numberOfUsedNondeterminismVariables , action . second . numberOfUsedNondeterminismVariables ) ;
}
// Treat all non-tau actions of the right module.
for ( auto const & actionIndex : right . getSynchronizingActionIndices ( ) ) {
// Here, we only need to treat actions that the first module does not have, because we have handled
// this case earlier.
if ( ! left . hasSynchronizingAction ( actionIndex ) ) {
if ( synchronizationActionIndices . find ( actionIndex ) ! = synchronizationActionIndices . end ( ) ) {
// If we are to synchronize over this action that does not exist in the first module, the
// result is that the synchronization is the empty action.
left . synchronizingActionToDecisionDiagramMap [ actionIndex ] = emptyAction ;
} else {
// If only the second module has this action, we need to use a dummy action decision diagram
// for the first module.
left . synchronizingActionToDecisionDiagramMap [ actionIndex ] = DdPrismModelBuilder < Type , ValueType > : : combineUnsynchronizedActions ( generationInfo , emptyAction , right . synchronizingActionToDecisionDiagramMap [ actionIndex ] , left . identity , right . identity ) ;
}
}
numberOfUsedNondeterminismVariables = std : : max ( numberOfUsedNondeterminismVariables , left . synchronizingActionToDecisionDiagramMap [ actionIndex ] . numberOfUsedNondeterminismVariables ) ;
}
// Combine identity matrices.
left . identity = left . identity * right . identity ;
// Keep track of the number of nondeterminism variables used.
left . numberOfUsedNondeterminismVariables = std : : max ( left . numberOfUsedNondeterminismVariables , numberOfUsedNondeterminismVariables ) ;
}
typename DdPrismModelBuilder < Type , ValueType > : : GenerationInformation & generationInfo ;
std : : map < uint_fast64_t , uint_fast64_t > synchronizingActionToOffsetMap ;
} ;
template < storm : : dd : : DdType Type , typename ValueType >
template < storm : : dd : : DdType Type , typename ValueType >
DdPrismModelBuilder < Type , ValueType > : : Options : : Options ( ) : buildAllRewardModels ( true ) , rewardModelsToBuild ( ) , constantDefinitions ( ) , buildAllLabels ( true ) , labelsToBuild ( ) , terminalStates ( ) , negatedTerminalStates ( ) {
DdPrismModelBuilder < Type , ValueType > : : Options : : Options ( ) : buildAllRewardModels ( true ) , rewardModelsToBuild ( ) , constantDefinitions ( ) , buildAllLabels ( true ) , labelsToBuild ( ) , terminalStates ( ) , negatedTerminalStates ( ) {
// Intentionally left empty.
// Intentionally left empty.
@ -789,61 +936,8 @@ namespace storm {
template < storm : : dd : : DdType Type , typename ValueType >
template < storm : : dd : : DdType Type , typename ValueType >
typename DdPrismModelBuilder < Type , ValueType > : : SystemResult DdPrismModelBuilder < Type , ValueType > : : createSystemDecisionDiagram ( GenerationInformation & generationInfo ) {
typename DdPrismModelBuilder < Type , ValueType > : : SystemResult DdPrismModelBuilder < Type , ValueType > : : createSystemDecisionDiagram ( GenerationInformation & generationInfo ) {
// Create the initial offset mapping.
std : : map < uint_fast64_t , uint_fast64_t > synchronizingActionToOffsetMap ;
for ( auto const & actionIndex : generationInfo . program . getSynchronizingActionIndices ( ) ) {
synchronizingActionToOffsetMap [ actionIndex ] = 0 ;
}
// Start by creating DDs for the first module.
STORM_LOG_TRACE ( " Translating (first) module ' " < < generationInfo . program . getModule ( 0 ) . getName ( ) < < " '. " ) ;
ModuleDecisionDiagram system = createModuleDecisionDiagram ( generationInfo , generationInfo . program . getModule ( 0 ) , synchronizingActionToOffsetMap ) ;
// Now translate module by module and combine it with the system created thus far.
for ( uint_fast64_t i = 1 ; i < generationInfo . program . getNumberOfModules ( ) ; + + i ) {
storm : : prism : : Module const & currentModule = generationInfo . program . getModule ( i ) ;
STORM_LOG_TRACE ( " Translating module ' " < < currentModule . getName ( ) < < " '. " ) ;
// Update the offset index.
for ( auto const & actionIndex : generationInfo . program . getSynchronizingActionIndices ( ) ) {
if ( system . hasSynchronizingAction ( actionIndex ) ) {
synchronizingActionToOffsetMap [ actionIndex ] = system . synchronizingActionToDecisionDiagramMap [ actionIndex ] . numberOfUsedNondeterminismVariables ;
}
}
ModuleDecisionDiagram currentModuleDd = createModuleDecisionDiagram ( generationInfo , currentModule , synchronizingActionToOffsetMap ) ;
// Combine the non-synchronizing action.
uint_fast64_t numberOfUsedNondeterminismVariables = currentModuleDd . numberOfUsedNondeterminismVariables ;
system . independentAction = combineUnsynchronizedActions ( generationInfo , system . independentAction , currentModuleDd . independentAction , system . identity , currentModuleDd . identity ) ;
numberOfUsedNondeterminismVariables = std : : max ( numberOfUsedNondeterminismVariables , system . independentAction . numberOfUsedNondeterminismVariables ) ;
ActionDecisionDiagram emptyAction ( * generationInfo . manager ) ;
// For all synchronizing actions that the next module does not have, we need to multiply the identity of the next module.
for ( auto & action : system . synchronizingActionToDecisionDiagramMap ) {
if ( ! currentModuleDd . hasSynchronizingAction ( action . first ) ) {
action . second = combineUnsynchronizedActions ( generationInfo , action . second , emptyAction , system . identity , currentModuleDd . identity ) ;
}
}
// Combine synchronizing actions.
for ( auto const & actionIndex : currentModule . getSynchronizingActionIndices ( ) ) {
if ( system . hasSynchronizingAction ( actionIndex ) ) {
system . synchronizingActionToDecisionDiagramMap [ actionIndex ] = combineSynchronizingActions ( generationInfo , system . synchronizingActionToDecisionDiagramMap [ actionIndex ] , currentModuleDd . synchronizingActionToDecisionDiagramMap [ actionIndex ] ) ;
numberOfUsedNondeterminismVariables = std : : max ( numberOfUsedNondeterminismVariables , system . synchronizingActionToDecisionDiagramMap [ actionIndex ] . numberOfUsedNondeterminismVariables ) ;
} else {
system . synchronizingActionToDecisionDiagramMap [ actionIndex ] = combineUnsynchronizedActions ( generationInfo , emptyAction , currentModuleDd . synchronizingActionToDecisionDiagramMap [ actionIndex ] , system . identity , currentModuleDd . identity ) ;
numberOfUsedNondeterminismVariables = std : : max ( numberOfUsedNondeterminismVariables , system . synchronizingActionToDecisionDiagramMap [ actionIndex ] . numberOfUsedNondeterminismVariables ) ;
}
}
// Combine identity matrices.
system . identity = system . identity * currentModuleDd . identity ;
// Keep track of the number of nondeterminism variables used.
system . numberOfUsedNondeterminismVariables = std : : max ( system . numberOfUsedNondeterminismVariables , numberOfUsedNondeterminismVariables ) ;
}
ModuleComposer < Type , ValueType > composer ( generationInfo ) ;
ModuleDecisionDiagram system = composer . compose ( generationInfo . program . specifiesSystemComposition ( ) ? generationInfo . program . getSystemCompositionConstruct ( ) . getSystemComposition ( ) : * generationInfo . program . getDefaultSystemComposition ( ) ) ;
storm : : dd : : Add < Type , ValueType > result = createSystemFromModule ( generationInfo , system ) ;
storm : : dd : : Add < Type , ValueType > result = createSystemFromModule ( generationInfo , system ) ;