@ -28,7 +28,20 @@ namespace storm {
class Environment ;
namespace counterexamples {
/**
* Helper to avoid case disticinot between prism and jani
* Returns the number of edges / commands in a symbolic model description .
*/
size_t nrCommands ( storm : : storage : : SymbolicModelDescription const & descr ) {
if ( descr . isJaniModel ( ) ) {
return descr . asJaniModel ( ) . getNumberOfEdges ( ) ;
} else {
assert ( descr . isPrismModel ( ) ) ;
return descr . asPrismProgram ( ) . getNumberOfCommands ( ) ;
}
}
/*!
* This class provides functionality to generate a minimal counterexample to a probabilistic reachability
* property in terms of used labels .
@ -313,12 +326,18 @@ namespace storm {
* @ param context The Z3 context in which to build the expressions .
* @ param solver The solver to use for the satisfiability evaluation .
*/
static void assertCuts ( storm : : storage : : SymbolicModelDescription const & symbolicModel , storm : : models : : sparse : : Model < T > const & model , std : : vector < boost : : container : : flat_set < uint_fast64_t > > const & labelSets , storm : : storage : : BitVector const & psiStates , VariableInformation const & variableInformation , RelevancyInformation const & relevancyInformation , storm : : solver : : SmtSolver & solver ) {
static std : : chrono : : milliseconds assertCuts ( storm : : storage : : SymbolicModelDescription const & symbolicModel , storm : : models : : sparse : : Model < T > const & model , std : : vector < boost : : container : : flat_set < uint_fast64_t > > const & labelSets , storm : : storage : : BitVector const & psiStates , VariableInformation const & variableInformation , RelevancyInformation const & relevancyInformation , storm : : solver : : SmtSolver & solver , bool addBackwardImplications ) {
/ / Walk through the model and
/ / * identify labels enabled in initial states
/ / * identify labels that can directly precede a given action
/ / * identify labels that directly reach a target state
/ / * identify labels that can directly follow a given action
auto assertCutsClock = std : : chrono : : high_resolution_clock : : now ( ) ;
/ /
if ( addBackwardImplications ) {
STORM_LOG_THROW ( ! symbolicModel . isJaniModel ( ) | | ! symbolicModel . asJaniModel ( ) . usesAssignmentLevels ( ) , storm : : exceptions : : NotSupportedException , " Counterexample generation with backward implications is not supported for indexed assignments " ) ;
}
boost : : container : : flat_set < uint_fast64_t > initialLabels ;
std : : set < boost : : container : : flat_set < uint_fast64_t > > initialCombinations ;
@ -334,6 +353,7 @@ namespace storm {
storm : : storage : : BitVector const & initialStates = model . getInitialStates ( ) ;
for ( auto currentState : relevancyInformation . relevantStates ) {
bool isInitial = initialStates . get ( currentState ) ;
for ( auto currentChoice : relevancyInformation . relevantChoicesForRelevantStates . at ( currentState ) ) {
/ / If the choice is a synchronization choice , we need to record it .
@ -344,7 +364,7 @@ namespace storm {
}
/ / If the state is initial , we need to add all the choice labels to the initial label set .
if ( initialStates . get ( currentState ) ) {
if ( isI nitial ) {
initialLabels . insert ( labelSets [ currentChoice ] . begin ( ) , labelSets [ currentChoice ] . end ( ) ) ;
initialCombinations . insert ( labelSets [ currentChoice ] ) ;
}
@ -378,6 +398,7 @@ namespace storm {
for ( auto const & successorEntry : transitionMatrix . getRow ( predecessorChoice ) ) {
if ( successorEntry . getColumn ( ) = = currentState ) {
choiceTargetsCurrentState = true ;
break ;
}
}
@ -389,11 +410,10 @@ namespace storm {
}
}
}
/ / Store the found implications in a container similar to the preceding label sets .
std : : map < boost : : container : : flat_set < uint_fast64_t > , std : : set < boost : : container : : flat_set < uint_fast64_t > > > backwardImplications ;
if ( ! symbolicModel . isJaniModel ( ) | | ! symbolicModel . asJaniModel ( ) . usesAssignmentLevels ( ) ) {
if ( addBackwardImplications ) {
/ / Create a new solver over the same variables as the given symbolic model description to use it for
/ / determining the symbolic cuts .
std : : unique_ptr < storm : : solver : : SmtSolver > localSolver ;
@ -587,8 +607,7 @@ namespace storm {
/ / Iterate over all possible combinations of updates of the preceding command set .
std : : vector < storm : : expressions : : Expression > formulae ;
bool done = false ;
while ( ! done ) {
while ( true ) {
std : : map < storm : : expressions : : Variable , storm : : expressions : : Expression > currentVariableUpdateCombinationMap ;
for ( auto const & updateIterator : iteratorVector ) {
for ( auto const & variableUpdatePair : * updateIterator ) {
@ -616,7 +635,7 @@ namespace storm {
/ / If we had to reset all iterator to the start , we are done .
if ( k = = 0 ) {
done = true ;
break ;
}
}
@ -722,85 +741,88 @@ namespace storm {
assertDisjunction ( solver , formulae , * variableInformation . manager ) ;
}
}
STORM_LOG_DEBUG ( " Asserting taken labels are followed and preceeded by another label if they are not a target label or an initial label, respectively. " ) ;
boost : : container : : flat_map < boost : : container : : flat_set < uint_fast64_t > , storm : : expressions : : Expression > labelSetToFormula ;
for ( auto const & labelSet : relevancyInformation . relevantLabelSets ) {
storm : : expressions : : Expression labelSetFormula = variableInformation . manager - > boolean ( false ) ;
/ / Compute the set of unknown labels on the left - hand side of the implication .
boost : : container : : flat_set < uint_fast64_t > unknownLhsLabels ;
std : : set_difference ( labelSet . begin ( ) , labelSet . end ( ) , relevancyInformation . knownLabels . begin ( ) , relevancyInformation . knownLabels . end ( ) , std : : inserter ( unknownLhsLabels , unknownLhsLabels . end ( ) ) ) ;
for ( auto label : unknownLhsLabels ) {
labelSetFormula = labelSetFormula | | ! variableInformation . labelVariables . at ( variableInformation . labelToIndexMap . at ( label ) ) ;
}
/ / Only build a constraint if the combination does not lead to a target state and
/ / no successor set is already known .
storm : : expressions : : Expression successorExpression ;
if ( targetCombinations . find ( labelSet ) = = targetCombinations . end ( ) & & hasKnownSuccessor . find ( labelSet ) = = hasKnownSuccessor . end ( ) ) {
successorExpression = variableInformation . manager - > boolean ( false ) ;
if ( addBackwardImplications ) {
auto const & followingLabelSets = followingLabels . at ( labelSet ) ;
STORM_LOG_DEBUG ( " Asserting taken labels are followed and preceeded by another label if they are not a target label or an initial label, respectively. " ) ;
boost : : container : : flat_map < boost : : container : : flat_set < uint_fast64_t > , storm : : expressions : : Expression > labelSetToFormula ;
for ( auto const & labelSet : relevancyInformation . relevantLabelSets ) {
storm : : expressions : : Expression labelSetFormula = variableInformation . manager - > boolean ( false ) ;
for ( auto const & followingSet : followingLabelSets ) {
boost : : container : : flat_set < uint_fast64_t > tmpSet ;
/ / Check which labels of the current following set are not known . This set must be non - empty , because
/ / otherwise a successor combination would already be known and control cannot reach this point .
std : : set_difference ( followingSet . begin ( ) , followingSet . end ( ) , relevancyInformation . knownLabels . begin ( ) , relevancyInformation . knownLabels . end ( ) , std : : inserter ( tmpSet , tmpSet . end ( ) ) ) ;
/ / Construct an expression that enables all unknown labels of the current following set .
storm : : expressions : : Expression conj = variableInformation . manager - > boolean ( true ) ;
for ( auto label : tmpSet ) {
conj = conj & & variableInformation . labelVariables . at ( variableInformation . labelToIndexMap . at ( label ) ) ;
/ / Compute the set of unknown labels on the left - hand side of the implication .
boost : : container : : flat_set < uint_fast64_t > unknownLhsLabels ;
std : : set_difference ( labelSet . begin ( ) , labelSet . end ( ) , relevancyInformation . knownLabels . begin ( ) , relevancyInformation . knownLabels . end ( ) , std : : inserter ( unknownLhsLabels , unknownLhsLabels . end ( ) ) ) ;
for ( auto label : unknownLhsLabels ) {
labelSetFormula = labelSetFormula | | ! variableInformation . labelVariables . at ( variableInformation . labelToIndexMap . at ( label ) ) ;
}
/ / Only build a constraint if the combination does not lead to a target state and
/ / no successor set is already known .
storm : : expressions : : Expression successorExpression ;
if ( targetCombinations . find ( labelSet ) = = targetCombinations . end ( ) & & hasKnownSuccessor . find ( labelSet ) = = hasKnownSuccessor . end ( ) ) {
successorExpression = variableInformation . manager - > boolean ( false ) ;
auto const & followingLabelSets = followingLabels . at ( labelSet ) ;
for ( auto const & followingSet : followingLabelSets ) {
boost : : container : : flat_set < uint_fast64_t > tmpSet ;
/ / Check which labels of the current following set are not known . This set must be non - empty , because
/ / otherwise a successor combination would already be known and control cannot reach this point .
std : : set_difference ( followingSet . begin ( ) , followingSet . end ( ) , relevancyInformation . knownLabels . begin ( ) , relevancyInformation . knownLabels . end ( ) , std : : inserter ( tmpSet , tmpSet . end ( ) ) ) ;
/ / Construct an expression that enables all unknown labels of the current following set .
storm : : expressions : : Expression conj = variableInformation . manager - > boolean ( true ) ;
for ( auto label : tmpSet ) {
conj = conj & & variableInformation . labelVariables . at ( variableInformation . labelToIndexMap . at ( label ) ) ;
}
successorExpression = successorExpression | | conj ;
}
successorExpression = successorExpression | | conj ;
} else {
successorExpression = variableInformation . manager - > boolean ( true ) ;
}
} else {
successorExpression = variableInformation . manager - > boolean ( true ) ;
}
/ / Constructed following cuts at this point .
/ / Only build a constraint if the combination is no initial combination and no
/ / predecessor set is already known .
storm : : expressions : : Expression predecessorExpression ;
if ( initialCombinations . find ( labelSet ) = = initialCombinations . end ( ) & & hasKnownPredecessor . find ( labelSet ) = = hasKnownPredecessor . end ( ) ) {
predecessorExpression = variableInformation . manager - > boolean ( false ) ;
/ / Constructed following cuts at this point .
/ / Only build a constraint if the combination is no initial combination and no
/ / predecessor set is already known .
storm : : expressions : : Expression predecessorExpression ;
if ( initialCombinations . find ( labelSet ) = = initialCombinations . end ( ) & & hasKnownPredecessor . find ( labelSet ) = = hasKnownPredecessor . end ( ) ) {
predecessorExpression = variableInformation . manager - > boolean ( false ) ;
/ / std : : cout < < " labelSet " < < std : : endl ;
/ / for ( auto const & e : labelSet ) {
/ / std : : cout < < e < < " , " ;
/ / }
/ / std : : cout < < std : : endl ;
auto const & preceedingLabelSets = backwardImplications . at ( labelSet ) ;
auto const & preceedingLabelSets = backwardImplications . at ( labelSet ) ;
for ( auto const & preceedingSet : preceedingLabelSets ) {
boost : : container : : flat_set < uint_fast64_t > tmpSet ;
/ / Check which labels of the current following set are not known . This set must be non - empty , because
/ / otherwise a predecessor combination would already be known and control cannot reach this point .
std : : set_difference ( preceedingSet . begin ( ) , preceedingSet . end ( ) , relevancyInformation . knownLabels . begin ( ) , relevancyInformation . knownLabels . end ( ) , std : : inserter ( tmpSet , tmpSet . end ( ) ) ) ;
/ / Construct an expression that enables all unknown labels of the current following set .
storm : : expressions : : Expression conj = variableInformation . manager - > boolean ( true ) ;
for ( auto label : tmpSet ) {
conj = conj & & variableInformation . labelVariables . at ( variableInformation . labelToIndexMap . at ( label ) ) ;
for ( auto const & preceedingSet : preceedingLabelSets ) {
boost : : container : : flat_set < uint_fast64_t > tmpSet ;
/ / Check which labels of the current following set are not known . This set must be non - empty , because
/ / otherwise a predecessor combination would already be known and control cannot reach this point .
std : : set_difference ( preceedingSet . begin ( ) , preceedingSet . end ( ) , relevancyInformation . knownLabels . begin ( ) , relevancyInformation . knownLabels . end ( ) , std : : inserter ( tmpSet , tmpSet . end ( ) ) ) ;
/ / Construct an expression that enables all unknown labels of the current following set .
storm : : expressions : : Expression conj = variableInformation . manager - > boolean ( true ) ;
for ( auto label : tmpSet ) {
conj = conj & & variableInformation . labelVariables . at ( variableInformation . labelToIndexMap . at ( label ) ) ;
}
predecessorExpression = predecessorExpression | | conj ;
}
predecessorExpression = predecessorExpression | | conj ;
} else {
predecessorExpression = variableInformation . manager - > boolean ( true ) ;
}
} else {
predecessorExpression = variableInformation . manager - > boolean ( true ) ;
labelSetFormula = labelSetFormula | | ( successorExpression & & predecessorExpression ) ;
labelSetToFormula [ labelSet ] = labelSetFormula ;
}
labelSetFormula = labelSetFormula | | ( successorExpression & & predecessorExpression ) ;
labelSetToFormula [ labelSet ] = labelSetFormula ;
}
for ( auto const & labelSetFormula : labelSetToFormula ) {
solver . add ( labelSetFormula . second | | getOtherSynchronizationEnabledFormula ( labelSetFormula . first , synchronizingLabels , labelSetToFormula , variableInformation , relevancyInformation ) ) ;
for ( auto const & labelSetFormula : labelSetToFormula ) {
solver . add ( labelSetFormula . second | | getOtherSynchronizationEnabledFormula ( labelSetFormula . first , synchronizingLabels , labelSetToFormula , variableInformation , relevancyInformation ) ) ;
}
}
STORM_LOG_DEBUG ( " Asserting synchronization cuts. " ) ;
@ -837,6 +859,9 @@ namespace storm {
assertDisjunction ( solver , formulae , * variableInformation . manager ) ;
}
}
auto endTime = std : : chrono : : high_resolution_clock : : now ( ) ;
return std : : chrono : : duration_cast < std : : chrono : : milliseconds > ( endTime - assertCutsClock ) ;
}
/*!
@ -1021,10 +1046,12 @@ namespace storm {
*/
static std : : vector < std : : vector < storm : : expressions : : Expression > > createAdderPairs ( VariableInformation const & variableInformation , std : : vector < std : : vector < storm : : expressions : : Expression > > const & in ) {
std : : vector < std : : vector < storm : : expressions : : Expression > > result ;
result . reserve ( in . size ( ) / 2 + in . size ( ) % 2 ) ;
for ( uint_fast64_t index = 0 ; index < in . size ( ) / 2 ; + + index ) {
uint64_t maxIndex = in . size ( ) / 2 ;
result . reserve ( maxIndex + in . size ( ) % 2 ) ;
for ( uint_fast64_t index = 0 ; index < maxIndex ; + + index ) {
result . push_back ( createAdder ( variableInformation , in [ 2 * index ] , in [ 2 * index + 1 ] ) ) ;
}
@ -1586,9 +1613,11 @@ namespace storm {
bool encodeReachability ;
bool useDynamicConstraints ;
bool silent = false ;
bool addBackwardImplicationCuts = true ;
uint64_t continueAfterFirstCounterexampleUntil = 0 ;
uint64_t maximumCounterexamples = 1 ;
uint64_t multipleCounterexampleSizeCap = 100000000 ;
uint64_t maximumExtraIterations = 100000000 ;
} ;
struct GeneratorStats {
@ -1596,8 +1625,11 @@ namespace storm {
std : : chrono : : milliseconds solverTime ;
std : : chrono : : milliseconds modelCheckingTime ;
std : : chrono : : milliseconds analysisTime ;
std : : chrono : : milliseconds cutTime ;
uint64_t iterations ;
} ;
/*!
* Computes the minimal command set that is needed in the given model to exceed the given probability threshold for satisfying phi until psi .
*
@ -1679,19 +1711,19 @@ namespace storm {
/ / and subsequently relax that .
variableInformation . adderVariables = assertAdder ( * solver , variableInformation ) ;
variableInformation . auxiliaryVariables . push_back ( assertLessOrEqualKRelaxed ( * solver , variableInformation , 0 ) ) ;
/ / As we are done with the setup at this point , stop the clock for the setup time .
totalSetupTime = std : : chrono : : high_resolution_clock : : now ( ) - setupTimeClock ;
/ / ( 6 ) Add constraints that cut off a lot of suboptimal solutions .
STORM_LOG_DEBUG ( " Asserting cuts. " ) ;
assertCuts ( symbolicModel , model , labelSets , psiStates , variableInformation , relevancyInformation , * solver ) ;
stats . cutTime = assertCuts ( symbolicModel , model , labelSets , psiStates , variableInformation , relevancyInformation , * solver , options . addBackwardImplicationCuts ) ;
STORM_LOG_DEBUG ( " Asserted cuts. " ) ;
if ( options . encodeReachability ) {
assertReachabilityCuts ( model , labelSets , psiStates , variableInformation , relevancyInformation , * solver ) ;
STORM_LOG_DEBUG ( " Asserted reachability cuts. " ) ;
}
/ / As we are done with the setup at this point , stop the clock for the setup time .
totalSetupTime = std : : chrono : : high_resolution_clock : : now ( ) - setupTimeClock ;
/ / ( 7 ) Find the smallest set of commands that satisfies all constraints . If the probability of
/ / satisfying phi until psi exceeds the given threshold , the set of labels is minimal and can be returned .
/ / Otherwise , the current solution has to be ruled out and the next smallest solution is retrieved from
@ -1712,11 +1744,17 @@ namespace storm {
uint_fast64_t lastSize = 0 ;
uint_fast64_t iterations = 0 ;
uint_fast64_t currentBound = 0 ;
uint64_t firstCounterexampleFound = 0 ; / / The value is not queried before being set .
std : : vector < double > maximalPropertyValue ;
uint_fast64_t zeroProbabilityCount = 0 ;
uint64 _t smallestCounterexampleSize = model . getNumberOfChoices ( ) ; / / Definitive u
size _t smallestCounterexampleSize = model . getNumberOfChoices ( ) ; / / Definitive upper bound
uint64_t progressDelay = storm : : settings : : getModule < storm : : settings : : modules : : GeneralSettings > ( ) . getShowProgressDelay ( ) ;
do {
+ + iterations ;
if ( result . size ( ) > 0 & & iterations > firstCounterexampleFound + options . maximumExtraIterations ) {
break ;
}
STORM_LOG_DEBUG ( " Computing minimal command set. " ) ;
solverClock = std : : chrono : : high_resolution_clock : : now ( ) ;
boost : : optional < boost : : container : : flat_set < uint_fast64_t > > smallest = findSmallestCommandSet ( * solver , variableInformation , currentBound ) ;
@ -1781,19 +1819,21 @@ namespace storm {
}
} else {
STORM_LOG_DEBUG ( " Found a counterexample. " ) ;
if ( result . empty ( ) ) {
/ / If this is the first counterexample we find , we store when we found it .
firstCounterexampleFound = iterations ;
}
result . push_back ( commandSet ) ;
if ( options . maximumCounterexamples > result . size ( ) ) {
STORM_LOG_DEBUG ( " Exclude counterexample for future. " ) ;
ruleOutBiggerSolutions ( * solver , commandSet , variableInformation , relevancyInformation ) ;
} else {
STORM_LOG_DEBUG ( " Stop searching for further counterexamples. " ) ;
done = true ;
}
smallestCounterexampleSize = std : : min ( smallestCounterexampleSize , commandSet . size ( ) ) ;
}
totalAnalysisTime + = ( std : : chrono : : high_resolution_clock : : now ( ) - analysisClock ) ;
+ + iterations ;
auto now = std : : chrono : : high_resolution_clock : : now ( ) ;
auto durationSinceLastMessage = std : : chrono : : duration_cast < std : : chrono : : seconds > ( now - timeOfLastMessage ) . count ( ) ;
@ -1816,6 +1856,7 @@ namespace storm {
stats . setupTime = std : : chrono : : duration_cast < std : : chrono : : milliseconds > ( totalSetupTime ) ;
stats . modelCheckingTime = std : : chrono : : duration_cast < std : : chrono : : milliseconds > ( totalModelCheckingTime ) ;
stats . solverTime = std : : chrono : : duration_cast < std : : chrono : : milliseconds > ( totalSolverTime ) ;
stats . iterations = iterations ;
if ( storm : : settings : : getModule < storm : : settings : : modules : : CoreSettings > ( ) . isShowStatisticsSet ( ) ) {
boost : : container : : flat_set < uint64_t > allLabels ;