Browse Source

First version of approximation model (with mdp intead of s2pg)

Former-commit-id: 86fdbc5f36
tempestpy_adaptions
TimQu 9 years ago
parent
commit
f72c30cdff
  1. 102
      src/modelchecker/region/AbstractSparseRegionModelChecker.cpp
  2. 4
      src/modelchecker/region/AbstractSparseRegionModelChecker.h
  3. 104
      src/modelchecker/region/ApproximationModel.cpp
  4. 24
      src/modelchecker/region/ApproximationModel.h
  5. 9
      src/modelchecker/region/SamplingModel.cpp
  6. 95
      src/modelchecker/region/SparseDtmcRegionModelChecker.cpp
  7. 11
      src/modelchecker/region/SparseDtmcRegionModelChecker.h
  8. 96
      src/modelchecker/region/SparseMdpRegionModelChecker.cpp
  9. 11
      src/modelchecker/region/SparseMdpRegionModelChecker.h

102
src/modelchecker/region/AbstractSparseRegionModelChecker.cpp

@ -81,9 +81,6 @@ namespace storm {
return this->simpleFormula;
}
template<typename ParametricSparseModelType, typename ConstantType>
void AbstractSparseRegionModelChecker<ParametricSparseModelType, ConstantType>::specifyFormula(std::shared_ptr<storm::logic::Formula> formula) {
std::chrono::high_resolution_clock::time_point timeSpecifyFormulaStart = std::chrono::high_resolution_clock::now();
@ -153,7 +150,7 @@ namespace storm {
template<typename ParametricSparseModelType, typename ConstantType>
void AbstractSparseRegionModelChecker<ParametricSparseModelType, ConstantType>::initializeApproximationModel(ParametricSparseModelType const& model, std::shared_ptr<storm::logic::OperatorFormula> formula) {
std::chrono::high_resolution_clock::time_point timeInitApproxModelStart = std::chrono::high_resolution_clock::now();
STORM_LOG_DEBUG("The Approximation Model is initialized");
STORM_LOG_DEBUG("Initializing the Approximation Model...");
STORM_LOG_THROW(this->isApproximationApplicable, storm::exceptions::UnexpectedException, "Approximation model requested but approximation is not applicable");
this->approximationModel=std::make_shared<ApproximationModel<ParametricSparseModelType, ConstantType>>(model, formula);
std::chrono::high_resolution_clock::time_point timeInitApproxModelEnd = std::chrono::high_resolution_clock::now();
@ -163,7 +160,7 @@ namespace storm {
template<typename ParametricSparseModelType, typename ConstantType>
void AbstractSparseRegionModelChecker<ParametricSparseModelType, ConstantType>::initializeSamplingModel(ParametricSparseModelType const& model, std::shared_ptr<storm::logic::OperatorFormula> formula) {
STORM_LOG_DEBUG("The Sampling Model is initialized");
STORM_LOG_DEBUG("Initializing the Sampling Model....");
std::chrono::high_resolution_clock::time_point timeInitSamplingModelStart = std::chrono::high_resolution_clock::now();
this->samplingModel=std::make_shared<SamplingModel<ParametricSparseModelType, ConstantType>>(model, formula);
std::chrono::high_resolution_clock::time_point timeInitSamplingModelEnd = std::chrono::high_resolution_clock::now();
@ -272,7 +269,102 @@ namespace storm {
break;
}
}
template<typename ParametricSparseModelType, typename ConstantType>
bool AbstractSparseRegionModelChecker<ParametricSparseModelType, ConstantType>::checkApproximativeValues(ParameterRegion<ParametricType>& region, std::vector<ConstantType>& lowerBounds, std::vector<ConstantType>& upperBounds) {
std::chrono::high_resolution_clock::time_point timeMDPBuildStart = std::chrono::high_resolution_clock::now();
this->getApproximationModel()->instantiate(region);
std::chrono::high_resolution_clock::time_point timeMDPBuildEnd = std::chrono::high_resolution_clock::now();
this->timeApproxModelInstantiation += timeMDPBuildEnd-timeMDPBuildStart;
// Decide whether to prove allsat or allviolated.
bool proveAllSat;
switch (region.getCheckResult()){
case RegionCheckResult::UNKNOWN:
switch(storm::settings::regionSettings().getApproxMode()){
case storm::settings::modules::RegionSettings::ApproxMode::TESTFIRST:
//Sample a single point to know whether we should try to prove ALLSAT or ALLVIOLATED
checkPoint(region,region.getSomePoint(), false);
proveAllSat= (region.getCheckResult()==RegionCheckResult::EXISTSSAT);
break;
case storm::settings::modules::RegionSettings::ApproxMode::GUESSALLSAT:
proveAllSat=true;
break;
case storm::settings::modules::RegionSettings::ApproxMode::GUESSALLVIOLATED:
proveAllSat=false;
break;
default:
STORM_LOG_THROW(false, storm::exceptions::InvalidSettingsException, "The specified approxmode is not supported");
}
break;
case RegionCheckResult::ALLSAT:
STORM_LOG_WARN("The checkresult of the current region should not be conclusive (ALLSAT)");
//Intentionally no break;
case RegionCheckResult::EXISTSSAT:
proveAllSat=true;
break;
case RegionCheckResult::ALLVIOLATED:
STORM_LOG_WARN("The checkresult of the current region should not be conclusive (ALLViolated)");
//Intentionally no break;
case RegionCheckResult::EXISTSVIOLATED:
proveAllSat=false;
break;
default:
STORM_LOG_WARN("The checkresult of the current region should not be conclusive, i.e. it should be either EXISTSSAT or EXISTSVIOLATED or UNKNOWN in order to apply approximative values");
proveAllSat=true;
}
bool formulaSatisfied;
if((this->specifiedFormulaHasUpperBound() && proveAllSat) || (!this->specifiedFormulaHasUpperBound() && !proveAllSat)){
//these are the cases in which we need to compute upper bounds
upperBounds = this->getApproximationModel()->computeValues(storm::solver::OptimizationDirection::Maximize);
lowerBounds = std::vector<ConstantType>();
formulaSatisfied = this->valueIsInBoundOfFormula(upperBounds[*this->getApproximationModel()->getModel()->getInitialStates().begin()]);
}
else{
//for the remaining cases we compute lower bounds
lowerBounds = this->getApproximationModel()->computeValues(storm::solver::OptimizationDirection::Minimize);
upperBounds = std::vector<ConstantType>();
formulaSatisfied = this->valueIsInBoundOfFormula(lowerBounds[*this->getApproximationModel()->getModel()->getInitialStates().begin()]);
}
//check if approximation was conclusive
if(proveAllSat && formulaSatisfied){
region.setCheckResult(RegionCheckResult::ALLSAT);
return true;
}
if(!proveAllSat && !formulaSatisfied){
region.setCheckResult(RegionCheckResult::ALLVIOLATED);
return true;
}
if(region.getCheckResult()==RegionCheckResult::UNKNOWN){
//In this case, it makes sense to try to prove the contrary statement
proveAllSat=!proveAllSat;
if(lowerBounds.empty()){
lowerBounds = this->getApproximationModel()->computeValues(storm::solver::OptimizationDirection::Minimize);
formulaSatisfied=this->valueIsInBoundOfFormula(lowerBounds[*this->getApproximationModel()->getModel()->getInitialStates().begin()]);
}
else{
upperBounds = this->getApproximationModel()->computeValues(storm::solver::OptimizationDirection::Maximize);
formulaSatisfied=this->valueIsInBoundOfFormula(upperBounds[*this->getApproximationModel()->getModel()->getInitialStates().begin()]);
}
//check if approximation was conclusive
if(proveAllSat && formulaSatisfied){
region.setCheckResult(RegionCheckResult::ALLSAT);
return true;
}
if(!proveAllSat && !formulaSatisfied){
region.setCheckResult(RegionCheckResult::ALLVIOLATED);
return true;
}
}
//if we reach this point than the result is still inconclusive.
return false;
}
template<typename ParametricSparseModelType, typename ConstantType>
std::shared_ptr<ApproximationModel<ParametricSparseModelType, ConstantType>> const& AbstractSparseRegionModelChecker<ParametricSparseModelType, ConstantType>::getApproximationModel() {
if(this->approximationModel==nullptr){

4
src/modelchecker/region/AbstractSparseRegionModelChecker.h

@ -130,7 +130,7 @@ namespace storm {
* However, if only the lowerBounds (or upperBounds) have been computed, the other vector is set to a vector of size 0.
* True is returned iff either ALLSAT or ALLVIOLATED could be proved.
*/
virtual bool checkApproximativeValues(ParameterRegion<ParametricType>& region, std::vector<ConstantType>& lowerBounds, std::vector<ConstantType>& upperBounds)=0;
bool checkApproximativeValues(ParameterRegion<ParametricType>& region, std::vector<ConstantType>& lowerBounds, std::vector<ConstantType>& upperBounds);
/*!
* Returns the approximation model.
@ -223,10 +223,10 @@ namespace storm {
std::chrono::high_resolution_clock::duration timeCheckRegion;
std::chrono::high_resolution_clock::duration timeSampling;
std::chrono::high_resolution_clock::duration timeApproximation;
std::chrono::high_resolution_clock::duration timeApproxModelInstantiation;
std::chrono::high_resolution_clock::duration timeSmt;
protected:
std::chrono::high_resolution_clock::duration timeComputeReachabilityFunction;
std::chrono::high_resolution_clock::duration timeApproxModelInstantiation;
};
} //namespace region
} //namespace modelchecker

104
src/modelchecker/region/ApproximationModel.cpp

@ -4,9 +4,12 @@
*
* Created on August 7, 2015, 9:29 AM
*/
#include "src/modelchecker/region/ApproximationModel.h"
#include "src/models/sparse/Dtmc.h"
#include "src/models/sparse/Mdp.h"
#include "src/models/ModelType.h"
#include "src/models/sparse/StandardRewardModel.h"
#include "src/modelchecker/region/ApproximationModel.h"
#include "src/modelchecker/prctl/SparseMdpPrctlModelChecker.h"
#include "src/modelchecker/results/ExplicitQuantitativeCheckResult.h"
#include "src/utility/macros.h"
@ -14,6 +17,7 @@
#include "src/utility/vector.h"
#include "src/exceptions/UnexpectedException.h"
#include "src/exceptions/InvalidArgumentException.h"
#include "exceptions/NotImplementedException.h"
namespace storm {
namespace modelchecker {
@ -32,12 +36,13 @@ namespace storm {
}
//Start with the probabilities
storm::storage::SparseMatrix<ConstantType> probabilityMatrix;
std::vector<typename storm::storage::SparseMatrix<ConstantType>::index_type> approxRowGroupIndices;// Indicates which rows of the probabilityMatrix belong to a given row of the parametricModel
std::vector<std::size_t> rowSubstitutions;// the substitution used in every row (required if rewards are computed)
std::vector<ProbTableEntry*> matrixEntryToEvalTableMapping;// This vector will get an entry for every probability-matrix entry
//for the corresponding matrix entry, it stores the corresponding entry in the probability evaluation table.
//We can later transform this mapping into the desired mapping with iterators
ProbTableEntry constantProbEntry; //this value is stored in the matrixEntrytoEvalTableMapping for every constant probability matrix entry.
initializeProbabilities(parametricModel, probabilityMatrix, rowSubstitutions, matrixEntryToEvalTableMapping, &constantProbEntry);
initializeProbabilities(parametricModel, probabilityMatrix, approxRowGroupIndices, rowSubstitutions, matrixEntryToEvalTableMapping, &constantProbEntry);
//Now consider rewards
@ -46,22 +51,34 @@ namespace storm {
RewTableEntry constantRewEntry; //this value is stored in the rewardEntryToEvalTableMapping for every constant reward vector entry.
if(this->computeRewards){
std::vector<ConstantType> stateActionRewards;
initializeRewards(parametricModel, probabilityMatrix, rowSubstitutions, stateActionRewards, rewardEntryToEvalTableMapping, &constantRewEntry);
initializeRewards(parametricModel, probabilityMatrix, approxRowGroupIndices, rowSubstitutions, stateActionRewards, rewardEntryToEvalTableMapping, &constantRewEntry);
rewardModels.insert(std::pair<std::string, storm::models::sparse::StandardRewardModel<ConstantType>>("", storm::models::sparse::StandardRewardModel<ConstantType>(boost::optional<std::vector<ConstantType>>(), boost::optional<std::vector<ConstantType>>(std::move(stateActionRewards)))));
}
//Obtain other model ingredients and the approximation model itself
storm::models::sparse::StateLabeling labeling(parametricModel.getStateLabeling());
boost::optional<std::vector<boost::container::flat_set<uint_fast64_t>>> noChoiceLabeling;
this->model=std::make_shared<storm::models::sparse::Mdp<ConstantType>>(std::move(probabilityMatrix), std::move(labeling), std::move(rewardModels), std::move(noChoiceLabeling));
boost::optional<std::vector<boost::container::flat_set<uint_fast64_t>>> noChoiceLabeling;
switch(parametricModel.getType()){
case storm::models::ModelType::Dtmc:
this->model=std::make_shared<storm::models::sparse::Mdp<ConstantType>>(std::move(probabilityMatrix), std::move(labeling), std::move(rewardModels), std::move(noChoiceLabeling));
break;
case storm::models::ModelType::Mdp:
//TODO: use a two-player-game here
this->model=std::make_shared<storm::models::sparse::Mdp<ConstantType>>(std::move(probabilityMatrix), std::move(labeling), std::move(rewardModels), std::move(noChoiceLabeling));
break;
default:
STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Tried to build an Approximation model for an unsupported model type");
}
//translate the matrixEntryToEvalTableMapping into the actual probability mapping
typename storm::storage::SparseMatrix<ConstantType>::index_type matrixRow=0;
auto tableEntry=matrixEntryToEvalTableMapping.begin();
for(typename storm::storage::SparseMatrix<ParametricType>::index_type row=0; row < parametricModel.getTransitionMatrix().getRowCount(); ++row ){
for (; matrixRow<this->model->getTransitionMatrix().getRowGroupIndices()[row+1]; ++matrixRow){
for (; matrixRow<approxRowGroupIndices[row+1]; ++matrixRow){
auto approxModelEntry = this->model->getTransitionMatrix().getRow(matrixRow).begin();
for(auto const& parEntry : parametricModel.getTransitionMatrix().getRow(row)){
if(*tableEntry == &constantProbEntry){
STORM_LOG_THROW(storm::utility::isConstant(parEntry.getValue()), storm::exceptions::UnexpectedException, "Expected a constant matrix entry but got a non-constant one.");
approxModelEntry->setValue(storm::utility::region::convertNumber<ConstantType>(storm::utility::region::getConstantPart(parEntry.getValue())));
} else {
this->probabilityMapping.emplace_back(std::make_pair(&((*tableEntry)->second), approxModelEntry));
@ -96,20 +113,37 @@ namespace storm {
template<typename ParametricSparseModelType, typename ConstantType>
void ApproximationModel<ParametricSparseModelType, ConstantType>::initializeProbabilities(ParametricSparseModelType const& parametricModel,
storm::storage::SparseMatrix<ConstantType>& probabilityMatrix,
std::vector<typename storm::storage::SparseMatrix<ConstantType>::index_type>& approxRowGroupIndices,
std::vector<std::size_t>& rowSubstitutions,
std::vector<ProbTableEntry*>& matrixEntryToEvalTableMapping,
ProbTableEntry* constantEntry) {
STORM_LOG_DEBUG("Approximation model initialization for probabilities");
// Run through the rows of the original model and obtain the different substitutions, the probability evaluation table,
// an MDP probability matrix with some dummy entries, and the mapping between the two
storm::storage::SparseMatrixBuilder<ConstantType> matrixBuilder(0, parametricModel.getNumberOfStates(), 0, true, true, parametricModel.getNumberOfStates());
this->probabilitySubstitutions.emplace_back(std::map<VariableType, TypeOfBound>()); //we want that the empty substitution is always the first one
// a probability matrix with some dummy entries, and the mapping between the two
//TODO: if the parametricModel is nondeterministic, we will need a matrix for a two player game. For now, we assume the two players cooperate, i.e. we get a simple MDP
bool customRowGroups = parametricModel.getTransitionMatrix().hasNontrivialRowGrouping();
auto curParamRowGroup = parametricModel.getTransitionMatrix().getRowGroupIndices().begin();
storm::storage::SparseMatrixBuilder<ConstantType> matrixBuilder(0, //unknown number of rows
parametricModel.getTransitionMatrix().getColumnCount(),
0, //Unknown number of entries
true, //force dimensions
true, //will have custom row grouping
parametricModel.getNumberOfStates());
approxRowGroupIndices.reserve(parametricModel.getTransitionMatrix().getRowCount()+1);
std::size_t numOfNonConstEntries=0;
typename storm::storage::SparseMatrix<ConstantType>::index_type matrixRow=0;
for(typename storm::storage::SparseMatrix<ParametricType>::index_type row=0; row < parametricModel.getTransitionMatrix().getRowCount(); ++row ){
matrixBuilder.newRowGroup(matrixRow);
typename storm::storage::SparseMatrix<ConstantType>::index_type apprModelMatrixRow=0;
for(typename storm::storage::SparseMatrix<ParametricType>::index_type paramRow=0; paramRow < parametricModel.getTransitionMatrix().getRowCount(); ++paramRow ){
approxRowGroupIndices.push_back(apprModelMatrixRow);
if(customRowGroups && paramRow==*curParamRowGroup){
//Only make a new row group if the parametric model matrix has a new row group at this position
matrixBuilder.newRowGroup(apprModelMatrixRow);
++curParamRowGroup;
} else if (!customRowGroups){
matrixBuilder.newRowGroup(apprModelMatrixRow);
}
// Find the different substitutions, i.e., mappings from Variables that occur in this row to {lower, upper}
std::set<VariableType> occurringVariables;
for(auto const& entry : parametricModel.getTransitionMatrix().getRow(row)){
for(auto const& entry : parametricModel.getTransitionMatrix().getRow(paramRow)){
storm::utility::region::gatherOccurringVariables(entry.getValue(), occurringVariables);
}
std::size_t numOfSubstitutions=1ull<<occurringVariables.size(); //=2^(#variables). Note that there is still 1 substitution when #variables==0 (i.e.,the empty substitution)
@ -133,7 +167,7 @@ namespace storm {
//For every substitution, run again through the row and add an entry in matrixEntryToEvalTableMapping as well as dummy entries in the matrix
//Note that this is still executed once, even if no parameters occur.
ConstantType dummyEntry=storm::utility::one<ConstantType>();
for(auto const& entry : parametricModel.getTransitionMatrix().getRow(row)){
for(auto const& entry : parametricModel.getTransitionMatrix().getRow(paramRow)){
if(storm::utility::isConstant(entry.getValue())){
matrixEntryToEvalTableMapping.emplace_back(constantEntry);
} else {
@ -141,30 +175,33 @@ namespace storm {
auto evalTableIt = this->probabilityEvaluationTable.insert(ProbTableEntry(FunctionSubstitution(entry.getValue(), substitutionIndex), storm::utility::zero<ConstantType>())).first;
matrixEntryToEvalTableMapping.emplace_back(&(*evalTableIt));
}
matrixBuilder.addNextValue(matrixRow, entry.getColumn(), dummyEntry);
matrixBuilder.addNextValue(apprModelMatrixRow, entry.getColumn(), dummyEntry);
dummyEntry=storm::utility::zero<ConstantType>();
}
++matrixRow;
++apprModelMatrixRow;
}
}
this->probabilityMapping.reserve(numOfNonConstEntries);
probabilityMatrix=matrixBuilder.build();
approxRowGroupIndices.push_back(apprModelMatrixRow);
}
template<typename ParametricSparseModelType, typename ConstantType>
void ApproximationModel<ParametricSparseModelType, ConstantType>::initializeRewards(ParametricSparseModelType const& parametricModel,
storm::storage::SparseMatrix<ConstantType> const& probabilityMatrix,
std::vector<typename storm::storage::SparseMatrix<ConstantType>::index_type> const& approxRowGroupIndices,
std::vector<std::size_t> const& rowSubstitutions,
std::vector<ConstantType>& stateActionRewardVector,
std::vector<RewTableEntry*>& rewardEntryToEvalTableMapping,
RewTableEntry* constantEntry){
STORM_LOG_DEBUG("Approximation model initialization for Rewards");
STORM_LOG_THROW(parametricModel.getType()==storm::models::ModelType::Dtmc, storm::exceptions::InvalidArgumentException, "Rewards are only supported for DTMCs (yet)"); // Todo : check if this code also works for rewards on mdps (we should then consider state action rewards of the original model and approxRowGroupIndices)
// run through the state reward vector of the parametric model.
// Constant entries can be set directly.
// For Parametric entries we set a dummy value and insert one entry to the rewardEntryEvalTableMapping
stateActionRewardVector.reserve(probabilityMatrix.getRowCount());
rewardEntryToEvalTableMapping.reserve(probabilityMatrix.getRowCount());
std::size_t numOfNonConstRewEntries=0;
this->rewardSubstitutions.emplace_back(std::map<VariableType, TypeOfBound>()); //we want that the empty substitution is always the first one
for(std::size_t state=0; state<parametricModel.getNumberOfStates(); ++state){
std::set<VariableType> occurringRewVariables;
@ -185,7 +222,7 @@ namespace storm {
RewTableEntry* evalTablePtr;
for(auto matrixRow=probabilityMatrix.getRowGroupIndices()[state]; matrixRow<probabilityMatrix.getRowGroupIndices()[state+1]; ++matrixRow){
if(rewardDependsOnProbVars){ //always executed in first iteration
rewardDependsOnProbVars=false;
rewardDependsOnProbVars=false; //Assume that independent...
std::map<VariableType, TypeOfBound> rewardSubstitution;
for(auto const& rewardVar : occurringRewVariables){
typename std::map<VariableType, TypeOfBound>::iterator const substitutionIt=this->probabilitySubstitutions[rowSubstitutions[matrixRow]].find(rewardVar);
@ -193,7 +230,7 @@ namespace storm {
rewardSubstitution.insert(std::make_pair(rewardVar, TypeOfBound::CHOSEOPTIMAL));
} else {
rewardSubstitution.insert(*substitutionIt);
rewardDependsOnProbVars=true;
rewardDependsOnProbVars=true; //.. assumption wrong
}
}
std::size_t substitutionIndex=storm::utility::vector::findOrInsert(this->rewardSubstitutions, std::move(rewardSubstitution));
@ -221,7 +258,7 @@ namespace storm {
}
template<typename ParametricSparseModelType, typename ConstantType>
std::shared_ptr<storm::models::sparse::Mdp<ConstantType>> const& ApproximationModel<ParametricSparseModelType, ConstantType>::getModel() const {
std::shared_ptr<storm::models::sparse::Model<ConstantType>> const& ApproximationModel<ParametricSparseModelType, ConstantType>::getModel() const {
return this->model;
}
@ -306,13 +343,26 @@ namespace storm {
}
template<typename ParametricSparseModelType, typename ConstantType>
std::vector<ConstantType> const& ApproximationModel<ParametricSparseModelType, ConstantType>::computeValues(storm::solver::OptimizationDirection const& optDir) {
storm::modelchecker::SparseMdpPrctlModelChecker<storm::models::sparse::Mdp<ConstantType>> modelChecker(*this->model);
std::vector<ConstantType> const& ApproximationModel<ParametricSparseModelType, ConstantType>::computeValues(storm::solver::OptimizationDirection const& approximationOpDir) {
std::unique_ptr<storm::modelchecker::AbstractModelChecker> modelChecker;
switch(this->getModel()->getType()){
case storm::models::ModelType::Mdp:
modelChecker = std::make_unique<storm::modelchecker::SparseMdpPrctlModelChecker<storm::models::sparse::Mdp<ConstantType>>>(*this->model->template as<storm::models::sparse::Mdp<ConstantType>>());
break;
case storm::models::ModelType::S2pg:
//TODO: instantiate right model checker here
STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "Approximation with two player games not yet implemented");
break;
default:
STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Tried to build an approximation model for an unsupported model type");
}
std::unique_ptr<storm::modelchecker::CheckResult> resultPtr;
//TODO: use this when two player games are available
boost::optional<storm::solver::OptimizationDirection> formulaOpDir = storm::logic::isLowerBound(this->formula->getComparisonType()) ? storm::solver::OptimizationDirection::Minimize : storm::solver::OptimizationDirection::Maximize;
if(this->computeRewards){
//write the reward values into the model
switch(optDir){
switch(approximationOpDir){
case storm::solver::OptimizationDirection::Minimize:
for(auto& mappingTriple : this->rewardMapping){
*std::get<2>(mappingTriple)=*std::get<0>(mappingTriple);
@ -326,13 +376,13 @@ namespace storm {
default:
STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "The given optimality Type is not supported.");
}
//perform model checking on the mdp
//perform model checking
boost::optional<std::string> noRewardModelName; //it should be uniquely given
resultPtr = modelChecker.computeReachabilityRewards(this->formula->asRewardOperatorFormula().getSubformula().asReachabilityRewardFormula(), noRewardModelName, false, optDir);
resultPtr = modelChecker->computeReachabilityRewards(this->formula->asRewardOperatorFormula().getSubformula().asReachabilityRewardFormula(), noRewardModelName, false, approximationOpDir);
}
else {
//perform model checking on the mdp
resultPtr = modelChecker.computeEventuallyProbabilities(this->formula->asProbabilityOperatorFormula().getSubformula().asEventuallyFormula(), false, optDir);
//perform model checking
resultPtr = modelChecker->computeEventuallyProbabilities(this->formula->asProbabilityOperatorFormula().getSubformula().asEventuallyFormula(), false, approximationOpDir);
}
return resultPtr->asExplicitQuantitativeCheckResult<ConstantType>().getValueVector();
}

24
src/modelchecker/region/ApproximationModel.h

@ -10,12 +10,11 @@
#include <unordered_map>
#include <memory>
#include "src/utility/region.h"
#include "src/logic/Formulas.h"
#include "src/utility/region.h"
#include "src/modelchecker/region/ParameterRegion.h"
#include "src/models/sparse/Dtmc.h"
#include "src/models/sparse/Mdp.h"
#include "src/logic/Formulas.h"
#include "src/models/sparse/Model.h"
#include "src/storage/SparseMatrix.h"
namespace storm {
@ -29,7 +28,8 @@ namespace storm {
typedef typename storm::utility::region::CoefficientType<ParametricType> CoefficientType;
/*!
* @note this will not check whether approximation is applicable
* Creates an Approximation model
* @note This will not check whether approximation is applicable
*/
ApproximationModel(ParametricSparseModelType const& parametricModel, std::shared_ptr<storm::logic::OperatorFormula> formula);
virtual ~ApproximationModel();
@ -37,7 +37,7 @@ namespace storm {
/*!
* returns the underlying model
*/
std::shared_ptr<storm::models::sparse::Mdp<ConstantType>> const& getModel() const;
std::shared_ptr<storm::models::sparse::Model<ConstantType>> const& getModel() const;
/*!
* Instantiates the underlying model according to the given region
@ -45,11 +45,11 @@ namespace storm {
void instantiate(ParameterRegion<ParametricType> const& region);
/*!
* Returns the approximated reachability probabilities or expected values for every state.
* Returns the approximated reachability probabilities or reward values for every state.
* Undefined behavior if model has not been instantiated first!
* @param optimalityType Use MAXIMIZE to get upper bounds or MINIMIZE to get lower bounds
* @param approximationOpDir Use MAXIMIZE to get upper bounds or MINIMIZE to get lower bounds
*/
std::vector<ConstantType> const& computeValues(storm::solver::OptimizationDirection const& optDir);
std::vector<ConstantType> const& computeValues(storm::solver::OptimizationDirection const& approximationOpDir);
private:
@ -123,11 +123,11 @@ namespace storm {
typedef typename std::unordered_map<FunctionSubstitution, ConstantType, FuncSubHash>::value_type ProbTableEntry;
typedef typename std::unordered_map<FunctionSubstitution, std::pair<ConstantType, ConstantType>, FuncSubHash>::value_type RewTableEntry;
void initializeProbabilities(ParametricSparseModelType const& parametricModel, storm::storage::SparseMatrix<ConstantType>& probabilityMatrix, std::vector<std::size_t>& rowSubstitutions, std::vector<ProbTableEntry*>& matrixEntryToEvalTableMapping, ProbTableEntry* constantEntry);
void initializeRewards(ParametricSparseModelType const& parametricModel, storm::storage::SparseMatrix<ConstantType> const& probabilityMatrix, std::vector<std::size_t> const& rowSubstitutions, std::vector<ConstantType>& stateActionRewardVector, std::vector<RewTableEntry*>& rewardEntryToEvalTableMapping, RewTableEntry* constantEntry);
void initializeProbabilities(ParametricSparseModelType const& parametricModel, storm::storage::SparseMatrix<ConstantType>& probabilityMatrix, std::vector<typename storm::storage::SparseMatrix<ConstantType>::index_type>& approxRowGroupIndices, std::vector<std::size_t>& rowSubstitutions, std::vector<ProbTableEntry*>& matrixEntryToEvalTableMapping, ProbTableEntry* constantEntry);
void initializeRewards(ParametricSparseModelType const& parametricModel, storm::storage::SparseMatrix<ConstantType> const& probabilityMatrix, std::vector<typename storm::storage::SparseMatrix<ConstantType>::index_type> const& approxRowGroupIndices, std::vector<std::size_t> const& rowSubstitutions, std::vector<ConstantType>& stateActionRewardVector, std::vector<RewTableEntry*>& rewardEntryToEvalTableMapping, RewTableEntry* constantEntry);
//The Model with which we work
std::shared_ptr<storm::models::sparse::Mdp<ConstantType>> model;
std::shared_ptr<storm::models::sparse::Model<ConstantType>> model;
//The formula for which we will compute the values
std::shared_ptr<storm::logic::OperatorFormula> formula;
//A flag that denotes whether we compute probabilities or rewards

9
src/modelchecker/region/SamplingModel.cpp

@ -9,6 +9,8 @@
#include "src/models/sparse/Dtmc.h"
#include "src/models/sparse/Mdp.h"
#include "src/models/ModelType.h"
#include "models/sparse/StandardRewardModel.h"
#include "src/modelchecker/prctl/SparseDtmcPrctlModelChecker.h"
#include "src/modelchecker/prctl/SparseMdpPrctlModelChecker.h"
#include "src/modelchecker/results/ExplicitQuantitativeCheckResult.h"
@ -17,9 +19,6 @@
#include "src/utility/vector.h"
#include "src/exceptions/UnexpectedException.h"
#include "src/exceptions/InvalidArgumentException.h"
#include "models/sparse/StandardRewardModel.h"
#include "cuddObj.hh"
#include "exceptions/IllegalArgumentException.h"
namespace storm {
namespace modelchecker {
@ -63,7 +62,7 @@ namespace storm {
this->model=std::make_shared<storm::models::sparse::Mdp<ConstantType>>(std::move(probabilityMatrix), std::move(labeling), std::move(rewardModels), std::move(noChoiceLabeling));
break;
default:
STORM_LOG_THROW(false, storm::exceptions::IllegalArgumentException, "Tried to build a sampling model for an unsupported model type");
STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Tried to build a sampling model for an unsupported model type");
}
//translate the matrixEntryToEvalTableMapping into the actual probability mapping
@ -196,7 +195,7 @@ namespace storm {
modelChecker = std::make_unique<storm::modelchecker::SparseMdpPrctlModelChecker<storm::models::sparse::Mdp<ConstantType>>>(*this->model->template as<storm::models::sparse::Mdp<ConstantType>>());
break;
default:
STORM_LOG_THROW(false, storm::exceptions::IllegalArgumentException, "Tried to build a sampling model for an unsupported model type");
STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Tried to build a sampling model for an unsupported model type");
}
std::unique_ptr<storm::modelchecker::CheckResult> resultPtr;
//perform model checking

95
src/modelchecker/region/SparseDtmcRegionModelChecker.cpp

@ -409,101 +409,6 @@ namespace storm {
this->timeComputeReachabilityFunction = timeComputeReachabilityFunctionEnd-timeComputeReachabilityFunctionStart;
}
template<typename ParametricSparseModelType, typename ConstantType>
bool SparseDtmcRegionModelChecker<ParametricSparseModelType, ConstantType>::checkApproximativeValues(ParameterRegion<ParametricType>& region, std::vector<ConstantType>& lowerBounds, std::vector<ConstantType>& upperBounds) {
std::chrono::high_resolution_clock::time_point timeMDPBuildStart = std::chrono::high_resolution_clock::now();
this->getApproximationModel()->instantiate(region);
std::chrono::high_resolution_clock::time_point timeMDPBuildEnd = std::chrono::high_resolution_clock::now();
this->timeApproxModelInstantiation += timeMDPBuildEnd-timeMDPBuildStart;
// Decide whether to prove allsat or allviolated.
bool proveAllSat;
switch (region.getCheckResult()){
case RegionCheckResult::UNKNOWN:
switch(storm::settings::regionSettings().getApproxMode()){
case storm::settings::modules::RegionSettings::ApproxMode::TESTFIRST:
//Sample a single point to know whether we should try to prove ALLSAT or ALLVIOLATED
checkPoint(region,region.getSomePoint(), false);
proveAllSat= (region.getCheckResult()==RegionCheckResult::EXISTSSAT);
break;
case storm::settings::modules::RegionSettings::ApproxMode::GUESSALLSAT:
proveAllSat=true;
break;
case storm::settings::modules::RegionSettings::ApproxMode::GUESSALLVIOLATED:
proveAllSat=false;
break;
default:
STORM_LOG_THROW(false, storm::exceptions::InvalidSettingsException, "The specified approxmode is not supported");
}
break;
case RegionCheckResult::ALLSAT:
STORM_LOG_WARN("The checkresult of the current region should not be conclusive (ALLSAT)");
//Intentionally no break;
case RegionCheckResult::EXISTSSAT:
proveAllSat=true;
break;
case RegionCheckResult::ALLVIOLATED:
STORM_LOG_WARN("The checkresult of the current region should not be conclusive (ALLViolated)");
//Intentionally no break;
case RegionCheckResult::EXISTSVIOLATED:
proveAllSat=false;
break;
default:
STORM_LOG_WARN("The checkresult of the current region should not be conclusive, i.e. it should be either EXISTSSAT or EXISTSVIOLATED or UNKNOWN in order to apply approximative values");
proveAllSat=true;
}
bool formulaSatisfied;
if((this->specifiedFormulaHasUpperBound() && proveAllSat) || (!this->specifiedFormulaHasUpperBound() && !proveAllSat)){
//these are the cases in which we need to compute upper bounds
upperBounds = this->getApproximationModel()->computeValues(storm::solver::OptimizationDirection::Maximize);
lowerBounds = std::vector<ConstantType>();
formulaSatisfied = this->valueIsInBoundOfFormula(upperBounds[*this->getApproximationModel()->getModel()->getInitialStates().begin()]);
}
else{
//for the remaining cases we compute lower bounds
lowerBounds = this->getApproximationModel()->computeValues(storm::solver::OptimizationDirection::Minimize);
upperBounds = std::vector<ConstantType>();
formulaSatisfied = this->valueIsInBoundOfFormula(lowerBounds[*this->getApproximationModel()->getModel()->getInitialStates().begin()]);
}
//check if approximation was conclusive
if(proveAllSat && formulaSatisfied){
region.setCheckResult(RegionCheckResult::ALLSAT);
return true;
}
if(!proveAllSat && !formulaSatisfied){
region.setCheckResult(RegionCheckResult::ALLVIOLATED);
return true;
}
if(region.getCheckResult()==RegionCheckResult::UNKNOWN){
//In this case, it makes sense to try to prove the contrary statement
proveAllSat=!proveAllSat;
if(lowerBounds.empty()){
lowerBounds = this->getApproximationModel()->computeValues(storm::solver::OptimizationDirection::Minimize);
formulaSatisfied=this->valueIsInBoundOfFormula(lowerBounds[*this->getApproximationModel()->getModel()->getInitialStates().begin()]);
}
else{
upperBounds = this->getApproximationModel()->computeValues(storm::solver::OptimizationDirection::Maximize);
formulaSatisfied=this->valueIsInBoundOfFormula(upperBounds[*this->getApproximationModel()->getModel()->getInitialStates().begin()]);
}
//check if approximation was conclusive
if(proveAllSat && formulaSatisfied){
region.setCheckResult(RegionCheckResult::ALLSAT);
return true;
}
if(!proveAllSat && !formulaSatisfied){
region.setCheckResult(RegionCheckResult::ALLVIOLATED);
return true;
}
}
//if we reach this point than the result is still inconclusive.
return false;
}
template<typename ParametricSparseModelType, typename ConstantType>
bool SparseDtmcRegionModelChecker<ParametricSparseModelType, ConstantType>::checkPoint(ParameterRegion<ParametricType>& region, std::map<VariableType, CoefficientType>const& point, bool favorViaFunction) {
bool valueInBoundOfFormula;

11
src/modelchecker/region/SparseDtmcRegionModelChecker.h

@ -89,17 +89,6 @@ namespace storm {
*/
void computeReachabilityFunction(ParametricSparseModelType const& simpleModel);
/*!
* Instantiates the approximation model to compute bounds on the maximal/minimal reachability probability (or reachability reward).
* If the current region result is EXISTSSAT (or EXISTSVIOLATED), then this function tries to prove ALLSAT (or ALLVIOLATED).
* If this succeeded, then the region check result is changed accordingly.
* If the current region result is UNKNOWN, then this function first tries to prove ALLSAT and if that failed, it tries to prove ALLVIOLATED.
* In any case, the computed bounds are written to the given lowerBounds/upperBounds.
* However, if only the lowerBounds (or upperBounds) have been computed, the other vector is set to a vector of size 0.
* True is returned iff either ALLSAT or ALLVIOLATED could be proved.
*/
virtual bool checkApproximativeValues(ParameterRegion<ParametricType>& region, std::vector<ConstantType>& lowerBounds, std::vector<ConstantType>& upperBounds);
/*!
* Checks the value of the function at the given sampling point.
* May set the satPoint and violatedPoint of the regions if thy are not yet specified and such point is given.

96
src/modelchecker/region/SparseMdpRegionModelChecker.cpp

@ -131,102 +131,6 @@ namespace storm {
}
}
template<typename ParametricSparseModelType, typename ConstantType>
bool SparseMdpRegionModelChecker<ParametricSparseModelType, ConstantType>::checkApproximativeValues(ParameterRegion<ParametricType>& region, std::vector<ConstantType>& lowerBounds, std::vector<ConstantType>& upperBounds) {
std::chrono::high_resolution_clock::time_point timeMDPBuildStart = std::chrono::high_resolution_clock::now();
STORM_LOG_THROW(false, storm::exceptions::NotImplementedException, "The approximation model for mdps has not been implemented yet");
this->getApproximationModel()->instantiate(region);
std::chrono::high_resolution_clock::time_point timeMDPBuildEnd = std::chrono::high_resolution_clock::now();
this->timeApproxModelInstantiation += timeMDPBuildEnd-timeMDPBuildStart;
// Decide whether to prove allsat or allviolated.
bool proveAllSat;
switch (region.getCheckResult()){
case RegionCheckResult::UNKNOWN:
switch(storm::settings::regionSettings().getApproxMode()){
case storm::settings::modules::RegionSettings::ApproxMode::TESTFIRST:
//Sample a single point to know whether we should try to prove ALLSAT or ALLVIOLATED
checkPoint(region,region.getSomePoint(), false);
proveAllSat= (region.getCheckResult()==RegionCheckResult::EXISTSSAT);
break;
case storm::settings::modules::RegionSettings::ApproxMode::GUESSALLSAT:
proveAllSat=true;
break;
case storm::settings::modules::RegionSettings::ApproxMode::GUESSALLVIOLATED:
proveAllSat=false;
break;
default:
STORM_LOG_THROW(false, storm::exceptions::InvalidSettingsException, "The specified approxmode is not supported");
}
break;
case RegionCheckResult::ALLSAT:
STORM_LOG_WARN("The checkresult of the current region should not be conclusive (ALLSAT)");
//Intentionally no break;
case RegionCheckResult::EXISTSSAT:
proveAllSat=true;
break;
case RegionCheckResult::ALLVIOLATED:
STORM_LOG_WARN("The checkresult of the current region should not be conclusive (ALLViolated)");
//Intentionally no break;
case RegionCheckResult::EXISTSVIOLATED:
proveAllSat=false;
break;
default:
STORM_LOG_WARN("The checkresult of the current region should not be conclusive, i.e. it should be either EXISTSSAT or EXISTSVIOLATED or UNKNOWN in order to apply approximative values");
proveAllSat=true;
}
bool formulaSatisfied;
if((this->specifiedFormulaHasUpperBound() && proveAllSat) || (!this->specifiedFormulaHasUpperBound() && !proveAllSat)){
//these are the cases in which we need to compute upper bounds
upperBounds = this->getApproximationModel()->computeValues(storm::solver::OptimizationDirection::Maximize);
lowerBounds = std::vector<ConstantType>();
formulaSatisfied = this->valueIsInBoundOfFormula(upperBounds[*this->getApproximationModel()->getModel()->getInitialStates().begin()]);
}
else{
//for the remaining cases we compute lower bounds
lowerBounds = this->getApproximationModel()->computeValues(storm::solver::OptimizationDirection::Minimize);
upperBounds = std::vector<ConstantType>();
formulaSatisfied = this->valueIsInBoundOfFormula(lowerBounds[*this->getApproximationModel()->getModel()->getInitialStates().begin()]);
}
//check if approximation was conclusive
if(proveAllSat && formulaSatisfied){
region.setCheckResult(RegionCheckResult::ALLSAT);
return true;
}
if(!proveAllSat && !formulaSatisfied){
region.setCheckResult(RegionCheckResult::ALLVIOLATED);
return true;
}
if(region.getCheckResult()==RegionCheckResult::UNKNOWN){
//In this case, it makes sense to try to prove the contrary statement
proveAllSat=!proveAllSat;
if(lowerBounds.empty()){
lowerBounds = this->getApproximationModel()->computeValues(storm::solver::OptimizationDirection::Minimize);
formulaSatisfied=this->valueIsInBoundOfFormula(lowerBounds[*this->getApproximationModel()->getModel()->getInitialStates().begin()]);
}
else{
upperBounds = this->getApproximationModel()->computeValues(storm::solver::OptimizationDirection::Maximize);
formulaSatisfied=this->valueIsInBoundOfFormula(upperBounds[*this->getApproximationModel()->getModel()->getInitialStates().begin()]);
}
//check if approximation was conclusive
if(proveAllSat && formulaSatisfied){
region.setCheckResult(RegionCheckResult::ALLSAT);
return true;
}
if(!proveAllSat && !formulaSatisfied){
region.setCheckResult(RegionCheckResult::ALLVIOLATED);
return true;
}
}
//if we reach this point than the result is still inconclusive.
return false;
}
template<typename ParametricSparseModelType, typename ConstantType>
bool SparseMdpRegionModelChecker<ParametricSparseModelType, ConstantType>::checkPoint(ParameterRegion<ParametricType>& region, std::map<VariableType, CoefficientType>const& point, bool favorViaFunction) {
if(this->valueIsInBoundOfFormula(this->getReachabilityValue(point))){

11
src/modelchecker/region/SparseMdpRegionModelChecker.h

@ -55,17 +55,6 @@ namespace storm {
*/
void preprocessForProbabilities(storm::storage::BitVector& maybeStates, storm::storage::BitVector& targetStates, bool& isApproximationApplicable, boost::optional<ConstantType>& constantResult);
/*!
* Instantiates the approximation model to compute bounds on the maximal/minimal reachability probability (or reachability reward).
* If the current region result is EXISTSSAT (or EXISTSVIOLATED), then this function tries to prove ALLSAT (or ALLVIOLATED).
* If this succeeded, then the region check result is changed accordingly.
* If the current region result is UNKNOWN, then this function first tries to prove ALLSAT and if that failed, it tries to prove ALLVIOLATED.
* In any case, the computed bounds are written to the given lowerBounds/upperBounds.
* However, if only the lowerBounds (or upperBounds) have been computed, the other vector is set to a vector of size 0.
* True is returned iff either ALLSAT or ALLVIOLATED could be proved.
*/
virtual bool checkApproximativeValues(ParameterRegion<ParametricType>& region, std::vector<ConstantType>& lowerBounds, std::vector<ConstantType>& upperBounds);
/*!
* Checks the value of the function at the given sampling point.
* May set the satPoint and violatedPoint of the regions if thy are not yet specified and such point is given.

Loading…
Cancel
Save