@ -7,6 +7,8 @@
# include "storm/utility/DirectEncodingExporter.h"
# include "storm/modelchecker/results/ExplicitQuantitativeCheckResult.h"
# include "storm/modelchecker/results/ExplicitQualitativeCheckResult.h"
# include "storm/models/ModelType.h"
# include "storm/transformer/NonMarkovianChainTransformer.h"
# include "storm-dft/builder/ExplicitDFTModelBuilder.h"
# include "storm-dft/storage/dft/DFTIsomorphism.h"
@ -17,7 +19,13 @@ namespace storm {
namespace modelchecker {
template < typename ValueType >
typename DFTModelChecker < ValueType > : : dft_results DFTModelChecker < ValueType > : : check ( storm : : storage : : DFT < ValueType > const & origDft , std : : vector < std : : shared_ptr < const storm : : logic : : Formula > > const & properties , bool symred , bool allowModularisation , std : : set < size_t > const & relevantEvents , bool allowDCForRelevantEvents , double approximationError , storm : : builder : : ApproximationHeuristic approximationHeuristic ) {
typename DFTModelChecker < ValueType > : : dft_results
DFTModelChecker < ValueType > : : check ( storm : : storage : : DFT < ValueType > const & origDft ,
std : : vector < std : : shared_ptr < const storm : : logic : : Formula > > const & properties ,
bool symred , bool allowModularisation , std : : set < size_t > const & relevantEvents ,
bool allowDCForRelevantEvents , double approximationError ,
storm : : builder : : ApproximationHeuristic approximationHeuristic ,
bool eliminateChains , bool ignoreLabeling ) {
totalTimer . start ( ) ;
dft_results results ;
@ -30,21 +38,32 @@ namespace storm {
// TODO: distinguish for all properties, not only for first one
if ( properties [ 0 ] - > isTimeOperatorFormula ( ) & & allowModularisation ) {
// Use parallel composition as modularisation approach for expected time
std : : shared_ptr < storm : : models : : sparse : : Model < ValueType > > model = buildModelViaComposition ( dft , properties , symred , true , relevantEvents ) ;
std : : shared_ptr < storm : : models : : sparse : : Model < ValueType > > model = buildModelViaComposition ( dft ,
properties ,
symred , true ,
relevantEvents ) ;
// Model checking
std : : vector < ValueType > resultsValue = checkModel ( model , properties ) ;
for ( ValueType result : resultsValue ) {
results . push_back ( result ) ;
}
} else {
results = checkHelper ( dft , properties , symred , allowModularisation , relevantEvents , allowDCForRelevantEvents , approximationError , approximationHeuristic ) ;
results = checkHelper ( dft , properties , symred , allowModularisation , relevantEvents ,
allowDCForRelevantEvents , approximationError , approximationHeuristic ,
eliminateChains , ignoreLabeling ) ;
}
totalTimer . stop ( ) ;
return results ;
}
template < typename ValueType >
typename DFTModelChecker < ValueType > : : dft_results DFTModelChecker < ValueType > : : checkHelper ( storm : : storage : : DFT < ValueType > const & dft , property_vector const & properties , bool symred , bool allowModularisation , std : : set < size_t > const & relevantEvents , bool allowDCForRelevantEvents , double approximationError , storm : : builder : : ApproximationHeuristic approximationHeuristic ) {
typename DFTModelChecker < ValueType > : : dft_results
DFTModelChecker < ValueType > : : checkHelper ( storm : : storage : : DFT < ValueType > const & dft ,
property_vector const & properties , bool symred ,
bool allowModularisation , std : : set < size_t > const & relevantEvents ,
bool allowDCForRelevantEvents , double approximationError ,
storm : : builder : : ApproximationHeuristic approximationHeuristic ,
bool eliminateChains , bool ignoreLabeling ) {
STORM_LOG_TRACE ( " Check helper called " ) ;
std : : vector < storm : : storage : : DFT < ValueType > > dfts ;
bool invResults = false ;
@ -73,7 +92,8 @@ namespace storm {
STORM_LOG_TRACE ( " top modularisation called VOT " ) ;
dfts = dft . topModularisation ( ) ;
STORM_LOG_TRACE ( " Modularisation into " < < dfts . size ( ) < < " submodules. " ) ;
nrK = std : : static_pointer_cast < storm : : storage : : DFTVot < ValueType > const > ( dft . getTopLevelGate ( ) ) - > threshold ( ) ;
nrK = std : : static_pointer_cast < storm : : storage : : DFTVot < ValueType > const > (
dft . getTopLevelGate ( ) ) - > threshold ( ) ;
nrM = dfts . size ( ) ;
if ( nrK < = nrM / 2 ) {
nrK - = 1 ;
@ -99,18 +119,21 @@ namespace storm {
std : : vector < ValueType > res ;
for ( auto const ft : dfts ) {
// TODO: allow approximation in modularisation
dft_results ftResults = checkHelper ( ft , { property } , symred , true , relevantEvents , allowDCForRelevantEvents , 0.0 ) ;
dft_results ftResults = checkHelper ( ft , { property } , symred , true , relevantEvents ,
allowDCForRelevantEvents , 0.0 ) ;
STORM_LOG_ASSERT ( ftResults . size ( ) = = 1 , " Wrong number of results " ) ;
res . push_back ( boost : : get < ValueType > ( ftResults [ 0 ] ) ) ;
}
// Combine modularisation results
STORM_LOG_TRACE ( " Combining all results... K= " < < nrK < < " ; M= " < < nrM < < " ; invResults= " < < ( invResults ? " On " : " Off " ) ) ;
STORM_LOG_TRACE ( " Combining all results... K= " < < nrK < < " ; M= " < < nrM < < " ; invResults= "
< < ( invResults ? " On " : " Off " ) ) ;
ValueType result = storm : : utility : : zero < ValueType > ( ) ;
int limK = invResults ? - 1 : nrM + 1 ;
int chK = invResults ? - 1 : 1 ;
// WARNING: there is a bug for computing permutations with more than 32 elements
STORM_LOG_THROW ( res . size ( ) < 32 , storm : : exceptions : : NotSupportedException , " Permutations work only for < 32 elements " ) ;
STORM_LOG_THROW ( res . size ( ) < 32 , storm : : exceptions : : NotSupportedException ,
" Permutations work only for < 32 elements " ) ;
for ( int cK = nrK ; cK ! = limK ; cK + = chK ) {
STORM_LOG_ASSERT ( cK > = 0 , " ck negative. " ) ;
size_t permutation = smallestIntWithNBitsSet ( static_cast < size_t > ( cK ) ) ;
@ -138,12 +161,18 @@ namespace storm {
return results ;
} else {
// No modularisation was possible
return checkDFT ( dft , properties , symred , relevantEvents , allowDCForRelevantEvents , approximationError , approximationHeuristic ) ;
return checkDFT ( dft , properties , symred , relevantEvents , allowDCForRelevantEvents , approximationError ,
approximationHeuristic , eliminateChains , ignoreLabeling ) ;
}
}
template < typename ValueType >
std : : shared_ptr < storm : : models : : sparse : : Ctmc < ValueType > > DFTModelChecker < ValueType > : : buildModelViaComposition ( storm : : storage : : DFT < ValueType > const & dft , property_vector const & properties , bool symred , bool allowModularisation , std : : set < size_t > const & relevantEvents , bool allowDCForRelevantEvents ) {
std : : shared_ptr < storm : : models : : sparse : : Ctmc < ValueType > >
DFTModelChecker < ValueType > : : buildModelViaComposition ( storm : : storage : : DFT < ValueType > const & dft ,
property_vector const & properties , bool symred ,
bool allowModularisation ,
std : : set < size_t > const & relevantEvents ,
bool allowDCForRelevantEvents ) {
// TODO: use approximation?
STORM_LOG_TRACE ( " Build model via composition " ) ;
std : : vector < storm : : storage : : DFT < ValueType > > dfts ;
@ -194,29 +223,37 @@ namespace storm {
// Build a single CTMC
STORM_LOG_DEBUG ( " Building Model... " ) ;
storm : : builder : : ExplicitDFTModelBuilder < ValueType > builder ( ft , symmetries , relevantEvents , allowDCForRelevantEvents ) ;
storm : : builder : : ExplicitDFTModelBuilder < ValueType > builder ( ft , symmetries , relevantEvents ,
allowDCForRelevantEvents ) ;
builder . buildModel ( 0 , 0.0 ) ;
std : : shared_ptr < storm : : models : : sparse : : Model < ValueType > > model = builder . getModel ( ) ;
explorationTimer . stop ( ) ;
STORM_LOG_THROW ( model - > isOfType ( storm : : models : : ModelType : : Ctmc ) , storm : : exceptions : : NotSupportedException , " Parallel composition only applicable for CTMCs " ) ;
STORM_LOG_THROW ( model - > isOfType ( storm : : models : : ModelType : : Ctmc ) ,
storm : : exceptions : : NotSupportedException ,
" Parallel composition only applicable for CTMCs " ) ;
std : : shared_ptr < storm : : models : : sparse : : Ctmc < ValueType > > ctmc = model - > template as < storm : : models : : sparse : : Ctmc < ValueType > > ( ) ;
// Apply bisimulation to new CTMC
bisimulationTimer . start ( ) ;
ctmc = storm : : api : : performDeterministicSparseBisimulationMinimization < storm : : models : : sparse : : Ctmc < ValueType > > ( ctmc , properties , storm : : storage : : BisimulationType : : Weak ) - > template as < storm : : models : : sparse : : Ctmc < ValueType > > ( ) ;
ctmc = storm : : api : : performDeterministicSparseBisimulationMinimization < storm : : models : : sparse : : Ctmc < ValueType > > (
ctmc , properties ,
storm : : storage : : BisimulationType : : Weak ) - > template as < storm : : models : : sparse : : Ctmc < ValueType > > ( ) ;
bisimulationTimer . stop ( ) ;
if ( firstTime ) {
composedModel = ctmc ;
firstTime = false ;
} else {
composedModel = storm : : builder : : ParallelCompositionBuilder < ValueType > : : compose ( composedModel , ctmc , isAnd ) ;
composedModel = storm : : builder : : ParallelCompositionBuilder < ValueType > : : compose ( composedModel ,
ctmc , isAnd ) ;
}
// Apply bisimulation to parallel composition
bisimulationTimer . start ( ) ;
composedModel = storm : : api : : performDeterministicSparseBisimulationMinimization < storm : : models : : sparse : : Ctmc < ValueType > > ( composedModel , properties , storm : : storage : : BisimulationType : : Weak ) - > template as < storm : : models : : sparse : : Ctmc < ValueType > > ( ) ;
composedModel = storm : : api : : performDeterministicSparseBisimulationMinimization < storm : : models : : sparse : : Ctmc < ValueType > > (
composedModel , properties ,
storm : : storage : : BisimulationType : : Weak ) - > template as < storm : : models : : sparse : : Ctmc < ValueType > > ( ) ;
bisimulationTimer . stop ( ) ;
STORM_LOG_DEBUG ( " No. states (Composed): " < < composedModel - > getNumberOfStates ( ) ) ;
@ -245,18 +282,27 @@ namespace storm {
// Build a single CTMC
STORM_LOG_DEBUG ( " Building Model... " ) ;
storm : : builder : : ExplicitDFTModelBuilder < ValueType > builder ( dft , symmetries , relevantEvents , allowDCForRelevantEvents ) ;
storm : : builder : : ExplicitDFTModelBuilder < ValueType > builder ( dft , symmetries , relevantEvents ,
allowDCForRelevantEvents ) ;
builder . buildModel ( 0 , 0.0 ) ;
std : : shared_ptr < storm : : models : : sparse : : Model < ValueType > > model = builder . getModel ( ) ;
//model->printModelInformationToStream(std::cout);
explorationTimer . stop ( ) ;
STORM_LOG_THROW ( model - > isOfType ( storm : : models : : ModelType : : Ctmc ) , storm : : exceptions : : NotSupportedException , " Parallel composition only applicable for CTMCs " ) ;
STORM_LOG_THROW ( model - > isOfType ( storm : : models : : ModelType : : Ctmc ) ,
storm : : exceptions : : NotSupportedException ,
" Parallel composition only applicable for CTMCs " ) ;
return model - > template as < storm : : models : : sparse : : Ctmc < ValueType > > ( ) ;
}
}
template < typename ValueType >
typename DFTModelChecker < ValueType > : : dft_results DFTModelChecker < ValueType > : : checkDFT ( storm : : storage : : DFT < ValueType > const & dft , property_vector const & properties , bool symred , std : : set < size_t > const & relevantEvents , bool allowDCForRelevantEvents , double approximationError , storm : : builder : : ApproximationHeuristic approximationHeuristic ) {
typename DFTModelChecker < ValueType > : : dft_results
DFTModelChecker < ValueType > : : checkDFT ( storm : : storage : : DFT < ValueType > const & dft ,
property_vector const & properties , bool symred ,
std : : set < size_t > const & relevantEvents , bool allowDCForRelevantEvents ,
double approximationError ,
storm : : builder : : ApproximationHeuristic approximationHeuristic ,
bool eliminateChains , bool ignoreLabeling ) {
explorationTimer . start ( ) ;
// Find symmetries
@ -273,10 +319,12 @@ namespace storm {
// Comparator for checking the error of the approximation
storm : : utility : : ConstantsComparator < ValueType > comparator ;
// Build approximate Markov Automata for lower and upper bound
approximation_result approxResult = std : : make_pair ( storm : : utility : : zero < ValueType > ( ) , storm : : utility : : zero < ValueType > ( ) ) ;
approximation_result approxResult = std : : make_pair ( storm : : utility : : zero < ValueType > ( ) ,
storm : : utility : : zero < ValueType > ( ) ) ;
std : : shared_ptr < storm : : models : : sparse : : Model < ValueType > > model ;
std : : vector < ValueType > newResult ;
storm : : builder : : ExplicitDFTModelBuilder < ValueType > builder ( dft , symmetries , relevantEvents , allowDCForRelevantEvents ) ;
storm : : builder : : ExplicitDFTModelBuilder < ValueType > builder ( dft , symmetries , relevantEvents ,
allowDCForRelevantEvents ) ;
// TODO: compute approximation for all properties simultaneously?
std : : shared_ptr < const storm : : logic : : Formula > property = properties [ 0 ] ;
@ -285,7 +333,9 @@ namespace storm {
}
bool probabilityFormula = property - > isProbabilityOperatorFormula ( ) ;
STORM_LOG_ASSERT ( ( property - > isTimeOperatorFormula ( ) & & ! probabilityFormula ) | | ( ! property - > isTimeOperatorFormula ( ) & & probabilityFormula ) , " Probability formula not initialized correctly " ) ;
STORM_LOG_ASSERT ( ( property - > isTimeOperatorFormula ( ) & & ! probabilityFormula ) | |
( ! property - > isTimeOperatorFormula ( ) & & probabilityFormula ) ,
" Probability formula not initialized correctly " ) ;
size_t iteration = 0 ;
do {
// Iteratively build finer models
@ -310,7 +360,9 @@ namespace storm {
// Check lower bounds
newResult = checkModel ( model , { property } ) ;
STORM_LOG_ASSERT ( newResult . size ( ) = = 1 , " Wrong size for result vector. " ) ;
STORM_LOG_ASSERT ( iteration = = 0 | | ! comparator . isLess ( newResult [ 0 ] , approxResult . first ) , " New under-approximation " < < newResult [ 0 ] < < " is smaller than old result " < < approxResult . first ) ;
STORM_LOG_ASSERT ( iteration = = 0 | | ! comparator . isLess ( newResult [ 0 ] , approxResult . first ) ,
" New under-approximation " < < newResult [ 0 ] < < " is smaller than old result "
< < approxResult . first ) ;
approxResult . first = newResult [ 0 ] ;
// Build model for upper bound
@ -321,17 +373,27 @@ namespace storm {
// Check upper bound
newResult = checkModel ( model , { property } ) ;
STORM_LOG_ASSERT ( newResult . size ( ) = = 1 , " Wrong size for result vector. " ) ;
STORM_LOG_ASSERT ( iteration = = 0 | | ! comparator . isLess ( approxResult . second , newResult [ 0 ] ) , " New over-approximation " < < newResult [ 0 ] < < " is greater than old result " < < approxResult . second ) ;
STORM_LOG_ASSERT ( iteration = = 0 | | ! comparator . isLess ( approxResult . second , newResult [ 0 ] ) ,
" New over-approximation " < < newResult [ 0 ] < < " is greater than old result "
< < approxResult . second ) ;
approxResult . second = newResult [ 0 ] ;
+ + iteration ;
STORM_LOG_ASSERT ( comparator . isLess ( approxResult . first , approxResult . second ) | | comparator . isEqual ( approxResult . first , approxResult . second ) , " Under-approximation " < < approxResult . first < < " is greater than over-approximation " < < approxResult . second ) ;
STORM_LOG_ASSERT ( comparator . isLess ( approxResult . first , approxResult . second ) | |
comparator . isEqual ( approxResult . first , approxResult . second ) ,
" Under-approximation " < < approxResult . first
< < " is greater than over-approximation "
< < approxResult . second ) ;
//STORM_LOG_INFO("Result after iteration " << iteration << ": (" << std::setprecision(10) << approxResult.first << ", " << approxResult.second << ")");
totalTimer . stop ( ) ;
printTimings ( ) ;
totalTimer . start ( ) ;
STORM_LOG_THROW ( ! storm : : utility : : isInfinity < ValueType > ( approxResult . first ) & & ! storm : : utility : : isInfinity < ValueType > ( approxResult . second ) , storm : : exceptions : : NotSupportedException , " Approximation does not work if result might be infinity. " ) ;
} while ( ! isApproximationSufficient ( approxResult . first , approxResult . second , approximationError , probabilityFormula ) ) ;
STORM_LOG_THROW ( ! storm : : utility : : isInfinity < ValueType > ( approxResult . first ) & &
! storm : : utility : : isInfinity < ValueType > ( approxResult . second ) ,
storm : : exceptions : : NotSupportedException ,
" Approximation does not work if result might be infinity. " ) ;
} while ( ! isApproximationSufficient ( approxResult . first , approxResult . second , approximationError ,
probabilityFormula ) ) ;
//STORM_LOG_INFO("Finished approximation after " << iteration << " iteration" << (iteration > 1 ? "s." : "."));
dft_results results ;
@ -341,9 +403,15 @@ namespace storm {
// Build a single Markov Automaton
auto ioSettings = storm : : settings : : getModule < storm : : settings : : modules : : IOSettings > ( ) ;
STORM_LOG_DEBUG ( " Building Model... " ) ;
storm : : builder : : ExplicitDFTModelBuilder < ValueType > builder ( dft , symmetries , relevantEvents , allowDCForRelevantEvents ) ;
storm : : builder : : ExplicitDFTModelBuilder < ValueType > builder ( dft , symmetries , relevantEvents ,
allowDCForRelevantEvents ) ;
builder . buildModel ( 0 , 0.0 ) ;
std : : shared_ptr < storm : : models : : sparse : : Model < ValueType > > model = builder . getModel ( ) ;
if ( eliminateChains & & model - > isOfType ( storm : : models : : ModelType : : MarkovAutomaton ) ) {
auto ma = std : : static_pointer_cast < storm : : models : : sparse : : MarkovAutomaton < ValueType > > ( model ) ;
model = storm : : transformer : : NonMarkovianChainTransformer < ValueType > : : eliminateNonmarkovianStates ( ma ,
! ignoreLabeling ) ;
}
explorationTimer . stop ( ) ;
// Print model information
@ -376,12 +444,17 @@ namespace storm {
}
template < typename ValueType >
std : : vector < ValueType > DFTModelChecker < ValueType > : : checkModel ( std : : shared_ptr < storm : : models : : sparse : : Model < ValueType > > & model , property_vector const & properties ) {
std : : vector < ValueType >
DFTModelChecker < ValueType > : : checkModel ( std : : shared_ptr < storm : : models : : sparse : : Model < ValueType > > & model ,
property_vector const & properties ) {
// Bisimulation
if ( model - > isOfType ( storm : : models : : ModelType : : Ctmc ) & & storm : : settings : : getModule < storm : : settings : : modules : : GeneralSettings > ( ) . isBisimulationSet ( ) ) {
if ( model - > isOfType ( storm : : models : : ModelType : : Ctmc ) & &
storm : : settings : : getModule < storm : : settings : : modules : : GeneralSettings > ( ) . isBisimulationSet ( ) ) {
bisimulationTimer . start ( ) ;
STORM_LOG_DEBUG ( " Bisimulation... " ) ;
model = storm : : api : : performDeterministicSparseBisimulationMinimization < storm : : models : : sparse : : Ctmc < ValueType > > ( model - > template as < storm : : models : : sparse : : Ctmc < ValueType > > ( ) , properties , storm : : storage : : BisimulationType : : Weak ) - > template as < storm : : models : : sparse : : Ctmc < ValueType > > ( ) ;
model = storm : : api : : performDeterministicSparseBisimulationMinimization < storm : : models : : sparse : : Ctmc < ValueType > > (
model - > template as < storm : : models : : sparse : : Ctmc < ValueType > > ( ) , properties ,
storm : : storage : : BisimulationType : : Weak ) - > template as < storm : : models : : sparse : : Ctmc < ValueType > > ( ) ;
STORM_LOG_DEBUG ( " No. states (Bisimulation): " < < model - > getNumberOfStates ( ) ) ;
STORM_LOG_DEBUG ( " No. transitions (Bisimulation): " < < model - > getNumberOfTransitions ( ) ) ;
bisimulationTimer . stop ( ) ;
@ -398,7 +471,9 @@ namespace storm {
singleModelCheckingTimer . reset ( ) ;
singleModelCheckingTimer . start ( ) ;
//STORM_PRINT_AND_LOG("Model checking property " << *property << " ..." << std::endl);
std : : unique_ptr < storm : : modelchecker : : CheckResult > result ( storm : : api : : verifyWithSparseEngine < ValueType > ( model , storm : : api : : createTask < ValueType > ( property , true ) ) ) ;
std : : unique_ptr < storm : : modelchecker : : CheckResult > result (
storm : : api : : verifyWithSparseEngine < ValueType > ( model , storm : : api : : createTask < ValueType > ( property ,
true ) ) ) ;
STORM_LOG_ASSERT ( result , " Result does not exist. " ) ;
result - > filter ( storm : : modelchecker : : ExplicitQualitativeCheckResult ( model - > getInitialStates ( ) ) ) ;
ValueType resultValue = result - > asExplicitQuantitativeCheckResult < ValueType > ( ) . getValueMap ( ) . begin ( ) - > second ;
@ -418,8 +493,10 @@ namespace storm {
}
template < >
bool DFTModelChecker < double > : : isApproximationSufficient ( double lowerBound , double upperBound , double approximationError , bool relative ) {
STORM_LOG_THROW ( ! std : : isnan ( lowerBound ) & & ! std : : isnan ( upperBound ) , storm : : exceptions : : NotSupportedException , " Approximation does not work if result is NaN. " ) ;
bool DFTModelChecker < double > : : isApproximationSufficient ( double lowerBound , double upperBound ,
double approximationError , bool relative ) {
STORM_LOG_THROW ( ! std : : isnan ( lowerBound ) & & ! std : : isnan ( upperBound ) ,
storm : : exceptions : : NotSupportedException , " Approximation does not work if result is NaN. " ) ;
if ( relative ) {
return upperBound - lowerBound < = approximationError ;
} else {
@ -453,10 +530,14 @@ namespace storm {
}
template class DFTModelChecker < double > ;
template
class DFTModelChecker < double > ;
# ifdef STORM_HAVE_CARL
template class DFTModelChecker < storm : : RationalFunction > ;
template
class DFTModelChecker < storm : : RationalFunction > ;
# endif
}
}