Browse Source

Support for checking multiple dft properties at once

tempestpy_adaptions
Matthias Volk 8 years ago
parent
commit
cd1bd6b538
  1. 84
      src/storm-dft-cli/storm-dyftee.cpp
  2. 358
      src/storm-dft/modelchecker/dft/DFTModelChecker.cpp
  3. 38
      src/storm-dft/modelchecker/dft/DFTModelChecker.h

84
src/storm-dft-cli/storm-dyftee.cpp

@ -34,35 +34,44 @@
#include <memory> #include <memory>
/*! /*!
* Load DFT from filename, build corresponding Model and check against given property.
* Analyse the given DFT according to the given properties.
* We first load the DFT from the given file, then build the corresponding model and last check against the given properties.
* *
* @param property PCTC formula capturing the property to check.
* @param properties PCTL formulas capturing the properties to check.
* @param symred Flag whether symmetry reduction should be used. * @param symred Flag whether symmetry reduction should be used.
* @param allowModularisation Flag whether modularisation should be applied if possible. * @param allowModularisation Flag whether modularisation should be applied if possible.
* @param enableDC Flag whether Don't Care propagation should be used. * @param enableDC Flag whether Don't Care propagation should be used.
* @param approximationError Allowed approximation error.
*/ */
template <typename ValueType> template <typename ValueType>
void analyzeDFT(std::string property, bool symred, bool allowModularisation, bool enableDC, double approximationError) {
void analyzeDFT(std::vector<std::string> const& properties, bool symred, bool allowModularisation, bool enableDC, double approximationError) {
storm::settings::modules::DFTSettings const& dftSettings = storm::settings::getModule<storm::settings::modules::DFTSettings>(); storm::settings::modules::DFTSettings const& dftSettings = storm::settings::getModule<storm::settings::modules::DFTSettings>();
std::shared_ptr<storm::storage::DFT<ValueType>> dft; std::shared_ptr<storm::storage::DFT<ValueType>> dft;
// Build DFT from given file.
if (dftSettings.isDftJsonFileSet()) { if (dftSettings.isDftJsonFileSet()) {
storm::parser::DFTJsonParser<ValueType> parser; storm::parser::DFTJsonParser<ValueType> parser;
std::cout << "Running DFT analysis on file " << dftSettings.getDftJsonFilename() << " with property " << property << std::endl;
std::cout << "Loading DFT from file " << dftSettings.getDftJsonFilename() << std::endl;
dft = std::make_shared<storm::storage::DFT<ValueType>>(parser.parseJson(dftSettings.getDftJsonFilename())); dft = std::make_shared<storm::storage::DFT<ValueType>>(parser.parseJson(dftSettings.getDftJsonFilename()));
} else { } else {
storm::parser::DFTGalileoParser<ValueType> parser; storm::parser::DFTGalileoParser<ValueType> parser;
std::cout << "Running DFT analysis on file " << dftSettings.getDftFilename() << " with property " << property << std::endl;
std::cout << "Loading DFT from file " << dftSettings.getDftFilename() << std::endl;
dft = std::make_shared<storm::storage::DFT<ValueType>>(parser.parseDFT(dftSettings.getDftFilename())); dft = std::make_shared<storm::storage::DFT<ValueType>>(parser.parseDFT(dftSettings.getDftFilename()));
} }
std::vector<std::shared_ptr<storm::logic::Formula const>> formulas = storm::extractFormulasFromProperties(storm::parsePropertiesForExplicit(property));
STORM_LOG_ASSERT(formulas.size() == 1, "Wrong number of formulas.");
// Build properties
std::string propString = properties[0];
for (size_t i = 1; i < properties.size(); ++i) {
propString += ";" + properties[i];
}
std::vector<std::shared_ptr<storm::logic::Formula const>> props = storm::extractFormulasFromProperties(storm::parsePropertiesForExplicit(propString));
STORM_LOG_ASSERT(props.size() > 0, "No properties found.");
// Check model
storm::modelchecker::DFTModelChecker<ValueType> modelChecker; storm::modelchecker::DFTModelChecker<ValueType> modelChecker;
modelChecker.check(*dft, formulas[0], symred, allowModularisation, enableDC, approximationError);
modelChecker.check(*dft, props, symred, allowModularisation, enableDC, approximationError);
modelChecker.printTimings(); modelChecker.printTimings();
modelChecker.printResult();
modelChecker.printResults();
} }
/*! /*!
@ -210,43 +219,42 @@ int main(const int argc, const char** argv) {
#endif #endif
// Set min or max // Set min or max
bool minimal = true;
std::string optimizationDirection = "min";
if (dftSettings.isComputeMaximalValue()) { if (dftSettings.isComputeMaximalValue()) {
STORM_LOG_THROW(!dftSettings.isComputeMinimalValue(), storm::exceptions::InvalidSettingsException, "Cannot compute minimal and maximal values at the same time."); STORM_LOG_THROW(!dftSettings.isComputeMinimalValue(), storm::exceptions::InvalidSettingsException, "Cannot compute minimal and maximal values at the same time.");
minimal = false;
optimizationDirection = "max";
} }
// Construct pctlFormula
std::string pctlFormula = "";
std::string operatorType = "";
std::string targetFormula = "";
// Construct properties to check for
std::vector<std::string> properties;
if (generalSettings.isPropertySet()) { if (generalSettings.isPropertySet()) {
STORM_LOG_THROW(!dftSettings.usePropExpectedTime() && !dftSettings.usePropProbability() && !dftSettings.usePropTimebound(), storm::exceptions::InvalidSettingsException, "More than one property given.");
pctlFormula = generalSettings.getProperty();
} else if (dftSettings.usePropExpectedTime()) {
STORM_LOG_THROW(!dftSettings.usePropProbability() && !dftSettings.usePropTimebound(), storm::exceptions::InvalidSettingsException, "More than one property given.");
operatorType = "T";
targetFormula = "F \"failed\"";
} else if (dftSettings.usePropProbability()) {
STORM_LOG_THROW(!dftSettings.usePropTimebound(), storm::exceptions::InvalidSettingsException, "More than one property given.");
operatorType = "P";;
targetFormula = "F \"failed\"";
} else {
STORM_LOG_THROW(dftSettings.usePropTimebound(), storm::exceptions::InvalidSettingsException, "No property given.");
properties.push_back(generalSettings.getProperty());
}
if (dftSettings.usePropExpectedTime()) {
properties.push_back("T" + optimizationDirection + "=? [F \"failed\"]");
}
if (dftSettings.usePropProbability()) {
properties.push_back("P" + optimizationDirection + "=? [F \"failed\"]");
}
if (dftSettings.usePropTimebound()) {
std::stringstream stream; std::stringstream stream;
stream << "F<=" << dftSettings.getPropTimebound() << " \"failed\"";
operatorType = "P";
targetFormula = stream.str();
stream << "P" << optimizationDirection << "=? [F<=" << dftSettings.getPropTimebound() << " \"failed\"]";
properties.push_back(stream.str());
} }
if (!targetFormula.empty()) {
STORM_LOG_ASSERT(pctlFormula.empty(), "Pctl formula not empty.");
pctlFormula = operatorType + (minimal ? "min" : "max") + "=?[" + targetFormula + "]";
if (dftSettings.usePropTimepoints()) {
for (double timepoint : dftSettings.getPropTimepoints()) {
std::stringstream stream;
stream << "P" << optimizationDirection << "=? [F<=" << timepoint << " \"failed\"]";
properties.push_back(stream.str());
}
} }
STORM_LOG_ASSERT(!pctlFormula.empty(), "Pctl formula empty.");
if (properties.empty()) {
STORM_LOG_THROW(false, storm::exceptions::InvalidSettingsException, "No property given.");
}
// Set possible approximation error
double approximationError = 0.0; double approximationError = 0.0;
if (dftSettings.isApproximationErrorSet()) { if (dftSettings.isApproximationErrorSet()) {
approximationError = dftSettings.getApproximationError(); approximationError = dftSettings.getApproximationError();
@ -255,12 +263,12 @@ int main(const int argc, const char** argv) {
// From this point on we are ready to carry out the actual computations. // From this point on we are ready to carry out the actual computations.
if (parametric) { if (parametric) {
#ifdef STORM_HAVE_CARL #ifdef STORM_HAVE_CARL
analyzeDFT<storm::RationalFunction>(pctlFormula, dftSettings.useSymmetryReduction(), dftSettings.useModularisation(), !dftSettings.isDisableDC(), approximationError);
analyzeDFT<storm::RationalFunction>(properties, dftSettings.useSymmetryReduction(), dftSettings.useModularisation(), !dftSettings.isDisableDC(), approximationError);
#else #else
STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Parameters are not supported in this build."); STORM_LOG_THROW(false, storm::exceptions::NotSupportedException, "Parameters are not supported in this build.");
#endif #endif
} else { } else {
analyzeDFT<double>(pctlFormula, dftSettings.useSymmetryReduction(), dftSettings.useModularisation(), !dftSettings.isDisableDC(), approximationError);
analyzeDFT<double>(properties, dftSettings.useSymmetryReduction(), dftSettings.useModularisation(), !dftSettings.isDisableDC(), approximationError);
} }
// All operations have now been performed, so we clean up everything and terminate. // All operations have now been performed, so we clean up everything and terminate.

358
src/storm-dft/modelchecker/dft/DFTModelChecker.cpp

@ -13,12 +13,7 @@ namespace storm {
namespace modelchecker { namespace modelchecker {
template<typename ValueType> template<typename ValueType>
DFTModelChecker<ValueType>::DFTModelChecker() {
checkResult = storm::utility::zero<ValueType>();
}
template<typename ValueType>
void DFTModelChecker<ValueType>::check(storm::storage::DFT<ValueType> const& origDft, std::shared_ptr<const storm::logic::Formula> const& formula, bool symred, bool allowModularisation, bool enableDC, double approximationError) {
void DFTModelChecker<ValueType>::check(storm::storage::DFT<ValueType> const& origDft, std::vector<std::shared_ptr<const storm::logic::Formula>> const& properties, bool symred, bool allowModularisation, bool enableDC, double approximationError) {
// Initialize // Initialize
this->approximationError = approximationError; this->approximationError = approximationError;
totalTimer.start(); totalTimer.start();
@ -29,31 +24,31 @@ namespace storm {
// TODO Matthias: check that all paths reach the target state for approximation // TODO Matthias: check that all paths reach the target state for approximation
// Checking DFT // Checking DFT
if (formula->isProbabilityOperatorFormula() || !allowModularisation) {
checkResult = checkHelper(dft, formula, symred, allowModularisation, enableDC, approximationError);
} else {
std::shared_ptr<storm::models::sparse::Model<ValueType>> model = buildModelComposition(dft, formula, symred, allowModularisation, enableDC);
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, enableDC, approximationError);
// Model checking // Model checking
std::unique_ptr<storm::modelchecker::CheckResult> result = checkModel(model, formula);
result->filter(storm::modelchecker::ExplicitQualitativeCheckResult(model->getInitialStates()));
checkResult = result->asExplicitQuantitativeCheckResult<ValueType>().getValueMap().begin()->second;
std::vector<ValueType> resultsValue = checkModel(model, properties);
for (ValueType result : resultsValue) {
checkResults.push_back(result);
}
} else {
checkResults = checkHelper(dft, properties, symred, allowModularisation, enableDC, approximationError);
} }
totalTimer.stop(); totalTimer.stop();
} }
template<typename ValueType> template<typename ValueType>
typename DFTModelChecker<ValueType>::dft_result DFTModelChecker<ValueType>::checkHelper(storm::storage::DFT<ValueType> const& dft, std::shared_ptr<const storm::logic::Formula> const& formula, bool symred, bool allowModularisation, bool enableDC, double approximationError) {
typename DFTModelChecker<ValueType>::dft_results DFTModelChecker<ValueType>::checkHelper(storm::storage::DFT<ValueType> const& dft, property_vector const& properties, bool symred, bool allowModularisation, bool enableDC, double approximationError) {
STORM_LOG_TRACE("Check helper called"); STORM_LOG_TRACE("Check helper called");
bool modularisationPossible = allowModularisation;
std::vector<storm::storage::DFT<ValueType>> dfts;
bool invResults = false;
size_t nrK = 0; // K out of M
size_t nrM = 0; // K out of M
// Try modularisation // Try modularisation
if(modularisationPossible) {
bool invResults = false;
std::vector<storm::storage::DFT<ValueType>> dfts;
size_t nrK = 0; // K out of M
size_t nrM = 0; // K out of M
if(allowModularisation) {
switch (dft.topLevelType()) { switch (dft.topLevelType()) {
case storm::storage::DFTElementType::AND: case storm::storage::DFTElementType::AND:
STORM_LOG_TRACE("top modularisation called AND"); STORM_LOG_TRACE("top modularisation called AND");
@ -61,7 +56,6 @@ namespace storm {
STORM_LOG_TRACE("Modularsation into " << dfts.size() << " submodules."); STORM_LOG_TRACE("Modularsation into " << dfts.size() << " submodules.");
nrK = dfts.size(); nrK = dfts.size();
nrM = dfts.size(); nrM = dfts.size();
modularisationPossible = dfts.size() > 1;
break; break;
case storm::storage::DFTElementType::OR: case storm::storage::DFTElementType::OR:
STORM_LOG_TRACE("top modularisation called OR"); STORM_LOG_TRACE("top modularisation called OR");
@ -70,7 +64,6 @@ namespace storm {
nrK = 0; nrK = 0;
nrM = dfts.size(); nrM = dfts.size();
invResults = true; invResults = true;
modularisationPossible = dfts.size() > 1;
break; break;
case storm::storage::DFTElementType::VOT: case storm::storage::DFTElementType::VOT:
STORM_LOG_TRACE("top modularisation called VOT"); STORM_LOG_TRACE("top modularisation called VOT");
@ -82,22 +75,29 @@ namespace storm {
nrK -= 1; nrK -= 1;
invResults = true; invResults = true;
} }
modularisationPossible = dfts.size() > 1;
break; break;
default: default:
// No static gate -> no modularisation applicable // No static gate -> no modularisation applicable
modularisationPossible = false;
break; break;
} }
}
if(modularisationPossible) {
STORM_LOG_TRACE("Recursive CHECK Call");
if (formula->isProbabilityOperatorFormula()) {
// Perform modularisation
if(dfts.size() > 1) {
STORM_LOG_TRACE("Recursive CHECK Call");
// TODO Matthias: compute simultaneously
dft_results results;
for (auto property : properties) {
if (!property->isProbabilityOperatorFormula()) {
STORM_LOG_WARN("Could not check property: " << *property);
} else {
// Recursively call model checking // Recursively call model checking
std::vector<ValueType> res; std::vector<ValueType> res;
for(auto const ft : dfts) { for(auto const ft : dfts) {
dft_result ftResult = checkHelper(ft, formula, symred, true, enableDC, 0.0);
res.push_back(boost::get<ValueType>(ftResult));
// TODO Matthias: allow approximation in modularisation
dft_results ftResults = checkHelper(ft, {property}, symred, true, enableDC, 0.0);
STORM_LOG_ASSERT(ftResults.size() == 1, "Wrong number of results");
res.push_back(boost::get<ValueType>(ftResults[0]));
} }
// Combine modularisation results // Combine modularisation results
@ -128,148 +128,138 @@ namespace storm {
if(invResults) { if(invResults) {
result = storm::utility::one<ValueType>() - result; result = storm::utility::one<ValueType>() - result;
} }
return result;
results.push_back(result);
} }
} }
return results;
} else {
// No modularisation was possible
return checkDFT(dft, properties, symred, enableDC, approximationError);
} }
// If we are here, no modularisation was possible
STORM_LOG_ASSERT(!modularisationPossible, "Modularisation should not be possible.");
return checkDFT(dft, formula, symred, enableDC, approximationError);
} }
template<typename ValueType> template<typename ValueType>
std::shared_ptr<storm::models::sparse::Ctmc<ValueType>> DFTModelChecker<ValueType>::buildModelComposition(storm::storage::DFT<ValueType> const& dft, std::shared_ptr<const storm::logic::Formula> const& formula, bool symred, bool allowModularisation, bool enableDC) {
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, bool enableDC, double approximationError) {
// TODO Matthias: use approximation?
STORM_LOG_TRACE("Build model via composition"); STORM_LOG_TRACE("Build model via composition");
// Use parallel composition for CTMCs for expected time
STORM_LOG_ASSERT(formula->isTimeOperatorFormula(), "Formula is not a time operator formula");
bool modularisationPossible = allowModularisation;
std::vector<storm::storage::DFT<ValueType>> dfts;
bool isAnd = true;
// Try modularisation // Try modularisation
if(modularisationPossible) {
std::vector<storm::storage::DFT<ValueType>> dfts;
bool isAnd = true;
if(allowModularisation) {
switch (dft.topLevelType()) { switch (dft.topLevelType()) {
case storm::storage::DFTElementType::AND: case storm::storage::DFTElementType::AND:
STORM_LOG_TRACE("top modularisation called AND"); STORM_LOG_TRACE("top modularisation called AND");
dfts = dft.topModularisation(); dfts = dft.topModularisation();
STORM_LOG_TRACE("Modularisation into " << dfts.size() << " submodules."); STORM_LOG_TRACE("Modularisation into " << dfts.size() << " submodules.");
modularisationPossible = dfts.size() > 1;
isAnd = true; isAnd = true;
break; break;
case storm::storage::DFTElementType::OR: case storm::storage::DFTElementType::OR:
STORM_LOG_TRACE("top modularisation called OR"); STORM_LOG_TRACE("top modularisation called OR");
dfts = dft.topModularisation(); dfts = dft.topModularisation();
STORM_LOG_TRACE("Modularsation into " << dfts.size() << " submodules."); STORM_LOG_TRACE("Modularsation into " << dfts.size() << " submodules.");
modularisationPossible = dfts.size() > 1;
isAnd = false; isAnd = false;
break; break;
case storm::storage::DFTElementType::VOT: case storm::storage::DFTElementType::VOT:
/*STORM_LOG_TRACE("top modularisation called VOT");
dfts = dft.topModularisation();
STORM_LOG_TRACE("Modularsation into " << dfts.size() << " submodules.");
std::static_pointer_cast<storm::storage::DFTVot<ValueType> const>(dft.getTopLevelGate())->threshold();
*/
// TODO enable modularisation for voting gate // TODO enable modularisation for voting gate
modularisationPossible = false;
break; break;
default: default:
// No static gate -> no modularisation applicable // No static gate -> no modularisation applicable
modularisationPossible = false;
break; break;
} }
}
if(modularisationPossible) {
STORM_LOG_TRACE("Recursive CHECK Call");
bool firstTime = true;
std::shared_ptr<storm::models::sparse::Ctmc<ValueType>> composedModel;
for (auto const ft : dfts) {
STORM_LOG_INFO("Building Model via parallel composition...");
explorationTimer.start();
// Perform modularisation via parallel composition
if(dfts.size() > 1) {
STORM_LOG_TRACE("Recursive CHECK Call");
bool firstTime = true;
std::shared_ptr<storm::models::sparse::Ctmc<ValueType>> composedModel;
for (auto const ft : dfts) {
STORM_LOG_INFO("Building Model via parallel composition...");
explorationTimer.start();
// Find symmetries
std::map<size_t, std::vector<std::vector<size_t>>> emptySymmetry;
storm::storage::DFTIndependentSymmetries symmetries(emptySymmetry);
if(symred) {
auto colouring = ft.colourDFT();
symmetries = ft.findSymmetries(colouring);
STORM_LOG_INFO("Found " << symmetries.groups.size() << " symmetries.");
STORM_LOG_TRACE("Symmetries: " << std::endl << symmetries);
}
// Find symmetries
std::map<size_t, std::vector<std::vector<size_t>>> emptySymmetry;
storm::storage::DFTIndependentSymmetries symmetries(emptySymmetry);
if(symred) {
auto colouring = ft.colourDFT();
symmetries = ft.findSymmetries(colouring);
STORM_LOG_INFO("Found " << symmetries.groups.size() << " symmetries.");
STORM_LOG_TRACE("Symmetries: " << std::endl << symmetries);
}
// Build a single CTMC
STORM_LOG_INFO("Building Model...");
storm::builder::ExplicitDFTModelBuilderApprox<ValueType> builder(ft, symmetries, enableDC);
typename storm::builder::ExplicitDFTModelBuilderApprox<ValueType>::LabelOptions labeloptions; // TODO initialize this with the formula
builder.buildModel(labeloptions, 0, 0.0);
std::shared_ptr<storm::models::sparse::Model<ValueType>> model = builder.getModel();
//model->printModelInformationToStream(std::cout);
STORM_LOG_INFO("No. states (Explored): " << model->getNumberOfStates());
STORM_LOG_INFO("No. transitions (Explored): " << model->getNumberOfTransitions());
explorationTimer.stop();
// Build a single CTMC
STORM_LOG_INFO("Building Model...");
storm::builder::ExplicitDFTModelBuilderApprox<ValueType> builder(ft, symmetries, enableDC);
typename storm::builder::ExplicitDFTModelBuilderApprox<ValueType>::LabelOptions labeloptions; // TODO initialize this with the formula
builder.buildModel(labeloptions, 0, 0.0);
std::shared_ptr<storm::models::sparse::Model<ValueType>> model = builder.getModel();
//model->printModelInformationToStream(std::cout);
STORM_LOG_INFO("No. states (Explored): " << model->getNumberOfStates());
STORM_LOG_INFO("No. transitions (Explored): " << model->getNumberOfTransitions());
explorationTimer.stop();
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>>();
ctmc = storm::performDeterministicSparseBisimulationMinimization<storm::models::sparse::Ctmc<ValueType>>(ctmc, {formula}, storm::storage::BisimulationType::Weak)->template as<storm::models::sparse::Ctmc<ValueType>>();
if (firstTime) {
composedModel = ctmc;
firstTime = false;
} else {
composedModel = storm::builder::ParallelCompositionBuilder<ValueType>::compose(composedModel, ctmc, isAnd);
}
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
bisimulationTimer.start();
composedModel = storm::performDeterministicSparseBisimulationMinimization<storm::models::sparse::Ctmc<ValueType>>(composedModel, {formula}, storm::storage::BisimulationType::Weak)->template as<storm::models::sparse::Ctmc<ValueType>>();
bisimulationTimer.stop();
STORM_LOG_INFO("No. states (Composed): " << composedModel->getNumberOfStates());
STORM_LOG_INFO("No. transitions (Composed): " << composedModel->getNumberOfTransitions());
if (composedModel->getNumberOfStates() <= 15) {
STORM_LOG_TRACE("Transition matrix: " << std::endl << composedModel->getTransitionMatrix());
} else {
STORM_LOG_TRACE("Transition matrix: too big to print");
}
// Apply bisimulation to new CTMC
bisimulationTimer.start();
ctmc = storm::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);
} }
return composedModel;
}
}
// If we are here, no composition was possible
STORM_LOG_ASSERT(!modularisationPossible, "Modularisation should not be possible.");
explorationTimer.start();
// Find symmetries
std::map<size_t, std::vector<std::vector<size_t>>> emptySymmetry;
storm::storage::DFTIndependentSymmetries symmetries(emptySymmetry);
if(symred) {
auto colouring = dft.colourDFT();
symmetries = dft.findSymmetries(colouring);
STORM_LOG_INFO("Found " << symmetries.groups.size() << " symmetries.");
STORM_LOG_TRACE("Symmetries: " << std::endl << symmetries);
}
// Build a single CTMC
STORM_LOG_INFO("Building Model...");
// Apply bisimulation to parallel composition
bisimulationTimer.start();
composedModel = storm::performDeterministicSparseBisimulationMinimization<storm::models::sparse::Ctmc<ValueType>>(composedModel, properties, storm::storage::BisimulationType::Weak)->template as<storm::models::sparse::Ctmc<ValueType>>();
bisimulationTimer.stop();
STORM_LOG_INFO("No. states (Composed): " << composedModel->getNumberOfStates());
STORM_LOG_INFO("No. transitions (Composed): " << composedModel->getNumberOfTransitions());
if (composedModel->getNumberOfStates() <= 15) {
STORM_LOG_TRACE("Transition matrix: " << std::endl << composedModel->getTransitionMatrix());
} else {
STORM_LOG_TRACE("Transition matrix: too big to print");
}
}
return composedModel;
} else {
// No composition was possible
explorationTimer.start();
// Find symmetries
std::map<size_t, std::vector<std::vector<size_t>>> emptySymmetry;
storm::storage::DFTIndependentSymmetries symmetries(emptySymmetry);
if(symred) {
auto colouring = dft.colourDFT();
symmetries = dft.findSymmetries(colouring);
STORM_LOG_INFO("Found " << symmetries.groups.size() << " symmetries.");
STORM_LOG_TRACE("Symmetries: " << std::endl << symmetries);
}
// Build a single CTMC
STORM_LOG_INFO("Building Model...");
storm::builder::ExplicitDFTModelBuilderApprox<ValueType> builder(dft, symmetries, enableDC);
typename storm::builder::ExplicitDFTModelBuilderApprox<ValueType>::LabelOptions labeloptions; // TODO initialize this with the formula
builder.buildModel(labeloptions, 0, 0.0);
std::shared_ptr<storm::models::sparse::Model<ValueType>> model = builder.getModel();
//model->printModelInformationToStream(std::cout);
STORM_LOG_INFO("No. states (Explored): " << model->getNumberOfStates());
STORM_LOG_INFO("No. transitions (Explored): " << model->getNumberOfTransitions());
explorationTimer.stop();
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>>();
storm::builder::ExplicitDFTModelBuilderApprox<ValueType> builder(dft, symmetries, enableDC);
typename storm::builder::ExplicitDFTModelBuilderApprox<ValueType>::LabelOptions labeloptions; // TODO initialize this with the formula
builder.buildModel(labeloptions, 0, 0.0);
std::shared_ptr<storm::models::sparse::Model<ValueType>> model = builder.getModel();
//model->printModelInformationToStream(std::cout);
STORM_LOG_INFO("No. states (Explored): " << model->getNumberOfStates());
STORM_LOG_INFO("No. transitions (Explored): " << model->getNumberOfTransitions());
explorationTimer.stop();
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> template<typename ValueType>
typename DFTModelChecker<ValueType>::dft_result DFTModelChecker<ValueType>::checkDFT(storm::storage::DFT<ValueType> const& dft, std::shared_ptr<const storm::logic::Formula> const& formula, bool symred, bool enableDC, double approximationError) {
typename DFTModelChecker<ValueType>::dft_results DFTModelChecker<ValueType>::checkDFT(storm::storage::DFT<ValueType> const& dft, property_vector const& properties, bool symred, bool enableDC, double approximationError) {
explorationTimer.start(); explorationTimer.start();
// Find symmetries // Find symmetries
@ -288,11 +278,18 @@ namespace storm {
// Build approximate Markov Automata for lower and upper bound // 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::shared_ptr<storm::models::sparse::Model<ValueType>> model;
std::vector<ValueType> newResult;
storm::builder::ExplicitDFTModelBuilderApprox<ValueType> builder(dft, symmetries, enableDC); storm::builder::ExplicitDFTModelBuilderApprox<ValueType> builder(dft, symmetries, enableDC);
typename storm::builder::ExplicitDFTModelBuilderApprox<ValueType>::LabelOptions labeloptions; // TODO initialize this with the formula typename storm::builder::ExplicitDFTModelBuilderApprox<ValueType>::LabelOptions labeloptions; // TODO initialize this with the formula
bool probabilityFormula = formula->isProbabilityOperatorFormula();
STORM_LOG_ASSERT((formula->isTimeOperatorFormula() && !probabilityFormula) || (!formula->isTimeOperatorFormula() && probabilityFormula), "Probability formula not initialized correctly");
// TODO Matthias: compute approximation for all properties simultaneously?
std::shared_ptr<const storm::logic::Formula> property = properties[0];
if (properties.size() > 1) {
STORM_LOG_WARN("Computing approximation only for first property: " << *property);
}
bool probabilityFormula = property->isProbabilityOperatorFormula();
STORM_LOG_ASSERT((property->isTimeOperatorFormula() && !probabilityFormula) || (!property->isTimeOperatorFormula() && probabilityFormula), "Probability formula not initialized correctly");
size_t iteration = 0; size_t iteration = 0;
do { do {
// Iteratively build finer models // Iteratively build finer models
@ -315,12 +312,11 @@ namespace storm {
STORM_LOG_INFO("No. transitions: " << model->getNumberOfTransitions()); STORM_LOG_INFO("No. transitions: " << model->getNumberOfTransitions());
buildingTimer.stop(); buildingTimer.stop();
// Check lower bound
std::unique_ptr<storm::modelchecker::CheckResult> result = checkModel(model, formula);
result->filter(storm::modelchecker::ExplicitQualitativeCheckResult(model->getInitialStates()));
ValueType newResult = result->asExplicitQuantitativeCheckResult<ValueType>().getValueMap().begin()->second;
STORM_LOG_ASSERT(iteration == 0 || !comparator.isLess(newResult, approxResult.first), "New under-approximation " << newResult << " is smaller than old result " << approxResult.first);
approxResult.first = newResult;
// 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);
approxResult.first = newResult[0];
// Build model for upper bound // Build model for upper bound
STORM_LOG_INFO("Getting model for upper bound..."); STORM_LOG_INFO("Getting model for upper bound...");
@ -328,11 +324,10 @@ namespace storm {
model = builder.getModelApproximation(probabilityFormula ? true : false); model = builder.getModelApproximation(probabilityFormula ? true : false);
buildingTimer.stop(); buildingTimer.stop();
// Check upper bound // Check upper bound
result = checkModel(model, formula);
result->filter(storm::modelchecker::ExplicitQualitativeCheckResult(model->getInitialStates()));
newResult = result->asExplicitQuantitativeCheckResult<ValueType>().getValueMap().begin()->second;
STORM_LOG_ASSERT(iteration == 0 || !comparator.isLess(approxResult.second, newResult), "New over-approximation " << newResult << " is greater than old result " << approxResult.second);
approxResult.second = newResult;
newResult = checkModel(model, {properties});
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);
approxResult.second = newResult[0];
++iteration; ++iteration;
STORM_LOG_INFO("Result after iteration " << iteration << ": (" << std::setprecision(10) << approxResult.first << ", " << approxResult.second << ")"); STORM_LOG_INFO("Result after iteration " << iteration << ": (" << std::setprecision(10) << approxResult.first << ", " << approxResult.second << ")");
@ -343,7 +338,9 @@ namespace storm {
} while (!isApproximationSufficient(approxResult.first, approxResult.second, approximationError, probabilityFormula)); } while (!isApproximationSufficient(approxResult.first, approxResult.second, approximationError, probabilityFormula));
STORM_LOG_INFO("Finished approximation after " << iteration << " iteration" << (iteration > 1 ? "s." : ".")); STORM_LOG_INFO("Finished approximation after " << iteration << " iteration" << (iteration > 1 ? "s." : "."));
return approxResult;
dft_results results;
results.push_back(approxResult);
return results;
} else { } else {
// Build a single Markov Automaton // Build a single Markov Automaton
STORM_LOG_INFO("Building Model..."); STORM_LOG_INFO("Building Model...");
@ -365,32 +362,50 @@ namespace storm {
explorationTimer.stop(); explorationTimer.stop();
// Model checking // Model checking
std::unique_ptr<storm::modelchecker::CheckResult> result = checkModel(model, formula);
result->filter(storm::modelchecker::ExplicitQualitativeCheckResult(model->getInitialStates()));
return result->asExplicitQuantitativeCheckResult<ValueType>().getValueMap().begin()->second;
std::vector<ValueType> resultsValue = checkModel(model, properties);
dft_results results;
for (ValueType result : resultsValue) {
results.push_back(result);
}
return results;
} }
} }
template<typename ValueType> template<typename ValueType>
std::unique_ptr<storm::modelchecker::CheckResult> DFTModelChecker<ValueType>::checkModel(std::shared_ptr<storm::models::sparse::Model<ValueType>>& model, std::shared_ptr<const storm::logic::Formula> const& formula) {
std::vector<ValueType> DFTModelChecker<ValueType>::checkModel(std::shared_ptr<storm::models::sparse::Model<ValueType>>& model, property_vector const& properties) {
// Bisimulation // Bisimulation
bisimulationTimer.start();
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_INFO("Bisimulation..."); STORM_LOG_INFO("Bisimulation...");
model = storm::performDeterministicSparseBisimulationMinimization<storm::models::sparse::Ctmc<ValueType>>(model->template as<storm::models::sparse::Ctmc<ValueType>>(), {formula}, storm::storage::BisimulationType::Weak)->template as<storm::models::sparse::Ctmc<ValueType>>();
model = storm::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_INFO("No. states (Bisimulation): " << model->getNumberOfStates()); STORM_LOG_INFO("No. states (Bisimulation): " << model->getNumberOfStates());
STORM_LOG_INFO("No. transitions (Bisimulation): " << model->getNumberOfTransitions()); STORM_LOG_INFO("No. transitions (Bisimulation): " << model->getNumberOfTransitions());
bisimulationTimer.stop();
} }
bisimulationTimer.stop();
modelCheckingTimer.start();
// Check the model // Check the model
STORM_LOG_INFO("Model checking..."); STORM_LOG_INFO("Model checking...");
std::unique_ptr<storm::modelchecker::CheckResult> result(storm::verifySparseModel(model, formula));
STORM_LOG_INFO("Model checking done.");
STORM_LOG_ASSERT(result, "Result does not exist.");
modelCheckingTimer.start();
std::vector<ValueType> results;
// Check each property
storm::utility::Stopwatch singleModelCheckingTimer;
for (auto property : properties) {
singleModelCheckingTimer.reset();
singleModelCheckingTimer.start();
STORM_PRINT_AND_LOG("Model checking property " << *property << " ..." << std::endl);
std::unique_ptr<storm::modelchecker::CheckResult> result(storm::verifySparseModel(model, 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;
STORM_PRINT_AND_LOG("Result (initial states): " << resultValue << std::endl);
results.push_back(resultValue);
singleModelCheckingTimer.stop();
STORM_PRINT_AND_LOG("Time for model checking: " << singleModelCheckingTimer << "." << std::endl);
}
modelCheckingTimer.stop(); modelCheckingTimer.stop();
return result;
STORM_LOG_INFO("Model checking done.");
return results;
} }
template<typename ValueType> template<typename ValueType>
@ -411,21 +426,30 @@ namespace storm {
template<typename ValueType> template<typename ValueType>
void DFTModelChecker<ValueType>::printTimings(std::ostream& os) { void DFTModelChecker<ValueType>::printTimings(std::ostream& os) {
os << "Times:" << std::endl; os << "Times:" << std::endl;
os << "Exploration:\t" << explorationTimer.getTimeInMilliseconds() / 1000.0 << "s" << std::endl;
os << "Building:\t" << buildingTimer.getTimeInMilliseconds() / 1000.0 << "s" << std::endl;
os << "Bisimulation:\t" << bisimulationTimer.getTimeInMilliseconds() / 1000.0 << "s" << std::endl;
os << "Modelchecking:\t" << modelCheckingTimer.getTimeInMilliseconds() / 1000.0 << "s" << std::endl;
os << "Total:\t\t" << totalTimer.getTimeInMilliseconds() / 1000.0 << "s" << std::endl;
os << "Exploration:\t" << explorationTimer << "s" << std::endl;
os << "Building:\t" << buildingTimer << "s" << std::endl;
os << "Bisimulation:\t" << bisimulationTimer<< "s" << std::endl;
os << "Modelchecking:\t" << modelCheckingTimer << "s" << std::endl;
os << "Total:\t\t" << totalTimer << "s" << std::endl;
} }
template<typename ValueType> template<typename ValueType>
void DFTModelChecker<ValueType>::printResult(std::ostream& os) {
void DFTModelChecker<ValueType>::printResults(std::ostream& os) {
bool first = true;
os << "Result: ["; os << "Result: [";
if (this->approximationError > 0.0) {
approximation_result result = boost::get<approximation_result>(checkResult);
os << "(" << result.first << ", " << result.second << ")";
} else {
os << boost::get<ValueType>(checkResult);
for (auto result : checkResults) {
if (!first) {
os << ", ";
}
if (this->approximationError > 0.0) {
approximation_result resultApprox = boost::get<approximation_result>(result);
os << "(" << resultApprox.first << ", " << resultApprox.second << ")";
} else {
os << boost::get<ValueType>(result);
}
if (first) {
first = false;
}
} }
os << "]" << std::endl; os << "]" << std::endl;
} }

38
src/storm-dft/modelchecker/dft/DFTModelChecker.h

@ -18,26 +18,28 @@ namespace storm {
class DFTModelChecker { class DFTModelChecker {
typedef std::pair<ValueType, ValueType> approximation_result; typedef std::pair<ValueType, ValueType> approximation_result;
typedef boost::variant<ValueType, approximation_result> dft_result;
typedef std::vector<boost::variant<ValueType, approximation_result>> dft_results;
typedef std::vector<std::shared_ptr<const storm::logic::Formula>> property_vector;
public: public:
/*! /*!
* Constructor. * Constructor.
*/ */
DFTModelChecker();
DFTModelChecker() {
}
/*! /*!
* Main method for checking DFTs. * Main method for checking DFTs.
* *
* @param origDft Original DFT * @param origDft Original DFT
* @param formula Formula to check for
* @param properties Properties to check for
* @param symred Flag indicating if symmetry reduction should be used * @param symred Flag indicating if symmetry reduction should be used
* @param allowModularisation Flag indication if modularisation is allowed * @param allowModularisation Flag indication if modularisation is allowed
* @param enableDC Flag indicating if dont care propagation should be used * @param enableDC Flag indicating if dont care propagation should be used
* @param approximationError Error allowed for approximation. Value 0 indicates no approximation * @param approximationError Error allowed for approximation. Value 0 indicates no approximation
*/ */
void check(storm::storage::DFT<ValueType> const& origDft, std::shared_ptr<const storm::logic::Formula> const& formula, bool symred = true, bool allowModularisation = true, bool enableDC = true, double approximationError = 0.0);
void check(storm::storage::DFT<ValueType> const& origDft, property_vector const& properties, bool symred = true, bool allowModularisation = true, bool enableDC = true, double approximationError = 0.0);
/*! /*!
* Print timings of all operations to stream. * Print timings of all operations to stream.
@ -51,7 +53,7 @@ namespace storm {
* *
* @param os Output stream to write to. * @param os Output stream to write to.
*/ */
void printResult(std::ostream& os = std::cout);
void printResults(std::ostream& os = std::cout);
private: private:
@ -62,8 +64,8 @@ namespace storm {
storm::utility::Stopwatch modelCheckingTimer; storm::utility::Stopwatch modelCheckingTimer;
storm::utility::Stopwatch totalTimer; storm::utility::Stopwatch totalTimer;
// Model checking result
dft_result checkResult;
// Model checking results
dft_results checkResults;
// Allowed error bound for approximation // Allowed error bound for approximation
double approximationError; double approximationError;
@ -72,21 +74,21 @@ namespace storm {
* Internal helper for model checking a DFT. * Internal helper for model checking a DFT.
* *
* @param dft DFT * @param dft DFT
* @param formula Formula to check for
* @param properties Properties to check for
* @param symred Flag indicating if symmetry reduction should be used * @param symred Flag indicating if symmetry reduction should be used
* @param allowModularisation Flag indication if modularisation is allowed * @param allowModularisation Flag indication if modularisation is allowed
* @param enableDC Flag indicating if dont care propagation should be used * @param enableDC Flag indicating if dont care propagation should be used
* @param approximationError Error allowed for approximation. Value 0 indicates no approximation * @param approximationError Error allowed for approximation. Value 0 indicates no approximation
* *
* @return Model checking result (or in case of approximation two results for lower and upper bound)
* @return Model checking results (or in case of approximation two results for lower and upper bound)
*/ */
dft_result checkHelper(storm::storage::DFT<ValueType> const& dft, std::shared_ptr<const storm::logic::Formula> const& formula, bool symred, bool allowModularisation, bool enableDC, double approximationError);
dft_results checkHelper(storm::storage::DFT<ValueType> const& dft, property_vector const& properties, bool symred, bool allowModularisation, bool enableDC, double approximationError);
/*! /*!
* Internal helper for building a CTMC from a DFT via parallel composition. * Internal helper for building a CTMC from a DFT via parallel composition.
* *
* @param dft DFT * @param dft DFT
* @param formula Formula to check for
* @param properties Properties to check for
* @param symred Flag indicating if symmetry reduction should be used * @param symred Flag indicating if symmetry reduction should be used
* @param allowModularisation Flag indication if modularisation is allowed * @param allowModularisation Flag indication if modularisation is allowed
* @param enableDC Flag indicating if dont care propagation should be used * @param enableDC Flag indicating if dont care propagation should be used
@ -94,30 +96,30 @@ namespace storm {
* *
* @return CTMC representing the DFT * @return CTMC representing the DFT
*/ */
std::shared_ptr<storm::models::sparse::Ctmc<ValueType>> buildModelComposition(storm::storage::DFT<ValueType> const& dft, std::shared_ptr<const storm::logic::Formula> const& formula, bool symred, bool allowModularisation, bool enableDC);
std::shared_ptr<storm::models::sparse::Ctmc<ValueType>> buildModelViaComposition(storm::storage::DFT<ValueType> const& dft, property_vector const& properties, bool symred, bool allowModularisation, bool enableDC, double approximationError);
/*! /*!
* Check model generated from DFT. * Check model generated from DFT.
* *
* @param dft The DFT * @param dft The DFT
* @param formula Formula to check for
* @param properties Properties to check for
* @param symred Flag indicating if symmetry reduction should be used * @param symred Flag indicating if symmetry reduction should be used
* @param enableDC Flag indicating if dont care propagation should be used * @param enableDC Flag indicating if dont care propagation should be used
* @param approximationError Error allowed for approximation. Value 0 indicates no approximation * @param approximationError Error allowed for approximation. Value 0 indicates no approximation
* *
* @return Model checking result * @return Model checking result
*/ */
dft_result checkDFT(storm::storage::DFT<ValueType> const& dft, std::shared_ptr<const storm::logic::Formula> const& formula, bool symred, bool enableDC, double approximationError = 0.0);
dft_results checkDFT(storm::storage::DFT<ValueType> const& dft, property_vector const& properties, bool symred, bool enableDC, double approximationError = 0.0);
/*! /*!
* Check the given markov model for the given property.
* Check the given markov model for the given properties.
* *
* @param model Model to check
* @param formula Formula to check for
* @param model Model to check
* @param properties Properties to check for
* *
* @return Model checking result * @return Model checking result
*/ */
std::unique_ptr<storm::modelchecker::CheckResult> checkModel(std::shared_ptr<storm::models::sparse::Model<ValueType>>& model, std::shared_ptr<const storm::logic::Formula> const& formula);
std::vector<ValueType> checkModel(std::shared_ptr<storm::models::sparse::Model<ValueType>>& model, property_vector const& properties);
/*! /*!
* Checks if the computed approximation is sufficient, i.e. * Checks if the computed approximation is sufficient, i.e.

Loading…
Cancel
Save