diff --git a/src/abstraction/prism/AbstractCommand.cpp b/src/abstraction/prism/AbstractCommand.cpp index 0f1dc3932..70438d216 100644 --- a/src/abstraction/prism/AbstractCommand.cpp +++ b/src/abstraction/prism/AbstractCommand.cpp @@ -69,8 +69,7 @@ namespace storm { this->recomputeCachedBdd(); } - // Refine bottom state abstractor. - // FIXME: Should this only be done if the other BDD needs recomputation? + // Refine bottom state abstractor. Note that this does not trigger a recomputation yet. bottomStateAbstractor.refine(predicates); } diff --git a/src/abstraction/prism/AbstractProgram.cpp b/src/abstraction/prism/AbstractProgram.cpp index 7e4f7ee21..ee4a46c03 100644 --- a/src/abstraction/prism/AbstractProgram.cpp +++ b/src/abstraction/prism/AbstractProgram.cpp @@ -122,12 +122,18 @@ namespace storm { for (auto const& successorValuePair : lowerChoiceAsAdd) { uint_fast64_t updateIndex = abstractionInformation.decodeAux(successorValuePair.first, 0, currentGame->getProbabilisticBranchingVariables().size()); + std::cout << "update idx: " << updateIndex << std::endl; storm::storage::BitVector successor(abstractionInformation.getNumberOfPredicates()); for (uint_fast64_t index = 0; index < abstractionInformation.getOrderedSuccessorVariables().size(); ++index) { auto const& successorVariable = abstractionInformation.getOrderedSuccessorVariables()[index]; + std::cout << successorVariable.getName() << " has value"; if (successorValuePair.first.getBooleanValue(successorVariable)) { successor.set(index); + std::cout << " true"; + } else { + std::cout << " false"; } + std::cout << std::endl; } result[updateIndex] = successor; @@ -162,11 +168,25 @@ namespace storm { } else { STORM_LOG_TRACE("No bottom state successor. Deriving a new predicate using weakest precondition."); + lowerChoice.template toAdd<ValueType>().exportToDot("lowerchoice_ref.dot"); + upperChoice.template toAdd<ValueType>().exportToDot("upperchoice_ref.dot"); + // Decode both choices to explicit mappings. + std::cout << "lower" << std::endl; std::map<uint_fast64_t, storm::storage::BitVector> lowerChoiceUpdateToSuccessorMapping = decodeChoiceToUpdateSuccessorMapping(lowerChoice); + std::cout << "upper" << std::endl; std::map<uint_fast64_t, storm::storage::BitVector> upperChoiceUpdateToSuccessorMapping = decodeChoiceToUpdateSuccessorMapping(upperChoice); - STORM_LOG_ASSERT(lowerChoiceUpdateToSuccessorMapping.size() == upperChoiceUpdateToSuccessorMapping.size(), "Mismatching sizes after decode."); + STORM_LOG_ASSERT(lowerChoiceUpdateToSuccessorMapping.size() == upperChoiceUpdateToSuccessorMapping.size(), "Mismatching sizes after decode (" << lowerChoiceUpdateToSuccessorMapping.size() << " vs. " << upperChoiceUpdateToSuccessorMapping.size() << ")."); + std::cout << "lower" << std::endl; + for (auto const& entry : lowerChoiceUpdateToSuccessorMapping) { + std::cout << entry.first << " -> " << entry.second << std::endl; + } + std::cout << "upper" << std::endl; + for (auto const& entry : upperChoiceUpdateToSuccessorMapping) { + std::cout << entry.first << " -> " << entry.second << std::endl; + } + // Now go through the mappings and find points of deviation. Currently, we take the first deviation. storm::expressions::Expression newPredicate; auto lowerIt = lowerChoiceUpdateToSuccessorMapping.begin(); @@ -186,6 +206,7 @@ namespace storm { } } } + STORM_LOG_ASSERT(newPredicate.isInitialized(), "Could not derive new predicate as there is no deviation."); STORM_LOG_TRACE("Derived new predicate: " << newPredicate); diff --git a/src/modelchecker/abstraction/GameBasedMdpModelChecker.cpp b/src/modelchecker/abstraction/GameBasedMdpModelChecker.cpp index 15cb2049d..2a7b407f2 100644 --- a/src/modelchecker/abstraction/GameBasedMdpModelChecker.cpp +++ b/src/modelchecker/abstraction/GameBasedMdpModelChecker.cpp @@ -61,13 +61,15 @@ namespace storm { template<storm::dd::DdType Type, typename ValueType> std::unique_ptr<CheckResult> GameBasedMdpModelChecker<Type, ValueType>::computeUntilProbabilities(CheckTask<storm::logic::UntilFormula> const& checkTask) { storm::logic::UntilFormula const& pathFormula = checkTask.getFormula(); - return performGameBasedAbstractionRefinement(checkTask.substituteFormula<storm::logic::Formula>(pathFormula), getExpression(pathFormula.getLeftSubformula()), getExpression(pathFormula.getRightSubformula())); + std::map<std::string, storm::expressions::Expression> labelToExpressionMapping = preprocessedProgram.getLabelToExpressionMapping(); + return performGameBasedAbstractionRefinement(checkTask.substituteFormula<storm::logic::Formula>(pathFormula), pathFormula.getLeftSubformula().toExpression(preprocessedProgram.getManager(), labelToExpressionMapping), pathFormula.getRightSubformula().toExpression(preprocessedProgram.getManager(), labelToExpressionMapping)); } template<storm::dd::DdType Type, typename ValueType> std::unique_ptr<CheckResult> GameBasedMdpModelChecker<Type, ValueType>::computeReachabilityProbabilities(CheckTask<storm::logic::EventuallyFormula> const& checkTask) { storm::logic::EventuallyFormula const& pathFormula = checkTask.getFormula(); - return performGameBasedAbstractionRefinement(checkTask.substituteFormula<storm::logic::Formula>(pathFormula), originalProgram.getManager().boolean(true), getExpression(pathFormula.getSubformula())); + std::map<std::string, storm::expressions::Expression> labelToExpressionMapping = preprocessedProgram.getLabelToExpressionMapping(); + return performGameBasedAbstractionRefinement(checkTask.substituteFormula<storm::logic::Formula>(pathFormula), originalProgram.getManager().boolean(true), pathFormula.getSubformula().toExpression(preprocessedProgram.getManager(), labelToExpressionMapping)); } template<typename ValueType> @@ -195,6 +197,7 @@ namespace storm { template<storm::dd::DdType Type, typename ValueType> void refineAfterQualitativeCheck(storm::abstraction::prism::PrismMenuGameAbstractor<Type, ValueType>& abstractor, storm::abstraction::MenuGame<Type, ValueType> const& game, detail::GameProb01Result<Type> const& prob01, storm::dd::Bdd<Type> const& transitionMatrixBdd) { + STORM_LOG_TRACE("Refining after qualitative check."); // Build the fragment of states that is reachable by any combination of the player 1 and player 2 strategies. storm::dd::Bdd<Type> reachableTransitions = prob01.min.first.getPlayer2Strategy() || prob01.max.second.getPlayer2Strategy(); reachableTransitions = (prob01.min.first.getPlayer1Strategy() && reachableTransitions) || (prob01.max.second.getPlayer1Strategy() && reachableTransitions); @@ -236,7 +239,8 @@ namespace storm { } template<storm::dd::DdType Type, typename ValueType> - void refineAfterQuantitativeCheck(storm::abstraction::prism::PrismMenuGameAbstractor<Type, ValueType>& abstractor, storm::abstraction::MenuGame<Type, ValueType> const& game, std::pair<storm::dd::Bdd<Type>, storm::dd::Bdd<Type>> const& minStrategyPair, std::pair<storm::dd::Bdd<Type>, storm::dd::Bdd<Type>> const& maxStrategyPair, storm::dd::Bdd<Type> const& transitionMatrixBdd) { + void refineAfterQuantitativeCheck(storm::abstraction::prism::PrismMenuGameAbstractor<Type, ValueType>& abstractor, storm::abstraction::MenuGame<Type, ValueType> const& game, storm::dd::Add<Type, ValueType> const& minResult, storm::dd::Add<Type, ValueType> const& maxResult, detail::GameProb01Result<Type> const& prob01, std::pair<storm::dd::Bdd<Type>, storm::dd::Bdd<Type>> const& minStrategyPair, std::pair<storm::dd::Bdd<Type>, storm::dd::Bdd<Type>> const& maxStrategyPair, storm::dd::Bdd<Type> const& transitionMatrixBdd) { + STORM_LOG_TRACE("Refining after quantitative check."); // Build the fragment of states that is reachable by any combination of the player 1 and player 2 strategies. storm::dd::Bdd<Type> reachableTransitions = minStrategyPair.second || maxStrategyPair.second; reachableTransitions = (minStrategyPair.first && reachableTransitions) || (maxStrategyPair.first && reachableTransitions); @@ -251,12 +255,48 @@ namespace storm { // Now that we have the pivot state candidates, we need to pick one. storm::dd::Bdd<Type> pivotState = pickPivotState<Type>(game.getInitialStates(), reachableTransitions, game.getRowVariables(), game.getColumnVariables(), pivotStates); + storm::dd::Add<Type, ValueType> pivotStateLower = pivotState.template toAdd<ValueType>() * minResult; + storm::dd::Add<Type, ValueType> pivotStateUpper = pivotState.template toAdd<ValueType>() * maxResult; + storm::dd::Bdd<Type> pivotStateIsMinProb0 = pivotState && prob01.min.first.states; + storm::dd::Bdd<Type> pivotStateIsMaxProb0 = pivotState && prob01.max.first.states; + storm::dd::Bdd<Type> pivotStateLowerStrategies = pivotState && prob01.min.first.getPlayer1Strategy() && prob01.min.first.getPlayer2Strategy(); + storm::dd::Bdd<Type> pivotStateUpperStrategies = pivotState && prob01.max.first.getPlayer1Strategy() && prob01.max.first.getPlayer2Strategy(); + storm::dd::Bdd<Type> pivotStateLowerPl1Strategy = pivotState && prob01.min.first.getPlayer1Strategy(); + storm::dd::Bdd<Type> pivotStateUpperPl1Strategy = pivotState && prob01.max.first.getPlayer1Strategy(); + storm::dd::Bdd<Type> pivotStateLowerPl2Strategy = pivotState && prob01.min.first.getPlayer2Strategy(); + storm::dd::Bdd<Type> pivotStateUpperPl2Strategy = pivotState && prob01.max.first.getPlayer2Strategy(); + + minResult.exportToDot("minresult.dot"); + maxResult.exportToDot("maxresult.dot"); + pivotState.template toAdd<ValueType>().exportToDot("pivot.dot"); + pivotStateLower.exportToDot("pivot_lower.dot"); + pivotStateUpper.exportToDot("pivot_upper.dot"); + pivotStateIsMinProb0.template toAdd<ValueType>().exportToDot("pivot_is_minprob0.dot"); + pivotStateIsMaxProb0.template toAdd<ValueType>().exportToDot("pivot_is_maxprob0.dot"); + pivotStateLowerStrategies.template toAdd<ValueType>().exportToDot("pivot_lower_strats.dot"); + pivotStateUpperStrategies.template toAdd<ValueType>().exportToDot("pivot_upper_strats.dot"); + pivotStateLowerPl1Strategy.template toAdd<ValueType>().exportToDot("pivot_lower_pl1_strat.dot"); + pivotStateUpperPl1Strategy.template toAdd<ValueType>().exportToDot("pivot_upper_pl1_strat.dot"); + pivotStateLowerPl2Strategy.template toAdd<ValueType>().exportToDot("pivot_lower_pl2_strat.dot"); + pivotStateUpperPl2Strategy.template toAdd<ValueType>().exportToDot("pivot_upper_pl2_strat.dot"); + // Compute the lower and the upper choice for the pivot state. std::set<storm::expressions::Variable> variablesToAbstract = game.getNondeterminismVariables(); variablesToAbstract.insert(game.getRowVariables().begin(), game.getRowVariables().end()); storm::dd::Bdd<Type> lowerChoice = pivotState && game.getExtendedTransitionMatrix().toBdd() && minStrategyPair.first; + (pivotState && minStrategyPair.first).template toAdd<ValueType>().exportToDot("pl1_choice_in_pivot.dot"); + (pivotState && minStrategyPair.first && maxStrategyPair.second).template toAdd<ValueType>().exportToDot("pl1and2_choice_in_pivot.dot"); + (pivotState && maxStrategyPair.second).template toAdd<ValueType>().exportToDot("pl2_choice_in_pivot.dot"); storm::dd::Bdd<Type> lowerChoice1 = (lowerChoice && minStrategyPair.second).existsAbstract(variablesToAbstract); - storm::dd::Bdd<Type> lowerChoice2 = (lowerChoice && maxStrategyPair.second).existsAbstract(variablesToAbstract); + lowerChoice.template toAdd<ValueType>().exportToDot("lower.dot"); + maxStrategyPair.second.template toAdd<ValueType>().exportToDot("max_strat.dot"); + storm::dd::Bdd<Type> lowerChoice2 = (lowerChoice && maxStrategyPair.second); + lowerChoice2.template toAdd<ValueType>().exportToDot("tmp_lower.dot"); + lowerChoice2 = lowerChoice2.existsAbstract(variablesToAbstract); + + lowerChoice.template toAdd<ValueType>().exportToDot("ref_lower.dot"); + lowerChoice1.template toAdd<ValueType>().exportToDot("ref_lower1.dot"); + lowerChoice2.template toAdd<ValueType>().exportToDot("ref_lower2.dot"); bool lowerChoicesDifferent = !lowerChoice1.exclusiveOr(lowerChoice2).isZero(); if (lowerChoicesDifferent) { @@ -377,6 +417,13 @@ namespace storm { return result; } + // 2.5 At this point, we need to fix the max strategy for the prob 0 states for player 2. This is because + // there may be player 2 nodes for which there is no choice in the currently computed strategy (by prob0) + // as no choice achieves probability 0. However, later we require the strategies to only differ if necessary + // so we need to force the player 2 strategy to agree with the player 2 strategy for the probability 0 + // states in the min case. + prob01.max.first.player2Strategy = prob01.max.first.getPlayer2Strategy().existsAbstract(game.getPlayer1Variables()).ite(prob01.max.first.getPlayer2Strategy(), prob01.min.first.getPlayer2Strategy()); + // 3. compute the states for which we know the result/for which we know there is more work to be done. storm::dd::Bdd<Type> maybeMin = !(prob01.min.first.states || prob01.min.second.states) && game.getReachableStates(); storm::dd::Bdd<Type> maybeMax = !(prob01.max.first.states || prob01.max.second.states) && game.getReachableStates(); @@ -465,7 +512,7 @@ namespace storm { maxMaybeStateResult.player1Strategy |= prob01.max.first.getPlayer1Strategy() || prob01.max.second.getPlayer1Strategy(); maxMaybeStateResult.player2Strategy |= prob01.max.first.getPlayer2Strategy() || prob01.max.second.getPlayer2Strategy(); - refineAfterQuantitativeCheck(abstractor, game, std::make_pair(minMaybeStateResult.player1Strategy, minMaybeStateResult.player2Strategy), std::make_pair(maxMaybeStateResult.player1Strategy, maxMaybeStateResult.player2Strategy), transitionMatrixBdd); + refineAfterQuantitativeCheck(abstractor, game, minResult, maxResult, prob01, std::make_pair(minMaybeStateResult.player1Strategy, minMaybeStateResult.player2Strategy), std::make_pair(maxMaybeStateResult.player1Strategy, maxMaybeStateResult.player2Strategy), transitionMatrixBdd); } }