#include "storm/utility/initialize.h" #include "storm/settings/modules/GeneralSettings.h" #include "storm/settings/modules/CoreSettings.h" #include "storm/settings/modules/IOSettings.h" #include "storm/settings/modules/DebugSettings.h" #include "storm/settings/modules/CuddSettings.h" #include "storm/settings/modules/SylvanSettings.h" #include "storm/settings/modules/EigenEquationSolverSettings.h" #include "storm/settings/modules/GmmxxEquationSolverSettings.h" #include "storm/settings/modules/NativeEquationSolverSettings.h" #include "storm/settings/modules/EliminationSettings.h" #include "storm/settings/modules/MinMaxEquationSolverSettings.h" #include "storm/settings/modules/GameSolverSettings.h" #include "storm/settings/modules/BisimulationSettings.h" #include "storm/settings/modules/GlpkSettings.h" #include "storm/settings/modules/GurobiSettings.h" #include "storm/settings/modules/Smt2SmtSolverSettings.h" #include "storm/settings/modules/ExplorationSettings.h" #include "storm/settings/modules/ResourceSettings.h" #include "storm/settings/modules/AbstractionSettings.h" #include "storm/settings/modules/BuildSettings.h" #include "storm/settings/modules/JitBuilderSettings.h" #include "storm/settings/modules/TopologicalEquationSolverSettings.h" #include "storm/settings/modules/ModelCheckerSettings.h" #include "storm/settings/modules/MultiplierSettings.h" #include "storm/settings/modules/TransformationSettings.h" #include "storm/settings/modules/MultiObjectiveSettings.h" #include "storm-pomdp-cli/settings/modules/POMDPSettings.h" #include "storm-pomdp-cli/settings/modules/QualitativePOMDPAnalysisSettings.h" #include "storm/analysis/GraphConditions.h" #include "storm-cli-utilities/cli.h" #include "storm-cli-utilities/model-handling.h" #include "storm-pomdp/transformer/KnownProbabilityTransformer.h" #include "storm-pomdp/transformer/ApplyFiniteSchedulerToPomdp.h" #include "storm-pomdp/transformer/GlobalPOMDPSelfLoopEliminator.h" #include "storm-pomdp/transformer/GlobalPomdpMecChoiceEliminator.h" #include "storm-pomdp/transformer/PomdpMemoryUnfolder.h" #include "storm-pomdp/transformer/BinaryPomdpTransformer.h" #include "storm-pomdp/analysis/UniqueObservationStates.h" #include "storm-pomdp/analysis/QualitativeAnalysis.h" #include "storm-pomdp/modelchecker/ApproximatePOMDPModelchecker.h" #include "storm-pomdp/analysis/MemlessStrategySearchQualitative.h" #include "storm-pomdp/analysis/QualitativeStrategySearchNaive.h" #include "storm/api/storm.h" #include /*! * Initialize the settings manager. */ void initializeSettings() { storm::settings::mutableManager().setName("Storm-POMDP", "storm-pomdp"); storm::settings::addModule(); storm::settings::addModule(); storm::settings::addModule(); storm::settings::addModule(); storm::settings::addModule(); storm::settings::addModule(); storm::settings::addModule(); storm::settings::addModule(); storm::settings::addModule(); storm::settings::addModule(); storm::settings::addModule(); storm::settings::addModule(); storm::settings::addModule(); storm::settings::addModule(); storm::settings::addModule(); storm::settings::addModule(); storm::settings::addModule(); storm::settings::addModule(); storm::settings::addModule(); storm::settings::addModule(); storm::settings::addModule(); storm::settings::addModule(); } template bool extractTargetAndSinkObservationSets(std::shared_ptr> const& pomdp, storm::logic::Formula const& subformula, std::set& 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); } } } } 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); } } } else { return false; } 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); } } } 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); } } } } return validFormula; } template std::set extractObservations(storm::models::sparse::Pomdp const& pomdp, storm::storage::BitVector const& states) { std::set observations; for(auto state : states) { observations.insert(pomdp.getObservation(state)); } return observations; } /*! * Entry point for the pomdp backend. * * @param argc The argc argument of main(). * @param argv The argv argument of main(). * @return Return code, 0 if successfull, not 0 otherwise. */ int main(const int argc, const char** argv) { //try { storm::utility::setUp(); storm::cli::printHeader("Storm-pomdp", argc, argv); initializeSettings(); bool optionsCorrect = storm::cli::parseOptions(argc, argv); if (!optionsCorrect) { return -1; } auto const& coreSettings = storm::settings::getModule(); auto const& pomdpSettings = storm::settings::getModule(); auto const& ioSettings = storm::settings::getModule(); auto const &general = storm::settings::getModule(); auto const &debug = storm::settings::getModule(); auto const& pomdpQualSettings = storm::settings::getModule(); if (general.isVerboseSet()) { storm::utility::setLogLevel(l3pp::LogLevel::INFO); } if (debug.isDebugSet()) { storm::utility::setLogLevel(l3pp::LogLevel::DEBUG); } if (debug.isTraceSet()) { storm::utility::setLogLevel(l3pp::LogLevel::TRACE); } if (debug.isLogfileSet()) { storm::utility::initializeFileLogging(); } // For several engines, no model building step is performed, but the verification is started right away. storm::settings::modules::CoreSettings::Engine engine = coreSettings.getEngine(); storm::cli::SymbolicInput symbolicInput = storm::cli::parseAndPreprocessSymbolicInput(); // We should not export here if we are going to do some processing first. auto model = storm::cli::buildPreprocessExportModelWithValueTypeAndDdlib(symbolicInput, engine); STORM_LOG_THROW(model && model->getType() == storm::models::ModelType::Pomdp, storm::exceptions::WrongFormatException, "Expected a POMDP."); std::shared_ptr> pomdp = model->template as>(); std::shared_ptr formula; if (!symbolicInput.properties.empty()) { formula = symbolicInput.properties.front().getRawFormula(); STORM_PRINT_AND_LOG("Analyzing property '" << *formula << "'" << std::endl); STORM_LOG_WARN_COND(symbolicInput.properties.size() == 1, "There is currently no support for multiple properties. All other properties will be ignored."); } if (pomdpSettings.isAnalyzeUniqueObservationsSet()) { STORM_PRINT_AND_LOG("Analyzing states with unique observation ..." << std::endl); storm::analysis::UniqueObservationStates uniqueAnalysis(*pomdp); std::cout << uniqueAnalysis.analyse() << std::endl; } if (formula) { storm::logic::ProbabilityOperatorFormula const &probFormula = formula->asProbabilityOperatorFormula(); storm::logic::Formula const &subformula1 = probFormula.getSubformula(); if (formula->isProbabilityOperatorFormula()) { boost::optional prob1States; boost::optional 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 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 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 kpt = storm::pomdp::transformer::KnownProbabilityTransformer(); pomdp = kpt.transform(*pomdp, *prob0States, *prob1States); } if (ioSettings.isExportDotSet()) { std::shared_ptr> sparseModel = pomdp; storm::api::exportSparseModelAsDot(sparseModel, ioSettings.getExportDotFilename(), ioSettings.getExportDotMaxWidth()); } if (ioSettings.isExportExplicitSet()) { std::shared_ptr> sparseModel = pomdp; storm::api::exportSparseModelAsDrn(sparseModel, ioSettings.getExportExplicitFilename()); } if (pomdpSettings.isGridApproximationSet()) { std::set 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!"); storm::pomdp::modelchecker::ApproximatePOMDPModelchecker checker = storm::pomdp::modelchecker::ApproximatePOMDPModelchecker(); double overRes = storm::utility::one(); double underRes = storm::utility::zero(); std::unique_ptr> result; //result = checker.refineReachabilityProbability(*pomdp, targetObservationSet,probFormula.getOptimalityType() == storm::OptimizationDirection::Minimize, pomdpSettings.getGridResolution(),1,10); result = checker.computeReachabilityProbabilityOTF(*pomdp, targetObservationSet, probFormula.getOptimalityType() == storm::OptimizationDirection::Minimize, pomdpSettings.getGridResolution(), pomdpSettings.getExplorationThreshold()); overRes = result->OverapproximationValue; underRes = result->UnderapproximationValue; 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()) { storm::analysis::QualitativeAnalysis qualitativeAnalysis(*pomdp); // After preprocessing, this might be done cheaper. storm::storage::BitVector targetStates = qualitativeAnalysis.analyseProb1(formula->asProbabilityOperatorFormula()); storm::storage::BitVector surelyNotAlmostSurelyReachTarget = qualitativeAnalysis.analyseProbSmaller1(formula->asProbabilityOperatorFormula()); std::set targetObservationSet = extractObservations(*pomdp, targetStates); storm::expressions::ExpressionManager expressionManager; std::shared_ptr smtSolverFactory = std::make_shared(); uint64_t lookahead = pomdpQualSettings.getLookahead(); if (lookahead == 0) { lookahead = pomdp->getNumberOfStates(); } storm::pomdp::MemlessSearchOptions options; options.onlyDeterministicStrategies = pomdpQualSettings.isOnlyDeterministicSet(); uint64_t loglevel = 0; // TODO a big ugly, but we have our own loglevels. if(storm::utility::getLogLevel() == l3pp::LogLevel::INFO) { loglevel = 1; } else if(storm::utility::getLogLevel() == l3pp::LogLevel::DEBUG) { loglevel = 2; } else if(storm::utility::getLogLevel() == l3pp::LogLevel::TRACE) { loglevel = 3; } options.setDebugLevel(loglevel); if (pomdpQualSettings.isExportSATCallsSet()) { options.setExportSATCalls(pomdpQualSettings.getExportSATCallsPath()); } if (storm::utility::graph::checkIfECWithChoiceExists(pomdp->getTransitionMatrix(), pomdp->getBackwardTransitions(), ~targetStates & ~surelyNotAlmostSurelyReachTarget, storm::storage::BitVector(pomdp->getNumberOfChoices(), true))) { options.lookaheadRequired = true; STORM_LOG_DEBUG("Lookahead required."); } else { options.lookaheadRequired = false; STORM_LOG_DEBUG("No lookahead required."); } if (pomdpSettings.getMemlessSearchMethod() == "ccd16memless") { storm::pomdp::QualitativeStrategySearchNaive memlessSearch(*pomdp, targetObservationSet, targetStates, surelyNotAlmostSurelyReachTarget, smtSolverFactory); memlessSearch.findNewStrategyForSomeState(lookahead); } else if (pomdpSettings.getMemlessSearchMethod() == "iterative") { storm::pomdp::MemlessStrategySearchQualitative memlessSearch(*pomdp, targetObservationSet, targetStates, surelyNotAlmostSurelyReachTarget, smtSolverFactory, options); memlessSearch.findNewStrategyForSomeState(lookahead); memlessSearch.finalizeStatistics(); memlessSearch.getStatistics().print(); } 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 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 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 checker = storm::pomdp::modelchecker::ApproximatePOMDPModelchecker(); double overRes = storm::utility::one(); double underRes = storm::utility::zero(); std::unique_ptr> result; result = checker.computeReachabilityReward(*pomdp, targetObservationSet, rewFormula.getOptimalityType() == storm::OptimizationDirection::Minimize, pomdpSettings.getGridResolution()); overRes = result->OverapproximationValue; underRes = result->UnderapproximationValue; } } 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 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 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); } if (pomdpSettings.isTransformBinarySet() || pomdpSettings.isTransformSimpleSet()) { if (pomdpSettings.isTransformSimpleSet()) { STORM_PRINT_AND_LOG("Transforming the POMDP to a simple POMDP."); pomdp = storm::transformer::BinaryPomdpTransformer().transform(*pomdp, true); } else { STORM_PRINT_AND_LOG("Transforming the POMDP to a binary POMDP."); pomdp = storm::transformer::BinaryPomdpTransformer().transform(*pomdp, false); } pomdp->printModelInformationToStream(std::cout); STORM_PRINT_AND_LOG(" done." << std::endl); } if (pomdpSettings.isExportToParametricSet()) { STORM_PRINT_AND_LOG("Transforming memoryless POMDP to pMC..."); storm::transformer::ApplyFiniteSchedulerToPomdp 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(pmc->as>(),{formula}, storm::storage::BisimulationType::Strong)->as>(); //} STORM_PRINT_AND_LOG(" done." << std::endl); pmc->printModelInformationToStream(std::cout); STORM_PRINT_AND_LOG("Exporting pMC..."); storm::analysis::ConstraintCollector constraints(*pmc); auto const& parameterSet = constraints.getVariables(); std::vector parameters(parameterSet.begin(), parameterSet.end()); std::vector 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); } } else { STORM_LOG_WARN("Nothing to be done. Did you forget to specify a formula?"); } // All operations have now been performed, so we clean up everything and terminate. storm::utility::cleanUp(); return 0; // } catch (storm::exceptions::BaseException const &exception) { // STORM_LOG_ERROR("An exception caused Storm-pomdp to terminate. The message of the exception is: " << exception.what()); // return 1; //} catch (std::exception const &exception) { // STORM_LOG_ERROR("An unexpected exception occurred and caused Storm-pomdp to terminate. The message of this exception is: " << exception.what()); // return 2; //} }