/* * File: SamplingModel.cpp * Author: tim * * Created on August 7, 2015, 9:31 AM */ #include "src/modelchecker/region/SamplingModel.h" #include "src/models/sparse/Dtmc.h" #include "src/models/sparse/Mdp.h" #include "src/modelchecker/prctl/SparseDtmcPrctlModelChecker.h" #include "src/modelchecker/prctl/SparseMdpPrctlModelChecker.h" #include "src/modelchecker/results/ExplicitQuantitativeCheckResult.h" #include "src/utility/macros.h" #include "src/utility/region.h" #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 { namespace region { template SamplingModel::SamplingModel(ParametricSparseModelType const& parametricModel, std::shared_ptr formula) : formula(formula){ if(this->formula->isProbabilityOperatorFormula()){ this->computeRewards=false; } else if(this->formula->isRewardOperatorFormula()){ this->computeRewards=true; STORM_LOG_THROW(parametricModel.hasUniqueRewardModel(), storm::exceptions::InvalidArgumentException, "The rewardmodel of the sampling model should be unique"); STORM_LOG_THROW(parametricModel.getUniqueRewardModel()->second.hasOnlyStateRewards(), storm::exceptions::InvalidArgumentException, "The rewardmodel of the sampling model should have state rewards only"); } else { STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Invalid formula: " << this->formula << ". Sampling model only supports eventually or reachability reward formulae."); } //Start with the probabilities storm::storage::SparseMatrix probabilityMatrix; std::vector 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. TableEntry constantEntry; //this value is stored in the matrixEntrytoEvalTableMapping for every constant matrix entry. (also used for rewards later) initializeProbabilities(parametricModel, probabilityMatrix, matrixEntryToEvalTableMapping, &constantEntry); //Now consider rewards std::unordered_map> rewardModels; std::vector rewardEntryToEvalTableMapping; //does a similar thing as matrixEntryToEvalTableMapping if(this->computeRewards){ boost::optional> stateRewards; initializeRewards(parametricModel, stateRewards, rewardEntryToEvalTableMapping, &constantEntry); rewardModels.insert(std::pair>("", storm::models::sparse::StandardRewardModel(std::move(stateRewards)))); } //Obtain other model ingredients and the sampling model itself storm::models::sparse::StateLabeling labeling(parametricModel.getStateLabeling()); boost::optional>> noChoiceLabeling; switch(parametricModel.getType()){ case storm::models::ModelType::Dtmc: this->model=std::make_shared>(std::move(probabilityMatrix), std::move(labeling), std::move(rewardModels), std::move(noChoiceLabeling)); break; case storm::models::ModelType::Mdp: this->model=std::make_shared>(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"); } //translate the matrixEntryToEvalTableMapping into the actual probability mapping auto sampleModelEntry = this->model->getTransitionMatrix().begin(); auto parModelEntry = parametricModel.getTransitionMatrix().begin(); for(auto tableEntry : matrixEntryToEvalTableMapping){ STORM_LOG_THROW(sampleModelEntry->getColumn()==parModelEntry->getColumn(), storm::exceptions::UnexpectedException, "The entries of the given parametric model and the constructed sampling model do not match"); if(tableEntry == &constantEntry){ sampleModelEntry->setValue(storm::utility::region::convertNumber(storm::utility::region::getConstantPart(parModelEntry->getValue()))); } else { this->probabilityMapping.emplace_back(std::make_pair(&(tableEntry->second), sampleModelEntry)); } ++sampleModelEntry; ++parModelEntry; } //also do this for the rewards if(this->computeRewards){ auto sampleModelStateRewardEntry = this->model->getUniqueRewardModel()->second.getStateRewardVector().begin(); for(auto tableEntry : rewardEntryToEvalTableMapping){ if(tableEntry != &constantEntry){ this->stateRewardMapping.emplace_back(std::make_pair(&(tableEntry->second), sampleModelStateRewardEntry)); } ++sampleModelStateRewardEntry; } } } template void SamplingModel::initializeProbabilities(ParametricSparseModelType const& parametricModel, storm::storage::SparseMatrix& probabilityMatrix, std::vector& matrixEntryToEvalTableMapping, TableEntry* constantEntry) { // Run through the rows of the original model and obtain the probability evaluation table, a matrix with dummy entries and the mapping between the two. bool addRowGroups = parametricModel.getTransitionMatrix().hasNontrivialRowGrouping(); auto curRowGroup = parametricModel.getTransitionMatrix().getRowGroupIndices().begin(); storm::storage::SparseMatrixBuilder matrixBuilder(parametricModel.getTransitionMatrix().getRowCount(), parametricModel.getTransitionMatrix().getColumnCount(), parametricModel.getTransitionMatrix().getEntryCount(), true, //force dimensions addRowGroups, addRowGroups ? parametricModel.getTransitionMatrix().getRowGroupCount() : 0); matrixEntryToEvalTableMapping.reserve(parametricModel.getTransitionMatrix().getEntryCount()); std::size_t numOfNonConstEntries=0; for(typename storm::storage::SparseMatrix::index_type row=0; row < parametricModel.getTransitionMatrix().getRowCount(); ++row ){ if(addRowGroups && row==*curRowGroup){ matrixBuilder.newRowGroup(row); ++curRowGroup; } ConstantType dummyEntry=storm::utility::one(); for(auto const& entry : parametricModel.getTransitionMatrix().getRow(row)){ if(storm::utility::isConstant(entry.getValue())){ matrixEntryToEvalTableMapping.emplace_back(constantEntry); } else { ++numOfNonConstEntries; auto evalTableIt = this->probabilityEvaluationTable.insert(TableEntry(entry.getValue(), storm::utility::zero())).first; matrixEntryToEvalTableMapping.emplace_back(&(*evalTableIt)); } matrixBuilder.addNextValue(row,entry.getColumn(), dummyEntry); dummyEntry=storm::utility::zero(); } } this->probabilityMapping.reserve(numOfNonConstEntries); probabilityMatrix=matrixBuilder.build(); } template void SamplingModel::initializeRewards(ParametricSparseModelType const& parametricModel, boost::optional>& stateRewards, std::vector& rewardEntryToEvalTableMapping, TableEntry* constantEntry) { // run through the state reward vector of the parametric model. Constant entries can be set directly. Parametric entries are inserted into the table std::vector stateRewardsAsVector(parametricModel.getNumberOfStates()); rewardEntryToEvalTableMapping.reserve(parametricModel.getNumberOfStates()); std::size_t numOfNonConstEntries=0; for(std::size_t state=0; statesecond.getStateRewardVector()[state])){ stateRewardsAsVector[state] = storm::utility::region::convertNumber(storm::utility::region::getConstantPart(parametricModel.getUniqueRewardModel()->second.getStateRewardVector()[state])); rewardEntryToEvalTableMapping.emplace_back(constantEntry); } else { ++numOfNonConstEntries; auto evalTableIt = this->probabilityEvaluationTable.insert(TableEntry(parametricModel.getUniqueRewardModel()->second.getStateRewardVector()[state], storm::utility::zero())).first; rewardEntryToEvalTableMapping.emplace_back(&(*evalTableIt)); } } this->stateRewardMapping.reserve(numOfNonConstEntries); stateRewards=std::move(stateRewardsAsVector); } template SamplingModel::~SamplingModel() { //Intentionally left empty } template std::shared_ptr> const& SamplingModel::getModel() const { return this->model; } template void SamplingModel::instantiate(std::mapconst& point) { //write entries into evaluation tables for(auto& tableEntry : this->probabilityEvaluationTable){ tableEntry.second=storm::utility::region::convertNumber( storm::utility::region::evaluateFunction(tableEntry.first, point)); } for(auto& tableEntry : this->rewardEvaluationTable){ tableEntry.second=storm::utility::region::convertNumber( storm::utility::region::evaluateFunction(tableEntry.first, point)); } //write the instantiated values to the matrix according to the mappings for(auto& mappingPair : this->probabilityMapping){ mappingPair.second->setValue(*(mappingPair.first)); } if(this->computeRewards){ for(auto& mappingPair : this->stateRewardMapping){ *mappingPair.second=*mappingPair.first; } } } template std::vector const& SamplingModel::computeValues() { std::unique_ptr modelChecker; switch(this->getModel()->getType()){ case storm::models::ModelType::Dtmc: modelChecker = std::make_unique>>(*this->model->template as>()); break; case storm::models::ModelType::Mdp: modelChecker = std::make_unique>>(*this->model->template as>()); break; default: STORM_LOG_THROW(false, storm::exceptions::IllegalArgumentException, "Tried to build a sampling model for an unsupported model type"); } std::unique_ptr resultPtr; //perform model checking boost::optional opDir = storm::logic::isLowerBound(this->formula->getComparisonType()) ? storm::solver::OptimizationDirection::Minimize : storm::solver::OptimizationDirection::Maximize; if(this->computeRewards){ resultPtr = modelChecker->computeReachabilityRewards(this->formula->asRewardOperatorFormula().getSubformula().asReachabilityRewardFormula()); } else { resultPtr = modelChecker->computeEventuallyProbabilities(this->formula->asProbabilityOperatorFormula().getSubformula().asEventuallyFormula(), false, opDir); } return resultPtr->asExplicitQuantitativeCheckResult().getValueVector(); } #ifdef STORM_HAVE_CARL template class SamplingModel>, double>; template class SamplingModel>, double>; #endif } //namespace region } }