@ -6,7 +6,8 @@
# include "storm/modelchecker/propositional/SparsePropositionalModelChecker.h"
# include "storm/modelchecker/results/ExplicitQualitativeCheckResult.h"
# include "storm/storage/MaximalEndComponentDecomposition.h"
# include "storm/transformer/StateDuplicator.h"
# include "storm/storage/memorystructure/MemoryStructureBuilder.h"
# include "storm/storage/memorystructure/SparseModelMemoryProduct.h"
# include "storm/transformer/SubsystemBuilder.h"
# include "storm/utility/macros.h"
# include "storm/utility/vector.h"
@ -89,13 +90,36 @@ namespace storm {
template < typename SparseModelType >
void SparsePcaaPreprocessor < SparseModelType > : : updatePreprocessedModel ( ReturnType & result , SparseModelType & newPreprocessedModel , std : : vector < uint_fast64_t > & newToOldStateIndexMapping ) {
result . preprocessedModel = std : : move ( newPreprocessedModel ) ;
// the given newToOldStateIndexMapping reff ers to the indices of the former preprocessedModel as 'old indices'
// the given newToOldStateIndexMapping refers to the indices of the former preprocessedModel as 'old indices'
for ( auto & preprocessedModelStateIndex : newToOldStateIndexMapping ) {
preprocessedModelStateIndex = result . newToOldStateIndexMapping [ preprocessedModelStateIndex ] ;
}
result . newToOldStateIndexMapping = std : : move ( newToOldStateIndexMapping ) ;
}
template < typename SparseModelType >
void SparsePcaaPreprocessor < SparseModelType > : : addMemoryToPreprocessedModel ( ReturnType & result , storm : : storage : : MemoryStructure & memory ) {
storm : : storage : : SparseModelMemoryProduct < ValueType > product = memory . product ( result . preprocessedModel ) ;
auto newModel = product . build ( ) ;
// update the newToOldStateIndexMapping
std : : vector < uint_fast64_t > updatedMapping ;
updatedMapping . reserve ( newModel - > getNumberOfStates ( ) ) ;
for ( uint_fast64_t oldState = 0 ; oldState < result . preprocessedModel . getNumberOfStates ( ) ; + + oldState ) {
for ( uint_fast64_t memoryState = 0 ; memoryState < memory . getNumberOfStates ( ) ; + + memoryState ) {
uint_fast64_t const & newState = product . getResultState ( oldState , memoryState ) ;
// Check if the state actually exists in the new model
if ( newState < newModel - > getNumberOfStates ( ) ) {
assert ( newState = = updatedMapping . size ( ) ) ;
updatedMapping . push_back ( result . newToOldStateIndexMapping [ oldState ] ) ;
}
}
}
result . preprocessedModel = std : : move ( dynamic_cast < SparseModelType & > ( * newModel ) ) ;
result . newToOldStateIndexMapping = std : : move ( updatedMapping ) ;
}
template < typename SparseModelType >
void SparsePcaaPreprocessor < SparseModelType > : : preprocessOperatorFormula ( storm : : logic : : OperatorFormula const & formula , ReturnType & result , PcaaObjective < ValueType > & currentObjective ) {
@ -190,34 +214,36 @@ namespace storm {
template < typename SparseModelType >
void SparsePcaaPreprocessor < SparseModelType > : : preprocessUntilFormula ( storm : : logic : : UntilFormula const & formula , ReturnType & result , PcaaObjective < ValueType > & currentObjective ) {
CheckTask < storm : : logic : : Formula , ValueType > phiTask ( formula . getLeftSubformula ( ) ) ;
CheckTask < storm : : logic : : Formula , ValueType > psiTask ( formula . getRightSubformula ( ) ) ;
storm : : modelchecker : : SparsePropositionalModelChecker < SparseModelType > mc ( result . preprocessedModel ) ;
STORM_LOG_THROW ( mc . canHandle ( phiTask ) & & mc . canHandle ( psiTask ) , storm : : exceptions : : InvalidPropertyException , " The subformulas of " < < formula < < " should be propositional. " ) ;
storm : : storage : : BitVector phiStates = mc . check ( phiTask ) - > asExplicitQualitativeCheckResult ( ) . getTruthValuesVector ( ) ;
storm : : storage : : BitVector psiStates = mc . check ( psiTask ) - > asExplicitQualitativeCheckResult ( ) . getTruthValuesVector ( ) ;
if ( ! ( psiStates & result . preprocessedModel . getInitialStates ( ) ) . empty ( ) & & ! currentObjective . lowerTimeBound ) {
// Create a memory structure that stores whether a PhiState or a PsiState has already been reached
storm : : storage : : MemoryStructureBuilder builder ( 2 ) ;
// Get a unique label that is not already present in the model
std : : string memoryLabel = " obj " + std : : to_string ( result . objectives . size ( ) ) ;
while ( result . preprocessedModel . hasLabel ( memoryLabel ) ) memoryLabel = " _ " + memoryLabel ;
builder . setLabel ( 0 , memoryLabel ) ;
auto negatedLeftSubFormula = std : : make_shared < storm : : logic : : UnaryBooleanStateFormula > ( storm : : logic : : UnaryBooleanStateFormula : : OperatorType : : Not , formula . getLeftSubformula ( ) . asSharedPointer ( ) ) ;
auto targetFormula = std : : make_shared < storm : : logic : : BinaryBooleanStateFormula > ( storm : : logic : : BinaryBooleanStateFormula : : OperatorType : : Or , negatedLeftSubFormula , formula . getRightSubformula ( ) . asSharedPointer ( ) ) ;
builder . setTransition ( 0 , 0 , std : : make_shared < storm : : logic : : UnaryBooleanStateFormula > ( storm : : logic : : UnaryBooleanStateFormula : : OperatorType : : Not , targetFormula ) ) ;
builder . setTransition ( 0 , 1 , targetFormula ) ;
builder . setTransition ( 1 , 1 , std : : make_shared < storm : : logic : : BooleanLiteralFormula > ( true ) ) ;
storm : : storage : : MemoryStructure memory = builder . build ( ) ;
addMemoryToPreprocessedModel ( result , memory ) ;
// build stateAction reward vector that gives (one*transitionProbability) reward whenever a transition leads from a memoryLabel-state to a psiState
storm : : storage : : BitVector const & statesWithMemoryLabel = result . preprocessedModel . getStates ( memoryLabel ) ;
if ( ( statesWithMemoryLabel & result . preprocessedModel . getInitialStates ( ) ) . empty ( ) & & ! currentObjective . lowerTimeBound ) {
// The probability is always one as the initial state is a target state.
// For this special case, the transformation to an expected reward objective fails.
// We could handle this with further preprocessing steps but as this case is boring anyway, we simply reject the input.
STORM_LOG_THROW ( false , storm : : exceptions : : InvalidPropertyException , " The Probability for the objective " < < currentObjective . originalFormula < < " is always one as the rhs of the until formula is true in the initial state. Please omit this objective. " ) ;
}
auto duplicatorResult = storm : : transformer : : StateDuplicator < SparseModelType > : : transform ( result . preprocessedModel , ~ phiStates | psiStates ) ;
updatePreprocessedModel ( result , * duplicatorResult . model , duplicatorResult . newToOldStateIndexMapping ) ;
storm : : storage : : BitVector newPsiStates ( result . preprocessedModel . getNumberOfStates ( ) , false ) ;
for ( auto const & oldPsiState : psiStates ) {
//note that psiStates are always located in the second copy
newPsiStates . set ( duplicatorResult . secondCopyOldToNewStateIndexMapping [ oldPsiState ] , true ) ;
// We could handle this with further preprocessing steps but as this case is trivial anyway, we reject the input.
STORM_LOG_THROW ( false , storm : : exceptions : : InvalidPropertyException , " The Probability for the objective " < < * currentObjective . originalFormula < < " is always one as the rhs of the until formula is true in the initial state. Please omit this objective. " ) ;
}
storm : : modelchecker : : SparsePropositionalModelChecker < SparseModelType > mc ( result . preprocessedModel ) ;
storm : : storage : : BitVector psiStates = mc . check ( formula . getRightSubformula ( ) ) - > asExplicitQualitativeCheckResult ( ) . getTruthValuesVector ( ) ;
// build stateAction reward vector that gives (one*transitionProbability) reward whenever a transition leads from the firstCopy to a psiState
std : : vector < ValueType > objectiveRewards ( result . preprocessedModel . getTransitionMatrix ( ) . getRowCount ( ) , storm : : utility : : zero < ValueType > ( ) ) ;
for ( auto const & fir stCopyS tate : duplicatorResult . firstCopy ) {
for ( uint_fast64_t row = result . preprocessedModel . getTransitionMatrix ( ) . getRowGroupIndices ( ) [ fir stCopyS tate] ; row < result . preprocessedModel . getTransitionMatrix ( ) . getRowGroupIndices ( ) [ fir stCopyS tate + 1 ] ; + + row ) {
objectiveRewards [ row ] = result . preprocessedModel . getTransitionMatrix ( ) . getConstrainedRowSum ( row , newP siStates) ;
for ( auto const & state : statesWithMemoryLabel ) {
for ( uint_fast64_t row = result . preprocessedModel . getTransitionMatrix ( ) . getRowGroupIndices ( ) [ state ] ; row < result . preprocessedModel . getTransitionMatrix ( ) . getRowGroupIndices ( ) [ state + 1 ] ; + + row ) {
objectiveRewards [ row ] = result . preprocessedModel . getTransitionMatrix ( ) . getConstrainedRowSum ( row , psiStates ) ;
}
}
if ( ! currentObjective . rewardsArePositive ) {
@ -263,30 +289,35 @@ namespace storm {
return ;
}
CheckTask < storm : : logic : : Formula , ValueType > targetTask ( formula . getSubformula ( ) ) ;
storm : : modelchecker : : SparsePropositionalModelChecker < SparseModelType > mc ( result . preprocessedModel ) ;
STORM_LOG_THROW ( mc . canHandle ( targetTask ) , storm : : exceptions : : InvalidPropertyException , " The subformula of " < < formula < < " should be propositional. " ) ;
storm : : storage : : BitVector targetStates = mc . check ( targetTask ) - > asExplicitQualitativeCheckResult ( ) . getTruthValuesVector ( ) ;
auto duplicatorResult = storm : : transformer : : StateDuplicator < SparseModelType > : : transform ( result . preprocessedModel , targetStates ) ;
updatePreprocessedModel ( result , * duplicatorResult . model , duplicatorResult . newToOldStateIndexMapping ) ;
// Create a memory structure that stores whether a target state has already been reached
storm : : storage : : MemoryStructureBuilder builder ( 2 ) ;
// Get a unique label that is not already present in the model
std : : string memoryLabel = " obj " + std : : to_string ( result . objectives . size ( ) ) ;
while ( result . preprocessedModel . hasLabel ( memoryLabel ) ) memoryLabel = " _ " + memoryLabel ;
builder . setLabel ( 0 , memoryLabel ) ;
builder . setTransition ( 0 , 0 , std : : make_shared < storm : : logic : : UnaryBooleanStateFormula > ( storm : : logic : : UnaryBooleanStateFormula : : OperatorType : : Not , formula . getSubformula ( ) . asSharedPointer ( ) ) ) ;
builder . setTransition ( 0 , 1 , formula . getSubformula ( ) . asSharedPointer ( ) ) ;
builder . setTransition ( 1 , 1 , std : : make_shared < storm : : logic : : BooleanLiteralFormula > ( true ) ) ;
storm : : storage : : MemoryStructure memory = builder . build ( ) ;
addMemoryToPreprocessedModel ( result , memory ) ;
// Add a reward model that gives zero reward to the actions of states of the second copy.
// Add a reward model that only gives reward to states with the memory label
RewardModelType objectiveRewards ( boost : : none ) ;
if ( formula . isReachabilityRewardFormula ( ) ) {
storm : : storage : : BitVector statesWithoutMemoryLabel = ~ result . preprocessedModel . getStates ( memoryLabel ) ;
objectiveRewards = result . preprocessedModel . getRewardModel ( optionalRewardModelName ? optionalRewardModelName . get ( ) : " " ) ;
objectiveRewards . reduceToStateBasedRewards ( result . preprocessedModel . getTransitionMatrix ( ) , false ) ;
if ( objectiveRewards . hasStateRewards ( ) ) {
storm : : utility : : vector : : setVectorValues ( objectiveRewards . getStateRewardVector ( ) , duplicatorResult . secondCopy , storm : : utility : : zero < ValueType > ( ) ) ;
storm : : utility : : vector : : setVectorValues ( objectiveRewards . getStateRewardVector ( ) , statesWithoutMemoryLabel , storm : : utility : : zero < ValueType > ( ) ) ;
}
if ( objectiveRewards . hasStateActionRewards ( ) ) {
for ( auto secondCopyS tate : duplicatorResult . secondCopy ) {
std : : fill_n ( objectiveRewards . getStateActionRewardVector ( ) . begin ( ) + result . preprocessedModel . getTransitionMatrix ( ) . getRowGroupIndices ( ) [ secondCopyS tate ] , result . preprocessedModel . getTransitionMatrix ( ) . getRowGroupSize ( secondCopyS tate ) , storm : : utility : : zero < ValueType > ( ) ) ;
for ( auto state : statesWithoutMemoryLabel ) {
std : : fill_n ( objectiveRewards . getStateActionRewardVector ( ) . begin ( ) + result . preprocessedModel . getTransitionMatrix ( ) . getRowGroupIndices ( ) [ state ] , result . preprocessedModel . getTransitionMatrix ( ) . getRowGroupSize ( state ) , storm : : utility : : zero < ValueType > ( ) ) ;
}
}
} else if ( formula . isReachabilityTimeFormula ( ) & & result . preprocessedModel . isOfType ( storm : : models : : ModelType : : MarkovAutomaton ) ) {
objectiveRewards = RewardModelType ( std : : vector < ValueType > ( result . preprocessedModel . getNumberOfStates ( ) , storm : : utility : : zero < ValueType > ( ) ) ) ;
storm : : utility : : vector : : setVectorValues ( objectiveRewards . getStateRewardVector ( ) , dynamic_cast < storm : : models : : sparse : : MarkovAutomaton < ValueType > * > ( & result . preprocessedModel ) - > getMarkovianStates ( ) & duplicatorResult . firstCopy , storm : : utility : : one < ValueType > ( ) ) ;
storm : : utility : : vector : : setVectorValues ( objectiveRewards . getStateRewardVector ( ) , dynamic_cast < storm : : models : : sparse : : MarkovAutomaton < ValueType > * > ( & result . preprocessedModel ) - > getMarkovianStates ( ) & result . preprocessedModel . getStates ( memoryLabel ) , storm : : utility : : one < ValueType > ( ) ) ;
} else {
STORM_LOG_THROW ( false , storm : : exceptions : : InvalidPropertyException , " The formula " < < formula < < " neither considers reachability probabilities nor reachability rewards " < < ( result . preprocessedModel . isOfType ( storm : : models : : ModelType : : MarkovAutomaton ) ? " nor reachability time " : " " ) < < " . This is not supported. " ) ;
}