Browse Source

storm-pomdp: towards a more mature cli

tempestpy_adaptions
Tim Quatmann 5 years ago
parent
commit
635fbc658a
  1. 478
      src/storm-pomdp-cli/storm-pomdp.cpp
  2. 177
      src/storm-pomdp/analysis/FormulaInformation.cpp
  3. 69
      src/storm-pomdp/analysis/FormulaInformation.h

478
src/storm-pomdp-cli/storm-pomdp.cpp

@ -22,11 +22,15 @@
#include "storm-pomdp/analysis/UniqueObservationStates.h"
#include "storm-pomdp/analysis/QualitativeAnalysis.h"
#include "storm-pomdp/modelchecker/ApproximatePOMDPModelchecker.h"
#include "storm-pomdp/analysis/FormulaInformation.h"
#include "storm-pomdp/analysis/MemlessStrategySearchQualitative.h"
#include "storm-pomdp/analysis/QualitativeStrategySearchNaive.h"
#include "storm/api/storm.h"
#include "storm/utility/Stopwatch.h"
#include "storm/exceptions/UnexpectedException.h"
#include "storm/exceptions/NotSupportedException.h"
#include <typeinfo>
@ -34,96 +38,184 @@ namespace storm {
namespace pomdp {
namespace cli {
template<typename ValueType>
bool extractTargetAndSinkObservationSets(std::shared_ptr<storm::models::sparse::Pomdp<ValueType>> const& pomdp, storm::logic::Formula const& subformula, std::set<uint32_t>& targetObservationSet, storm::storage::BitVector& targetStates, storm::storage::BitVector& badStates) {
//TODO refactor (use model checker to determine the states, then transform into observations).
//TODO rename into appropriate function name.
bool validFormula = false;
if (subformula.isEventuallyFormula()) {
storm::logic::EventuallyFormula const &eventuallyFormula = subformula.asEventuallyFormula();
storm::logic::Formula const &subformula2 = eventuallyFormula.getSubformula();
if (subformula2.isAtomicLabelFormula()) {
storm::logic::AtomicLabelFormula const &alFormula = subformula2.asAtomicLabelFormula();
validFormula = true;
std::string targetLabel = alFormula.getLabel();
auto labeling = pomdp->getStateLabeling();
for (size_t state = 0; state < pomdp->getNumberOfStates(); ++state) {
if (labeling.getStateHasLabel(targetLabel, state)) {
targetObservationSet.insert(pomdp->getObservation(state));
targetStates.set(state);
}
}
} else if (subformula2.isAtomicExpressionFormula()) {
validFormula = true;
std::stringstream stream;
stream << subformula2.asAtomicExpressionFormula().getExpression();
storm::logic::AtomicLabelFormula formula3 = storm::logic::AtomicLabelFormula(stream.str());
std::string targetLabel = formula3.getLabel();
auto labeling = pomdp->getStateLabeling();
for (size_t state = 0; state < pomdp->getNumberOfStates(); ++state) {
if (labeling.getStateHasLabel(targetLabel, state)) {
targetObservationSet.insert(pomdp->getObservation(state));
targetStates.set(state);
}
}
/// Perform preprocessings based on the graph structure (if requested or necessary). Return true, if some preprocessing has been done
template<typename ValueType, storm::dd::DdType DdType>
bool performPreprocessing(std::shared_ptr<storm::models::sparse::Pomdp<ValueType>>& pomdp, storm::pomdp::analysis::FormulaInformation& formulaInfo, storm::logic::Formula const& formula) {
auto const& pomdpSettings = storm::settings::getModule<storm::settings::modules::POMDPSettings>();
bool preprocessingPerformed = false;
if (pomdpSettings.isSelfloopReductionSet()) {
bool apply = formulaInfo.isNonNestedReachabilityProbability() && formulaInfo.maximize();
apply = apply || (formulaInfo.isNonNestedExpectedRewardFormula() && formulaInfo.minimize());
if (apply) {
STORM_PRINT_AND_LOG("Eliminating self-loop choices ...");
uint64_t oldChoiceCount = pomdp->getNumberOfChoices();
storm::transformer::GlobalPOMDPSelfLoopEliminator<ValueType> selfLoopEliminator(*pomdp);
pomdp = selfLoopEliminator.transform();
STORM_PRINT_AND_LOG(oldChoiceCount - pomdp->getNumberOfChoices() << " choices eliminated through self-loop elimination." << std::endl);
preprocessingPerformed = true;
}
} else if (subformula.isUntilFormula()) {
storm::logic::UntilFormula const &untilFormula = subformula.asUntilFormula();
storm::logic::Formula const &subformula1 = untilFormula.getLeftSubformula();
if (subformula1.isAtomicLabelFormula()) {
storm::logic::AtomicLabelFormula const &alFormula = subformula1.asAtomicLabelFormula();
std::string targetLabel = alFormula.getLabel();
auto labeling = pomdp->getStateLabeling();
for (size_t state = 0; state < pomdp->getNumberOfStates(); ++state) {
if (!labeling.getStateHasLabel(targetLabel, state)) {
badStates.set(state);
}
}
} else if (subformula1.isAtomicExpressionFormula()) {
std::stringstream stream;
stream << subformula1.asAtomicExpressionFormula().getExpression();
storm::logic::AtomicLabelFormula formula3 = storm::logic::AtomicLabelFormula(stream.str());
std::string targetLabel = formula3.getLabel();
auto labeling = pomdp->getStateLabeling();
for (size_t state = 0; state < pomdp->getNumberOfStates(); ++state) {
if (!labeling.getStateHasLabel(targetLabel, state)) {
badStates.set(state);
}
}
if (pomdpSettings.isQualitativeReductionSet() && formulaInfo.isNonNestedReachabilityProbability()) {
storm::analysis::QualitativeAnalysis<ValueType> qualitativeAnalysis(*pomdp);
STORM_PRINT_AND_LOG("Computing states with probability 0 ...");
storm::storage::BitVector prob0States = qualitativeAnalysis.analyseProb0(formula.asProbabilityOperatorFormula());
std::cout << prob0States << std::endl;
STORM_PRINT_AND_LOG(" done." << std::endl);
STORM_PRINT_AND_LOG("Computing states with probability 1 ...");
storm::storage::BitVector prob1States = qualitativeAnalysis.analyseProb1(formula.asProbabilityOperatorFormula());
std::cout << prob1States << std::endl;
STORM_PRINT_AND_LOG(" done." << std::endl);
storm::pomdp::transformer::KnownProbabilityTransformer<ValueType> kpt = storm::pomdp::transformer::KnownProbabilityTransformer<ValueType>();
pomdp = kpt.transform(*pomdp, prob0States, prob1States);
// Update formulaInfo to changes from Preprocessing
formulaInfo.updateTargetStates(*pomdp, std::move(prob1States));
formulaInfo.updateSinkStates(*pomdp, std::move(prob0States));
preprocessingPerformed = true;
} else if (pomdpSettings.isGridApproximationSet()) {
// We still might need to apply the KnownProbabilityTransformer, to ensure that the grid approximation works properly
if (formulaInfo.isNonNestedReachabilityProbability()) {
if (!formulaInfo.getTargetStates().observationClosed || !formulaInfo.getSinkStates().states.empty()) {
// Make target states observation closed and/or sink states absorbing
storm::pomdp::transformer::KnownProbabilityTransformer<ValueType> kpt = storm::pomdp::transformer::KnownProbabilityTransformer<ValueType>();
auto prob0States = formulaInfo.getSinkStates().states;
auto prob1States = formulaInfo.getTargetStates().states;
pomdp = kpt.transform(*pomdp, prob0States, prob1States);
// Update formulaInfo to changes from Preprocessing
formulaInfo.updateTargetStates(*pomdp, std::move(prob1States));
formulaInfo.updateSinkStates(*pomdp, std::move(prob0States));
preprocessingPerformed = true;
}
} else if (formulaInfo.isNonNestedExpectedRewardFormula()) {
STORM_LOG_THROW(formulaInfo.getTargetStates().observationClosed, storm::exceptions::NotSupportedException, "Target states of reward property are not observation closed. This case is not yet implemented.");
}
}
return preprocessingPerformed;
}
template<typename ValueType, storm::dd::DdType DdType>
bool performAnalysis(std::shared_ptr<storm::models::sparse::Pomdp<ValueType>> const& pomdp, storm::pomdp::analysis::FormulaInformation const& formulaInfo) {
auto const& pomdpSettings = storm::settings::getModule<storm::settings::modules::POMDPSettings>();
bool analysisPerformed = false;
if (pomdpSettings.isGridApproximationSet()) {
STORM_LOG_THROW(formulaInfo.isNonNestedReachabilityProbability() || formulaInfo.isNonNestedExpectedRewardFormula(), storm::exceptions::NotSupportedException, "Unsupported formula type for Grid approximation.");
STORM_LOG_THROW(!formulaInfo.getTargetStates().empty(), storm::exceptions::UnexpectedException, "The set of target states is empty.");
STORM_LOG_THROW(formulaInfo.getTargetStates().observationClosed, storm::exceptions::UnexpectedException, "Observations on target states also occur on non-target states. This is unexpected at this point.");
storm::pomdp::modelchecker::ApproximatePOMDPModelchecker<ValueType> checker = storm::pomdp::modelchecker::ApproximatePOMDPModelchecker<ValueType>();
std::unique_ptr<storm::pomdp::modelchecker::POMDPCheckResult<ValueType>> result;
if (formulaInfo.isNonNestedReachabilityProbability()) {
result = checker.refineReachabilityProbability(*pomdp, formulaInfo.getTargetStates().observations, formulaInfo.minimize(), pomdpSettings.getGridResolution(), pomdpSettings.getExplorationThreshold());
} else {
// TODO: no exploration threshold?
result = checker.computeReachabilityReward(*pomdp, formulaInfo.getTargetStates().observations, formulaInfo.minimize(), pomdpSettings.getGridResolution());
}
ValueType overRes = result->overApproxValue;
ValueType underRes = result->underApproxValue;
if (overRes != underRes) {
STORM_PRINT("Overapproximation Result: " << overRes << std::endl)
STORM_PRINT("Underapproximation Result: " << underRes << std::endl)
} else {
STORM_PRINT("Result: " << overRes << std::endl)
}
analysisPerformed = true;
}
if (pomdpSettings.isMemlessSearchSet()) {
STORM_LOG_THROW(formulaInfo.isNonNestedReachabilityProbability(), storm::exceptions::NotSupportedException, "Qualitative memoryless scheduler search is not implemented for this property type.");
// std::cout << std::endl;
// pomdp->writeDotToStream(std::cout);
// std::cout << std::endl;
// std::cout << std::endl;
storm::expressions::ExpressionManager expressionManager;
std::shared_ptr<storm::utility::solver::SmtSolverFactory> smtSolverFactory = std::make_shared<storm::utility::solver::Z3SmtSolverFactory>();
if (pomdpSettings.getMemlessSearchMethod() == "ccd16memless") {
storm::pomdp::QualitativeStrategySearchNaive<ValueType> memlessSearch(*pomdp, formulaInfo.getTargetStates().observations, formulaInfo.getTargetStates().states, formulaInfo.getSinkStates().states, smtSolverFactory);
memlessSearch.findNewStrategyForSomeState(5);
} else if (pomdpSettings.getMemlessSearchMethod() == "iterative") {
storm::pomdp::MemlessStrategySearchQualitative<ValueType> memlessSearch(*pomdp, formulaInfo.getTargetStates().observations, formulaInfo.getTargetStates().states, formulaInfo.getSinkStates().states, smtSolverFactory);
memlessSearch.findNewStrategyForSomeState(5);
} else {
return false;
STORM_LOG_ERROR("This method is not implemented.");
}
storm::logic::Formula const &subformula2 = untilFormula.getRightSubformula();
if (subformula2.isAtomicLabelFormula()) {
storm::logic::AtomicLabelFormula const &alFormula = subformula2.asAtomicLabelFormula();
validFormula = true;
std::string targetLabel = alFormula.getLabel();
auto labeling = pomdp->getStateLabeling();
for (size_t state = 0; state < pomdp->getNumberOfStates(); ++state) {
if (labeling.getStateHasLabel(targetLabel, state)) {
targetObservationSet.insert(pomdp->getObservation(state));
targetStates.set(state);
}
analysisPerformed = true;
}
return analysisPerformed;
}
}
} else if (subformula2.isAtomicExpressionFormula()) {
validFormula = true;
std::stringstream stream;
stream << subformula2.asAtomicExpressionFormula().getExpression();
storm::logic::AtomicLabelFormula formula3 = storm::logic::AtomicLabelFormula(stream.str());
std::string targetLabel = formula3.getLabel();
auto labeling = pomdp->getStateLabeling();
for (size_t state = 0; state < pomdp->getNumberOfStates(); ++state) {
if (labeling.getStateHasLabel(targetLabel, state)) {
targetObservationSet.insert(pomdp->getObservation(state));
targetStates.set(state);
}
}
template<typename ValueType, storm::dd::DdType DdType>
bool performTransformation(std::shared_ptr<storm::models::sparse::Pomdp<ValueType>>& pomdp, storm::logic::Formula const& formula) {
auto const& pomdpSettings = storm::settings::getModule<storm::settings::modules::POMDPSettings>();
bool transformationPerformed = false;
bool memoryUnfolded = false;
if (pomdpSettings.getMemoryBound() > 1) {
STORM_PRINT_AND_LOG("Computing the unfolding for memory bound " << pomdpSettings.getMemoryBound() << " and memory pattern '" << storm::storage::toString(pomdpSettings.getMemoryPattern()) << "' ...");
storm::storage::PomdpMemory memory = storm::storage::PomdpMemoryBuilder().build(pomdpSettings.getMemoryPattern(), pomdpSettings.getMemoryBound());
std::cout << memory.toString() << std::endl;
storm::transformer::PomdpMemoryUnfolder<ValueType> memoryUnfolder(*pomdp, memory);
pomdp = memoryUnfolder.transform();
STORM_PRINT_AND_LOG(" done." << std::endl);
pomdp->printModelInformationToStream(std::cout);
transformationPerformed = true;
memoryUnfolded = true;
}
// From now on the pomdp is considered memoryless
if (pomdpSettings.isMecReductionSet()) {
STORM_PRINT_AND_LOG("Eliminating mec choices ...");
// Note: Elimination of mec choices only preserves memoryless schedulers.
uint64_t oldChoiceCount = pomdp->getNumberOfChoices();
storm::transformer::GlobalPomdpMecChoiceEliminator<ValueType> mecChoiceEliminator(*pomdp);
pomdp = mecChoiceEliminator.transform(formula);
STORM_PRINT_AND_LOG(" done." << std::endl);
STORM_PRINT_AND_LOG(oldChoiceCount - pomdp->getNumberOfChoices() << " choices eliminated through MEC choice elimination." << std::endl);
pomdp->printModelInformationToStream(std::cout);
transformationPerformed = true;
}
if (pomdpSettings.isTransformBinarySet() || pomdpSettings.isTransformSimpleSet()) {
if (pomdpSettings.isTransformSimpleSet()) {
STORM_PRINT_AND_LOG("Transforming the POMDP to a simple POMDP.");
pomdp = storm::transformer::BinaryPomdpTransformer<ValueType>().transform(*pomdp, true);
} else {
STORM_PRINT_AND_LOG("Transforming the POMDP to a binary POMDP.");
pomdp = storm::transformer::BinaryPomdpTransformer<ValueType>().transform(*pomdp, false);
}
pomdp->printModelInformationToStream(std::cout);
STORM_PRINT_AND_LOG(" done." << std::endl);
transformationPerformed = true;
}
if (pomdpSettings.isExportToParametricSet()) {
STORM_PRINT_AND_LOG("Transforming memoryless POMDP to pMC...");
storm::transformer::ApplyFiniteSchedulerToPomdp<ValueType> toPMCTransformer(*pomdp);
std::string transformMode = pomdpSettings.getFscApplicationTypeString();
auto pmc = toPMCTransformer.transform(storm::transformer::parsePomdpFscApplicationMode(transformMode));
STORM_PRINT_AND_LOG(" done." << std::endl);
pmc->printModelInformationToStream(std::cout);
STORM_PRINT_AND_LOG("Simplifying pMC...");
//if (generalSettings.isBisimulationSet()) {
pmc = storm::api::performBisimulationMinimization<storm::RationalFunction>(pmc->template as<storm::models::sparse::Dtmc<storm::RationalFunction>>(),{formula.asSharedPointer()}, storm::storage::BisimulationType::Strong)->template as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
//}
STORM_PRINT_AND_LOG(" done." << std::endl);
pmc->printModelInformationToStream(std::cout);
STORM_PRINT_AND_LOG("Exporting pMC...");
storm::analysis::ConstraintCollector<storm::RationalFunction> constraints(*pmc);
auto const& parameterSet = constraints.getVariables();
std::vector<storm::RationalFunctionVariable> parameters(parameterSet.begin(), parameterSet.end());
std::vector<std::string> parameterNames;
for (auto const& parameter : parameters) {
parameterNames.push_back(parameter.name());
}
storm::api::exportSparseModelAsDrn(pmc, pomdpSettings.getExportToParametricFilename(), parameterNames);
STORM_PRINT_AND_LOG(" done." << std::endl);
transformationPerformed = true;
}
if (transformationPerformed && !memoryUnfolded) {
STORM_PRINT_AND_LOG("Implicitly assumed restriction to memoryless schedulers for at least one transformation." << std::endl);
}
return validFormula;
return transformationPerformed;
}
template<typename ValueType, storm::dd::DdType DdType>
@ -131,7 +223,11 @@ namespace storm {
auto const& pomdpSettings = storm::settings::getModule<storm::settings::modules::POMDPSettings>();
auto model = storm::cli::buildPreprocessExportModelWithValueTypeAndDdlib<DdType, ValueType>(symbolicInput, mpi);
STORM_LOG_THROW(model && model->getType() == storm::models::ModelType::Pomdp && model->isSparseModel(), storm::exceptions::WrongFormatException, "Expected a POMDP in sparse representation.");
if (!model) {
STORM_PRINT_AND_LOG("No input model given.");
return;
}
STORM_LOG_THROW(model->getType() == storm::models::ModelType::Pomdp && model->isSparseModel(), storm::exceptions::WrongFormatException, "Expected a POMDP in sparse representation.");
std::shared_ptr<storm::models::sparse::Pomdp<ValueType>> pomdp = model->template as<storm::models::sparse::Pomdp<ValueType>>();
storm::transformer::MakePOMDPCanonic<ValueType> makeCanonic(*pomdp);
@ -151,207 +247,28 @@ namespace storm {
}
if (formula) {
if (formula->isProbabilityOperatorFormula()) {
storm::logic::ProbabilityOperatorFormula const &probFormula = formula->asProbabilityOperatorFormula();
storm::logic::Formula const &subformula1 = probFormula.getSubformula();
std::set<uint32_t> targetObservationSet;
storm::storage::BitVector targetStates(pomdp->getNumberOfStates());
storm::storage::BitVector badStates(pomdp->getNumberOfStates());
bool validFormula = extractTargetAndSinkObservationSets(pomdp, subformula1, targetObservationSet, targetStates, badStates);
STORM_LOG_THROW(validFormula, storm::exceptions::InvalidPropertyException,
"The formula is not supported by the grid approximation");
STORM_LOG_ASSERT(!targetObservationSet.empty(), "The set of target observations is empty!");
boost::optional<storm::storage::BitVector> prob1States;
boost::optional<storm::storage::BitVector> prob0States;
if (pomdpSettings.isSelfloopReductionSet() && !storm::solver::minimize(formula->asProbabilityOperatorFormula().getOptimalityType())) {
STORM_PRINT_AND_LOG("Eliminating self-loop choices ...");
uint64_t oldChoiceCount = pomdp->getNumberOfChoices();
storm::transformer::GlobalPOMDPSelfLoopEliminator<ValueType> selfLoopEliminator(*pomdp);
pomdp = selfLoopEliminator.transform();
STORM_PRINT_AND_LOG(oldChoiceCount - pomdp->getNumberOfChoices() << " choices eliminated through self-loop elimination." << std::endl);
}
if (pomdpSettings.isQualitativeReductionSet()) {
storm::analysis::QualitativeAnalysis<ValueType> qualitativeAnalysis(*pomdp);
STORM_PRINT_AND_LOG("Computing states with probability 0 ...");
prob0States = qualitativeAnalysis.analyseProb0(formula->asProbabilityOperatorFormula());
std::cout << *prob0States << std::endl;
STORM_PRINT_AND_LOG(" done." << std::endl);
STORM_PRINT_AND_LOG("Computing states with probability 1 ...");
prob1States = qualitativeAnalysis.analyseProb1(formula->asProbabilityOperatorFormula());
std::cout << *prob1States << std::endl;
STORM_PRINT_AND_LOG(" done." << std::endl);
//std::cout << "actual reduction not yet implemented..." << std::endl;
storm::pomdp::transformer::KnownProbabilityTransformer<ValueType> kpt = storm::pomdp::transformer::KnownProbabilityTransformer<ValueType>();
pomdp = kpt.transform(*pomdp, *prob0States, *prob1States);
}
if (pomdpSettings.isGridApproximationSet()) {
storm::pomdp::modelchecker::ApproximatePOMDPModelchecker<ValueType> checker = storm::pomdp::modelchecker::ApproximatePOMDPModelchecker<ValueType>();
auto overRes = storm::utility::one<ValueType>();
auto underRes = storm::utility::zero<ValueType>();
std::unique_ptr<storm::pomdp::modelchecker::POMDPCheckResult<ValueType>> result;
result = checker.refineReachabilityProbability(*pomdp, targetObservationSet, probFormula.getOptimalityType() == storm::OptimizationDirection::Minimize,
pomdpSettings.getGridResolution(), pomdpSettings.getExplorationThreshold());
//result = checker.computeReachabilityProbabilityOTF(*pomdp, targetObservationSet, probFormula.getOptimalityType() == storm::OptimizationDirection::Minimize, pomdpSettings.getGridResolution(), pomdpSettings.getExplorationThreshold());
overRes = result->overApproxValue;
underRes = result->underApproxValue;
if (overRes != underRes) {
STORM_PRINT("Overapproximation Result: " << overRes << std::endl)
STORM_PRINT("Underapproximation Result: " << underRes << std::endl)
} else {
STORM_PRINT("Result: " << overRes << std::endl)
}
}
if (pomdpSettings.isMemlessSearchSet()) {
// std::cout << std::endl;
// pomdp->writeDotToStream(std::cout);
// std::cout << std::endl;
// std::cout << std::endl;
storm::expressions::ExpressionManager expressionManager;
std::shared_ptr<storm::utility::solver::SmtSolverFactory> smtSolverFactory = std::make_shared<storm::utility::solver::Z3SmtSolverFactory>();
if (pomdpSettings.getMemlessSearchMethod() == "ccd16memless") {
storm::pomdp::QualitativeStrategySearchNaive<ValueType> memlessSearch(*pomdp, targetObservationSet, targetStates, badStates, smtSolverFactory);
memlessSearch.findNewStrategyForSomeState(5);
} else if (pomdpSettings.getMemlessSearchMethod() == "iterative") {
storm::pomdp::MemlessStrategySearchQualitative<ValueType> memlessSearch(*pomdp, targetObservationSet, targetStates, badStates, smtSolverFactory);
memlessSearch.findNewStrategyForSomeState(5);
} else {
STORM_LOG_ERROR("This method is not implemented.");
}
}
} else if (formula->isRewardOperatorFormula()) {
if (pomdpSettings.isSelfloopReductionSet() && storm::solver::minimize(formula->asRewardOperatorFormula().getOptimalityType())) {
STORM_PRINT_AND_LOG("Eliminating self-loop choices ...");
uint64_t oldChoiceCount = pomdp->getNumberOfChoices();
storm::transformer::GlobalPOMDPSelfLoopEliminator<ValueType> selfLoopEliminator(*pomdp);
pomdp = selfLoopEliminator.transform();
STORM_PRINT_AND_LOG(oldChoiceCount - pomdp->getNumberOfChoices() << " choices eliminated through self-loop elimination." << std::endl);
}
if (pomdpSettings.isGridApproximationSet()) {
std::string rewardModelName;
storm::logic::RewardOperatorFormula const &rewFormula = formula->asRewardOperatorFormula();
if (rewFormula.hasRewardModelName()) {
rewardModelName = rewFormula.getRewardModelName();
}
storm::logic::Formula const &subformula1 = rewFormula.getSubformula();
std::set<uint32_t> targetObservationSet;
//TODO refactor
bool validFormula = false;
if (subformula1.isEventuallyFormula()) {
storm::logic::EventuallyFormula const &eventuallyFormula = subformula1.asEventuallyFormula();
storm::logic::Formula const &subformula2 = eventuallyFormula.getSubformula();
if (subformula2.isAtomicLabelFormula()) {
storm::logic::AtomicLabelFormula const &alFormula = subformula2.asAtomicLabelFormula();
validFormula = true;
std::string targetLabel = alFormula.getLabel();
auto labeling = pomdp->getStateLabeling();
for (size_t state = 0; state < pomdp->getNumberOfStates(); ++state) {
if (labeling.getStateHasLabel(targetLabel, state)) {
targetObservationSet.insert(pomdp->getObservation(state));
}
}
} else if (subformula2.isAtomicExpressionFormula()) {
validFormula = true;
std::stringstream stream;
stream << subformula2.asAtomicExpressionFormula().getExpression();
storm::logic::AtomicLabelFormula formula3 = storm::logic::AtomicLabelFormula(stream.str());
std::string targetLabel = formula3.getLabel();
auto labeling = pomdp->getStateLabeling();
for (size_t state = 0; state < pomdp->getNumberOfStates(); ++state) {
if (labeling.getStateHasLabel(targetLabel, state)) {
targetObservationSet.insert(pomdp->getObservation(state));
}
}
}
}
STORM_LOG_THROW(validFormula, storm::exceptions::InvalidPropertyException,
"The formula is not supported by the grid approximation");
STORM_LOG_ASSERT(!targetObservationSet.empty(), "The set of target observations is empty!");
storm::pomdp::modelchecker::ApproximatePOMDPModelchecker<ValueType> checker = storm::pomdp::modelchecker::ApproximatePOMDPModelchecker<ValueType>();
auto overRes = storm::utility::one<ValueType>();
auto underRes = storm::utility::zero<ValueType>();
std::unique_ptr<storm::pomdp::modelchecker::POMDPCheckResult<ValueType>> result;
result = checker.computeReachabilityReward(*pomdp, targetObservationSet,
rewFormula.getOptimalityType() ==
storm::OptimizationDirection::Minimize,
pomdpSettings.getGridResolution());
overRes = result->overApproxValue;
underRes = result->underApproxValue;
}
}
if (pomdpSettings.getMemoryBound() > 1) {
STORM_PRINT_AND_LOG("Computing the unfolding for memory bound " << pomdpSettings.getMemoryBound() << " and memory pattern '" << storm::storage::toString(pomdpSettings.getMemoryPattern()) << "' ...");
storm::storage::PomdpMemory memory = storm::storage::PomdpMemoryBuilder().build(pomdpSettings.getMemoryPattern(), pomdpSettings.getMemoryBound());
std::cout << memory.toString() << std::endl;
storm::transformer::PomdpMemoryUnfolder<ValueType> memoryUnfolder(*pomdp, memory);
pomdp = memoryUnfolder.transform();
STORM_PRINT_AND_LOG(" done." << std::endl);
pomdp->printModelInformationToStream(std::cout);
} else {
STORM_PRINT_AND_LOG("Assumming memoryless schedulers." << std::endl;)
}
// From now on the pomdp is considered memoryless
if (pomdpSettings.isMecReductionSet()) {
STORM_PRINT_AND_LOG("Eliminating mec choices ...");
// Note: Elimination of mec choices only preserves memoryless schedulers.
uint64_t oldChoiceCount = pomdp->getNumberOfChoices();
storm::transformer::GlobalPomdpMecChoiceEliminator<ValueType> mecChoiceEliminator(*pomdp);
pomdp = mecChoiceEliminator.transform(*formula);
STORM_PRINT_AND_LOG(" done." << std::endl);
STORM_PRINT_AND_LOG(oldChoiceCount - pomdp->getNumberOfChoices() << " choices eliminated through MEC choice elimination." << std::endl);
auto formulaInfo = storm::pomdp::analysis::getFormulaInformation(*pomdp, *formula);
STORM_LOG_THROW(!formulaInfo.isUnsupported(), storm::exceptions::InvalidPropertyException, "The formula '" << *formula << "' is not supported by storm-pomdp.");
storm::utility::Stopwatch sw(true);
// Note that formulaInfo contains state-based information which potentially needs to be updated during preprocessing
if (performPreprocessing<ValueType, DdType>(pomdp, formulaInfo, *formula)) {
sw.stop();
STORM_PRINT_AND_LOG("Time for graph-based POMDP (pre-)processing: " << sw << "s." << std::endl);
pomdp->printModelInformationToStream(std::cout);
}
if (pomdpSettings.isTransformBinarySet() || pomdpSettings.isTransformSimpleSet()) {
if (pomdpSettings.isTransformSimpleSet()) {
STORM_PRINT_AND_LOG("Transforming the POMDP to a simple POMDP.");
pomdp = storm::transformer::BinaryPomdpTransformer<ValueType>().transform(*pomdp, true);
} else {
STORM_PRINT_AND_LOG("Transforming the POMDP to a binary POMDP.");
pomdp = storm::transformer::BinaryPomdpTransformer<ValueType>().transform(*pomdp, false);
}
pomdp->printModelInformationToStream(std::cout);
STORM_PRINT_AND_LOG(" done." << std::endl);
sw.restart();
if (performAnalysis<ValueType, DdType>(pomdp, formulaInfo)) {
sw.stop();
STORM_PRINT_AND_LOG("Time for POMDP analysis: " << sw << "s." << std::endl);
}
if (pomdpSettings.isExportToParametricSet()) {
STORM_PRINT_AND_LOG("Transforming memoryless POMDP to pMC...");
storm::transformer::ApplyFiniteSchedulerToPomdp<ValueType> toPMCTransformer(*pomdp);
std::string transformMode = pomdpSettings.getFscApplicationTypeString();
auto pmc = toPMCTransformer.transform(storm::transformer::parsePomdpFscApplicationMode(transformMode));
STORM_PRINT_AND_LOG(" done." << std::endl);
pmc->printModelInformationToStream(std::cout);
STORM_PRINT_AND_LOG("Simplifying pMC...");
//if (generalSettings.isBisimulationSet()) {
pmc = storm::api::performBisimulationMinimization<storm::RationalFunction>(pmc->template as<storm::models::sparse::Dtmc<storm::RationalFunction>>(),{formula}, storm::storage::BisimulationType::Strong)->template as<storm::models::sparse::Dtmc<storm::RationalFunction>>();
//}
STORM_PRINT_AND_LOG(" done." << std::endl);
pmc->printModelInformationToStream(std::cout);
STORM_PRINT_AND_LOG("Exporting pMC...");
storm::analysis::ConstraintCollector<storm::RationalFunction> constraints(*pmc);
auto const& parameterSet = constraints.getVariables();
std::vector<storm::RationalFunctionVariable> parameters(parameterSet.begin(), parameterSet.end());
std::vector<std::string> parameterNames;
for (auto const& parameter : parameters) {
parameterNames.push_back(parameter.name());
}
storm::api::exportSparseModelAsDrn(pmc, pomdpSettings.getExportToParametricFilename(), parameterNames);
STORM_PRINT_AND_LOG(" done." << std::endl);
sw.restart();
if (performTransformation<ValueType, DdType>(pomdp, *formula)) {
sw.stop();
STORM_PRINT_AND_LOG("Time for POMDP transformation(s): " << sw << "s." << std::endl);
}
} else {
STORM_LOG_WARN("Nothing to be done. Did you forget to specify a formula?");
}
@ -360,6 +277,7 @@ namespace storm {
template <storm::dd::DdType DdType>
void processOptionsWithDdLib(storm::cli::SymbolicInput const& symbolicInput, storm::cli::ModelProcessingInformation const& mpi) {
STORM_LOG_ERROR_COND(mpi.buildValueType == mpi.verificationValueType, "Build value type differs from verification value type. Will ignore Verification value type.");
switch (mpi.buildValueType) {
case storm::cli::ModelProcessingInformation::ValueType::FinitePrecision:
processOptionsWithValueTypeAndDdLib<double, DdType>(symbolicInput, mpi);

177
src/storm-pomdp/analysis/FormulaInformation.cpp

@ -0,0 +1,177 @@
#include "storm-pomdp/analysis/FormulaInformation.h"
#include "storm/logic/Formulas.h"
#include "storm/logic/FragmentSpecification.h"
#include "storm/models/sparse/Pomdp.h"
#include "storm/models/sparse/StandardRewardModel.h"
#include "storm/modelchecker/propositional/SparsePropositionalModelChecker.h"
#include "storm/modelchecker/results/ExplicitQualitativeCheckResult.h"
#include "storm/utility/macros.h"
#include "storm/exceptions/InvalidPropertyException.h"
namespace storm {
namespace pomdp {
namespace analysis {
bool FormulaInformation::StateSet::empty() const {
STORM_LOG_ASSERT(states.empty() == observations.empty(), "Inconsistent StateSet.");
return observations.empty();
}
FormulaInformation::FormulaInformation() : type(Type::Unsupported) {
// Intentionally left empty
}
FormulaInformation::FormulaInformation(Type const& type, storm::solver::OptimizationDirection const& dir, boost::optional<std::string> const& rewardModelName) : type(type), optimizationDirection(dir), rewardModelName(rewardModelName) {
STORM_LOG_ASSERT(!this->rewardModelName.is_initialized() || this->type == Type::NonNestedExpectedRewardFormula, "Got a reward model name for a non-reward formula.");
}
FormulaInformation::Type const& FormulaInformation::getType() const {
return type;
}
bool FormulaInformation::isNonNestedReachabilityProbability() const {
return type == Type::NonNestedReachabilityProbability;
}
bool FormulaInformation::isNonNestedExpectedRewardFormula() const {
return type == Type::NonNestedExpectedRewardFormula;
}
bool FormulaInformation::isUnsupported() const {
return type == Type::Unsupported;
}
typename FormulaInformation::StateSet const& FormulaInformation::getTargetStates() const {
STORM_LOG_ASSERT(this->type == Type::NonNestedExpectedRewardFormula || this->type == Type::NonNestedReachabilityProbability, "Target states requested for unexpected formula type.");
return targetStates.get();
}
typename FormulaInformation::StateSet const& FormulaInformation::getSinkStates() const {
STORM_LOG_ASSERT(this->type == Type::NonNestedReachabilityProbability, "Sink states requested for unexpected formula type.");
return sinkStates.get();
}
std::string const& FormulaInformation::getRewardModelName() const {
STORM_LOG_ASSERT(this->type == Type::NonNestedExpectedRewardFormula, "Reward model requested for unexpected formula type.");
return rewardModelName.get();
}
storm::solver::OptimizationDirection const& FormulaInformation::getOptimizationDirection() const {
return optimizationDirection;
}
bool FormulaInformation::minimize() const {
return storm::solver::minimize(optimizationDirection);
}
bool FormulaInformation::maximize() const {
return storm::solver::maximize(optimizationDirection);
}
template <typename PomdpType>
FormulaInformation::StateSet getStateSet(PomdpType const& pomdp, storm::storage::BitVector&& inputStates) {
FormulaInformation::StateSet result;
result.states = std::move(inputStates);
for (auto const& state : result.states) {
result.observations.insert(pomdp.getObservation(state));
}
// check if this set is observation-closed, i.e., whether there is a state outside of this set with one of the observations collected above
result.observationClosed = true;
for (uint64_t state = result.states.getNextUnsetIndex(0); state < result.states.size(); state = result.states.getNextUnsetIndex(state + 1)) {
if (result.observations.count(pomdp.getObservation(state)) > 0) {
result.observationClosed = false;
break;
}
}
return result;
}
template <typename PomdpType>
void FormulaInformation::updateTargetStates(PomdpType const& pomdp, storm::storage::BitVector&& newTargetStates) {
STORM_LOG_ASSERT(this->type == Type::NonNestedExpectedRewardFormula || this->type == Type::NonNestedReachabilityProbability, "Target states updated for unexpected formula type.");
targetStates = getStateSet(pomdp, std::move(newTargetStates));
}
template <typename PomdpType>
void FormulaInformation::updateSinkStates(PomdpType const& pomdp, storm::storage::BitVector&& newSinkStates) {
STORM_LOG_ASSERT(this->type == Type::NonNestedReachabilityProbability, "Sink states requested for unexpected formula type.");
sinkStates = getStateSet(pomdp, std::move(newSinkStates));
}
template <typename PomdpType>
storm::storage::BitVector getStates(storm::logic::Formula const& propositionalFormula, bool formulaInverted, PomdpType const& pomdp) {
storm::modelchecker::SparsePropositionalModelChecker<PomdpType> mc(pomdp);
auto checkResult = mc.check(propositionalFormula);
storm::storage::BitVector resultBitVector(checkResult->asExplicitQualitativeCheckResult().getTruthValuesVector());
if (formulaInverted) {
resultBitVector.complement();
}
return resultBitVector;
}
template <typename PomdpType>
FormulaInformation getFormulaInformation(PomdpType const& pomdp, storm::logic::ProbabilityOperatorFormula const& formula) {
STORM_LOG_THROW(formula.hasOptimalityType(), storm::exceptions::InvalidPropertyException, "The property does not specify an optimization direction (min/max)");
STORM_LOG_WARN_COND(!formula.hasBound(), "The probability threshold for the given property will be ignored.");
auto const& subformula = formula.getSubformula();
std::shared_ptr<storm::logic::Formula const> targetStatesFormula, constraintsStatesFormula;
if (subformula.isEventuallyFormula()) {
targetStatesFormula = subformula.asEventuallyFormula().getSubformula().asSharedPointer();
constraintsStatesFormula = storm::logic::Formula::getTrueFormula()->asSharedPointer();
} else if (subformula.isUntilFormula()) {
storm::logic::UntilFormula const &untilFormula = subformula.asUntilFormula();
targetStatesFormula = untilFormula.getRightSubformula().asSharedPointer();
constraintsStatesFormula = untilFormula.getLeftSubformula().asSharedPointer();
}
if (targetStatesFormula && targetStatesFormula->isInFragment(storm::logic::propositional()) && constraintsStatesFormula && constraintsStatesFormula->isInFragment(storm::logic::propositional())) {
FormulaInformation result(FormulaInformation::Type::NonNestedReachabilityProbability, formula.getOptimalityType());
result.updateTargetStates(pomdp, getStates(*targetStatesFormula, false, pomdp));
result.updateSinkStates(pomdp, getStates(*constraintsStatesFormula, true, pomdp));
return result;
}
return FormulaInformation();
}
template <typename PomdpType>
FormulaInformation getFormulaInformation(PomdpType const& pomdp, storm::logic::RewardOperatorFormula const& formula) {
STORM_LOG_THROW(formula.hasOptimalityType(), storm::exceptions::InvalidPropertyException, "The property does not specify an optimization direction (min/max)");
STORM_LOG_WARN_COND(formula.hasBound(), "The reward threshold for the given property will be ignored.");
std::string rewardModelName = "";
if (formula.hasRewardModelName()) {
rewardModelName = formula.getRewardModelName();
STORM_LOG_THROW(pomdp.hasRewardModel(rewardModelName), storm::exceptions::InvalidPropertyException, "Selected reward model with name '" << rewardModelName << "' does not exist.");
} else {
STORM_LOG_THROW(pomdp.hasUniqueRewardModel(), storm::exceptions::InvalidPropertyException, "Reward operator formula does not specify a reward model and the reward model is not unique.");
rewardModelName = pomdp.getUniqueRewardModelName();
}
auto const& subformula = formula.getSubformula();
std::shared_ptr<storm::logic::Formula const> targetStatesFormula;
if (subformula.isEventuallyFormula()) {
targetStatesFormula = subformula.asEventuallyFormula().getSubformula().asSharedPointer();
}
if (targetStatesFormula && targetStatesFormula->isInFragment(storm::logic::propositional())) {
FormulaInformation result(FormulaInformation::Type::NonNestedReachabilityProbability, formula.getOptimalityType(), rewardModelName);
result.updateTargetStates(pomdp, getStates(*targetStatesFormula, false, pomdp));
return result;
}
return FormulaInformation();
}
template <typename PomdpType>
FormulaInformation getFormulaInformation(PomdpType const& pomdp, storm::logic::Formula const& formula) {
if (formula.isProbabilityOperatorFormula()) {
return getFormulaInformation(pomdp, formula.asProbabilityOperatorFormula());
} else if (formula.isRewardOperatorFormula()) {
return getFormulaInformation(pomdp, formula.asRewardOperatorFormula());
}
return FormulaInformation();
}
template FormulaInformation getFormulaInformation<storm::models::sparse::Pomdp<double>>(storm::models::sparse::Pomdp<double> const& pomdp, storm::logic::Formula const& formula);
template FormulaInformation getFormulaInformation<storm::models::sparse::Pomdp<storm::RationalNumber>>(storm::models::sparse::Pomdp<storm::RationalNumber> const& pomdp, storm::logic::Formula const& formula);
}
}
}

69
src/storm-pomdp/analysis/FormulaInformation.h

@ -0,0 +1,69 @@
#pragma once
#include <set>
#include <string>
#include <boost/optional.hpp>
#include "storm/storage/BitVector.h"
#include "storm/solver/OptimizationDirection.h"
namespace storm {
namespace logic {
class Formula;
}
namespace pomdp {
namespace analysis {
class FormulaInformation {
public:
/// Characterizes a certain set of states
struct StateSet {
storm::storage::BitVector states; // The set of states
std::set<uint32_t> observations; // The set of the observations that are assigned to at least one state of the set
bool observationClosed; // True iff this state set can be uniquely characterized by the observations
bool empty() const;
};
/// Possible supported formula types
enum class Type {
NonNestedReachabilityProbability, // e.g. 'Pmax=? [F "target"]' or 'Pmin=? [!"sink" U "target"]'
NonNestedExpectedRewardFormula, // e.g. 'Rmin=? [F x>0 ]'
Unsupported // The formula type is unsupported
};
FormulaInformation(); // Unsupported
FormulaInformation(Type const& type, storm::solver::OptimizationDirection const& dir, boost::optional<std::string> const& rewardModelName = boost::none);
Type const& getType() const;
bool isNonNestedReachabilityProbability() const;
bool isNonNestedExpectedRewardFormula() const;
bool isUnsupported() const;
StateSet const& getTargetStates() const;
StateSet const& getSinkStates() const; // Shall not be called for reward formulas
std::string const& getRewardModelName() const; // Shall not be called for probability formulas
storm::solver::OptimizationDirection const& getOptimizationDirection() const;
bool minimize() const;
bool maximize() const;
template <typename PomdpType>
void updateTargetStates(PomdpType const& pomdp, storm::storage::BitVector&& newTargetStates);
template <typename PomdpType>
void updateSinkStates(PomdpType const& pomdp, storm::storage::BitVector&& newSinkStates);
private:
Type type;
storm::solver::OptimizationDirection optimizationDirection;
boost::optional<StateSet> targetStates;
boost::optional<StateSet> sinkStates;
boost::optional<std::string> rewardModelName;
};
template <typename PomdpType>
FormulaInformation getFormulaInformation(PomdpType const& pomdp, storm::logic::Formula const& formula);
}
}
}
Loading…
Cancel
Save