diff --git a/src/storm/builder/DdJaniModelBuilder.cpp b/src/storm/builder/DdJaniModelBuilder.cpp index f5ae8e13a..48496b352 100644 --- a/src/storm/builder/DdJaniModelBuilder.cpp +++ b/src/storm/builder/DdJaniModelBuilder.cpp @@ -1515,7 +1515,7 @@ namespace storm { // Keep track of the fragment of transient assignments. for (auto const& transientAssignment : currentEdge.transientEdgeAssignments) { - addToTransientAssignmentMap(transientAssignments, transientAssignment.first, remainingGuardChoicesIntersection.template toAdd<ValueType>() * transientAssignment.second * indicesEncodedWithLocalNondeterminismVariables[k].first.template toAdd<ValueType>()); + addToTransientAssignmentMap(transientAssignments, transientAssignment.first, remainingGuardChoicesIntersection.template toAdd<ValueType>() * transientAssignment.second * indicesEncodedWithLocalNondeterminismVariables[k].second); } // Keep track of the written global variables of the fragment. diff --git a/src/storm/builder/ExplicitModelBuilder.cpp b/src/storm/builder/ExplicitModelBuilder.cpp index 6467793c6..986e04ae7 100644 --- a/src/storm/builder/ExplicitModelBuilder.cpp +++ b/src/storm/builder/ExplicitModelBuilder.cpp @@ -310,7 +310,7 @@ namespace storm { // Initialize the model components with the obtained information. storm::storage::sparse::ModelComponents<ValueType, RewardModelType> modelComponents(transitionMatrixBuilder.build(0, transitionMatrixBuilder.getCurrentRowGroupCount()), buildStateLabeling(), std::unordered_map<std::string, RewardModelType>(), !generator->isDiscreteTimeModel(), std::move(markovianStates)); - + // Now finalize all reward models. for (auto& rewardModelBuilder : rewardModelBuilders) { modelComponents.rewardModels.emplace(rewardModelBuilder.getName(), rewardModelBuilder.build(modelComponents.transitionMatrix.getRowCount(), modelComponents.transitionMatrix.getColumnCount(), modelComponents.transitionMatrix.getRowGroupCount())); diff --git a/src/storm/counterexamples/SMTMinimalLabelSetGenerator.h b/src/storm/counterexamples/SMTMinimalLabelSetGenerator.h index 854d92500..7210d6e1b 100644 --- a/src/storm/counterexamples/SMTMinimalLabelSetGenerator.h +++ b/src/storm/counterexamples/SMTMinimalLabelSetGenerator.h @@ -44,6 +44,9 @@ namespace storm { // The set of relevant labels. boost::container::flat_set<uint_fast64_t> relevantLabels; + // The set of relevant label sets. + boost::container::flat_set<boost::container::flat_set<uint_fast64_t>> relevantLabelSets; + // The set of labels that matter in terms of minimality. boost::container::flat_set<uint_fast64_t> minimalityLabels; @@ -128,19 +131,17 @@ namespace storm { relevancyInformation.relevantChoicesForRelevantStates.emplace(state, std::list<uint_fast64_t>()); for (uint_fast64_t row = nondeterministicChoiceIndices[state]; row < nondeterministicChoiceIndices[state + 1]; ++row) { - bool currentChoiceRelevant = false; - for (auto const& entry : transitionMatrix.getRow(row)) { // If there is a relevant successor, we need to add the labels of the current choice. if (relevancyInformation.relevantStates.get(entry.getColumn()) || psiStates.get(entry.getColumn())) { + relevancyInformation.relevantChoicesForRelevantStates[state].push_back(row); + for (auto const& label : labelSets[row]) { relevancyInformation.relevantLabels.insert(label); } - - if (!currentChoiceRelevant) { - currentChoiceRelevant = true; - relevancyInformation.relevantChoicesForRelevantStates[state].push_back(row); - } + + relevancyInformation.relevantLabelSets.insert(labelSets[row]); + break; } } } @@ -277,6 +278,43 @@ namespace storm { solver.add(formula); } + static storm::expressions::Expression getOtherSynchronizationEnabledFormula(boost::container::flat_set<uint_fast64_t> const& labelSet, std::map<uint_fast64_t, std::set<boost::container::flat_set<uint_fast64_t>>> const& synchronizingLabels, boost::container::flat_map<boost::container::flat_set<uint_fast64_t>, storm::expressions::Expression> const& labelSetToFormula, VariableInformation const& variableInformation, RelevancyInformation const& relevancyInformation) { + // Taking all commands of a combination does not necessarily mean that a following label set needs to be taken. + // This is because it could be that the commands are taken to enable other synchronizations. Therefore, we need + // to add an additional clause that says that the right-hand side of the implication is also true if all commands + // of the current choice have enabled synchronization options. + if (labelSet.size() > 1) { + storm::expressions::Expression result = variableInformation.manager->boolean(true); + for (auto label : labelSet) { + storm::expressions::Expression alternativeExpressionForLabel = variableInformation.manager->boolean(false); + std::set<boost::container::flat_set<uint_fast64_t>> const& synchsForCommand = synchronizingLabels.at(label); + + for (auto const& synchSet : synchsForCommand) { + storm::expressions::Expression alternativeExpression = variableInformation.manager->boolean(true); + + // If the current synchSet is the same as left-hand side of the implication, we can to skip it. + if (synchSet == labelSet) continue; + + // Build labels that are missing for this synchronization option. + std::set<uint_fast64_t> unknownSynchSetLabels; + std::set_difference(synchSet.begin(), synchSet.end(), relevancyInformation.knownLabels.begin(), relevancyInformation.knownLabels.end(), std::inserter(unknownSynchSetLabels, unknownSynchSetLabels.end())); + + for (auto label : unknownSynchSetLabels) { + alternativeExpression = alternativeExpression && variableInformation.labelVariables.at(variableInformation.labelToIndexMap.at(label)); + } + + alternativeExpressionForLabel = alternativeExpressionForLabel || (alternativeExpression && labelSetToFormula.at(synchSet)); + } + + result = result && alternativeExpressionForLabel; + } + + return result; + } else { + return variableInformation.manager->boolean(false); + } + } + /*! * Asserts cuts that are derived from the explicit representation of the model and rule out a lot of * suboptimal solutions. @@ -285,7 +323,7 @@ namespace storm { * @param context The Z3 context in which to build the expressions. * @param solver The solver to use for the satisfiability evaluation. */ - static void assertExplicitCuts(storm::models::sparse::Model<T> const& model, std::vector<boost::container::flat_set<uint_fast64_t>> const& labelSets, storm::storage::BitVector const& psiStates, VariableInformation const& variableInformation, RelevancyInformation const& relevancyInformation, storm::solver::SmtSolver& solver) { + static void assertCuts(storm::prism::Program& program, storm::models::sparse::Model<T> const& model, std::vector<boost::container::flat_set<uint_fast64_t>> const& labelSets, storm::storage::BitVector const& psiStates, VariableInformation const& variableInformation, RelevancyInformation const& relevancyInformation, storm::solver::SmtSolver& solver) { // Walk through the model and // * identify labels enabled in initial states // * identify labels that can directly precede a given action @@ -294,16 +332,16 @@ namespace storm { boost::container::flat_set<uint_fast64_t> initialLabels; std::set<boost::container::flat_set<uint_fast64_t>> initialCombinations; - std::map<uint_fast64_t, boost::container::flat_set<uint_fast64_t>> precedingLabels; boost::container::flat_set<uint_fast64_t> targetLabels; boost::container::flat_set<boost::container::flat_set<uint_fast64_t>> targetCombinations; + std::map<boost::container::flat_set<uint_fast64_t>, std::set<boost::container::flat_set<uint_fast64_t>>> precedingLabels; std::map<boost::container::flat_set<uint_fast64_t>, std::set<boost::container::flat_set<uint_fast64_t>>> followingLabels; std::map<uint_fast64_t, std::set<boost::container::flat_set<uint_fast64_t>>> synchronizingLabels; // Get some data from the model for convenient access. storm::storage::SparseMatrix<T> const& transitionMatrix = model.getTransitionMatrix(); - storm::storage::BitVector const& initialStates = model.getInitialStates(); storm::storage::SparseMatrix<T> backwardTransitions = model.getBackwardTransitions(); + storm::storage::BitVector const& initialStates = model.getInitialStates(); for (auto currentState : relevancyInformation.relevantStates) { for (auto currentChoice : relevancyInformation.relevantChoicesForRelevantStates.at(currentState)) { @@ -326,6 +364,8 @@ namespace storm { for (auto const& entry : transitionMatrix.getRow(currentChoice)) { if (relevancyInformation.relevantStates.get(entry.getColumn())) { for (auto relevantChoice : relevancyInformation.relevantChoicesForRelevantStates.at(entry.getColumn())) { + if (labelSets[currentChoice] == labelSets[relevantChoice]) continue; + followingLabels[labelSets[currentChoice]].insert(labelSets[relevantChoice]); } } else if (psiStates.get(entry.getColumn())) { @@ -338,250 +378,6 @@ namespace storm { targetLabels.insert(labelSets[currentChoice].begin(), labelSets[currentChoice].end()); targetCombinations.insert(labelSets[currentChoice]); } - } - - // Iterate over predecessors and add all choices that target the current state to the preceding - // label set of all labels of all relevant choices of the current state. - for (auto const& predecessorEntry : backwardTransitions.getRow(currentState)) { - if (relevancyInformation.relevantStates.get(predecessorEntry.getColumn())) { - for (auto predecessorChoice : relevancyInformation.relevantChoicesForRelevantStates.at(predecessorEntry.getColumn())) { - bool choiceTargetsCurrentState = false; - for (auto const& successorEntry : transitionMatrix.getRow(predecessorChoice)) { - if (successorEntry.getColumn() == currentState) { - choiceTargetsCurrentState = true; - } - } - - if (choiceTargetsCurrentState) { - for (auto currentChoice : relevancyInformation.relevantChoicesForRelevantStates.at(currentState)) { - for (auto labelToAdd : labelSets[predecessorChoice]) { - for (auto labelForWhichToAdd : labelSets[currentChoice]) { - precedingLabels[labelForWhichToAdd].insert(labelToAdd); - } - } - } - } - } - } - } - } - - STORM_LOG_DEBUG("Successfully gathered data for explicit cuts."); - - STORM_LOG_DEBUG("Asserting initial combination is taken."); - { - std::vector<storm::expressions::Expression> formulae; - - // Start by asserting that we take at least one initial label. We may do so only if there is no initial - // combination that is already known. Otherwise this condition would be too strong. - bool initialCombinationKnown = false; - for (auto const& combination : initialCombinations) { - boost::container::flat_set<uint_fast64_t> tmpSet; - std::set_difference(combination.begin(), combination.end(), relevancyInformation.knownLabels.begin(), relevancyInformation.knownLabels.end(), std::inserter(tmpSet, tmpSet.end())); - if (tmpSet.size() == 0) { - initialCombinationKnown = true; - break; - } else { - storm::expressions::Expression conj = variableInformation.manager->boolean(true); - for (auto label : tmpSet) { - conj = conj && variableInformation.labelVariables.at(variableInformation.labelToIndexMap.at(label)); - } - formulae.push_back(conj); - } - } - if (!initialCombinationKnown) { - assertDisjunction(solver, formulae, *variableInformation.manager); - } - } - - STORM_LOG_DEBUG("Asserting target combination is taken."); - { - std::vector<storm::expressions::Expression> formulae; - - // Likewise, if no target combination is known, we may assert that there is at least one. - bool targetCombinationKnown = false; - for (auto const& combination : targetCombinations) { - boost::container::flat_set<uint_fast64_t> tmpSet; - std::set_difference(combination.begin(), combination.end(), relevancyInformation.knownLabels.begin(), relevancyInformation.knownLabels.end(), std::inserter(tmpSet, tmpSet.end())); - if (tmpSet.size() == 0) { - targetCombinationKnown = true; - break; - } else { - storm::expressions::Expression conj = variableInformation.manager->boolean(true); - for (auto label : tmpSet) { - conj = conj && variableInformation.labelVariables.at(variableInformation.labelToIndexMap.at(label)); - } - formulae.push_back(conj); - } - } - if (!targetCombinationKnown) { - assertDisjunction(solver, formulae, *variableInformation.manager); - } - } - - // Compute the sets of labels such that the transitions labeled with this set possess at least one known successor. - boost::container::flat_set<boost::container::flat_set<uint_fast64_t>> hasKnownSuccessor; - for (auto const& labelSetFollowingSetsPair : followingLabels) { - for (auto const& set : labelSetFollowingSetsPair.second) { - if (std::includes(relevancyInformation.knownLabels.begin(), relevancyInformation.knownLabels.end(), set.begin(), set.end())) { - hasKnownSuccessor.insert(set); - break; - } - } - } - - STORM_LOG_DEBUG("Asserting taken labels are followed by another label if they are not a target label."); - // Now assert that for each non-target label, we take a following label. - for (auto const& labelSetFollowingSetsPair : followingLabels) { - std::vector<storm::expressions::Expression> formulae; - - // Only build a constraint if the combination does not lead to a target state and - // no successor set is already known. - if (targetCombinations.find(labelSetFollowingSetsPair.first) == targetCombinations.end() && hasKnownSuccessor.find(labelSetFollowingSetsPair.first) == hasKnownSuccessor.end()) { - - // Compute the set of unknown labels on the left-hand side of the implication. - boost::container::flat_set<uint_fast64_t> unknownLhsLabels; - std::set_difference(labelSetFollowingSetsPair.first.begin(), labelSetFollowingSetsPair.first.end(), relevancyInformation.knownLabels.begin(), relevancyInformation.knownLabels.end(), std::inserter(unknownLhsLabels, unknownLhsLabels.end())); - for (auto label : unknownLhsLabels) { - formulae.push_back(!variableInformation.labelVariables.at(variableInformation.labelToIndexMap.at(label))); - } - - for (auto const& followingSet : labelSetFollowingSetsPair.second) { - boost::container::flat_set<uint_fast64_t> tmpSet; - - // Check which labels of the current following set are not known. This set must be non-empty, because - // otherwise a successor combination would already be known and control cannot reach this point. - std::set_difference(followingSet.begin(), followingSet.end(), relevancyInformation.knownLabels.begin(), relevancyInformation.knownLabels.end(), std::inserter(tmpSet, tmpSet.end())); - - // Construct an expression that enables all unknown labels of the current following set. - storm::expressions::Expression conj = variableInformation.manager->boolean(true); - for (auto label : tmpSet) { - conj = conj && variableInformation.labelVariables.at(variableInformation.labelToIndexMap.at(label)); - } - formulae.push_back(conj); - } - - if (labelSetFollowingSetsPair.first.size() > 1) { - // Taking all commands of a combination does not necessarily mean that a following label set needs to be taken. - // This is because it could be that the commands are taken to enable other synchronizations. Therefore, we need - // to add an additional clause that says that the right-hand side of the implication is also true if all commands - // of the current choice have enabled synchronization options. - storm::expressions::Expression finalDisjunct = variableInformation.manager->boolean(true); - for (auto label : labelSetFollowingSetsPair.first) { - storm::expressions::Expression alternativeExpressionForLabel = variableInformation.manager->boolean(false); - std::set<boost::container::flat_set<uint_fast64_t>> const& synchsForCommand = synchronizingLabels.at(label); - - for (auto const& synchSet : synchsForCommand) { - storm::expressions::Expression alternativeExpression = variableInformation.manager->boolean(true); - - // If the current synchSet is the same as left-hand side of the implication, we need to skip it. - if (synchSet == labelSetFollowingSetsPair.first) continue; - - // Now that we have the labels that are unknown and "missing", we still need to check whether this other - // synchronizing set already has a known successor or leads directly to a target state. - if (hasKnownSuccessor.find(synchSet) == hasKnownSuccessor.end() && targetCombinations.find(synchSet) == targetCombinations.end()) { - // If not, we can assert that we take one of its possible successors. - boost::container::flat_set<uint_fast64_t> unknownSynchs; - std::set_difference(synchSet.begin(), synchSet.end(), relevancyInformation.knownLabels.begin(), relevancyInformation.knownLabels.end(), std::inserter(unknownSynchs, unknownSynchs.end())); - unknownSynchs.erase(label); - - for (auto label : unknownSynchs) { - alternativeExpression = alternativeExpression && variableInformation.labelVariables.at(variableInformation.labelToIndexMap.at(label)); - } - - storm::expressions::Expression disjunctionOverSuccessors = variableInformation.manager->boolean(false); - for (auto successorSet : followingLabels.at(synchSet)) { - storm::expressions::Expression conjunctionOverLabels = variableInformation.manager->boolean(true); - for (auto label : successorSet) { - if (relevancyInformation.knownLabels.find(label) == relevancyInformation.knownLabels.end()) { - conjunctionOverLabels = conjunctionOverLabels && variableInformation.labelVariables.at(variableInformation.labelToIndexMap.at(label)); - } - } - disjunctionOverSuccessors = disjunctionOverSuccessors || conjunctionOverLabels; - } - - alternativeExpression = alternativeExpression && disjunctionOverSuccessors; - } - - alternativeExpressionForLabel = alternativeExpressionForLabel || alternativeExpression; - } - - finalDisjunct = finalDisjunct && alternativeExpressionForLabel; - } - - formulae.push_back(finalDisjunct); - } - - if (formulae.size() > 0) { - assertDisjunction(solver, formulae, *variableInformation.manager); - } - } - } - - STORM_LOG_DEBUG("Asserting synchronization cuts."); - // Finally, assert that if we take one of the synchronizing labels, we also take one of the combinations - // the label appears in. - for (auto const& labelSynchronizingSetsPair : synchronizingLabels) { - std::vector<storm::expressions::Expression> formulae; - - if (relevancyInformation.knownLabels.find(labelSynchronizingSetsPair.first) == relevancyInformation.knownLabels.end()) { - formulae.push_back(!variableInformation.labelVariables.at(variableInformation.labelToIndexMap.at(labelSynchronizingSetsPair.first))); - } - - // We need to be careful, because there may be one synchronisation set out of which all labels are - // known, which means we must not assert anything. - bool allImplicantsKnownForOneSet = false; - for (auto const& synchronizingSet : labelSynchronizingSetsPair.second) { - storm::expressions::Expression currentCombination = variableInformation.manager->boolean(true); - bool allImplicantsKnownForCurrentSet = true; - for (auto label : synchronizingSet) { - if (relevancyInformation.knownLabels.find(label) == relevancyInformation.knownLabels.end() && label != labelSynchronizingSetsPair.first) { - currentCombination = currentCombination && variableInformation.labelVariables.at(variableInformation.labelToIndexMap.at(label)); - } - } - formulae.push_back(currentCombination); - - // If all implicants of the current set are known, we do not need to further build the constraint. - if (allImplicantsKnownForCurrentSet) { - allImplicantsKnownForOneSet = true; - break; - } - } - - if (!allImplicantsKnownForOneSet) { - assertDisjunction(solver, formulae, *variableInformation.manager); - } - } - } - - /*! - * Asserts cuts that are derived from the symbolic representation of the model and rule out a lot of - * suboptimal solutions. - * - * @param program The symbolic representation of the model in terms of a program. - * @param solver The solver to use for the satisfiability evaluation. - */ - static void assertSymbolicCuts(storm::prism::Program& program, storm::models::sparse::Model<T> const& model, std::vector<boost::container::flat_set<uint_fast64_t>> const& labelSets, VariableInformation const& variableInformation, RelevancyInformation const& relevancyInformation, storm::solver::SmtSolver& solver) { - // A container storing the label sets that may precede a given label set. - std::map<boost::container::flat_set<uint_fast64_t>, std::set<boost::container::flat_set<uint_fast64_t>>> precedingLabelSets; - - // A container that maps labels to their reachable synchronization sets. - std::map<uint_fast64_t, std::set<boost::container::flat_set<uint_fast64_t>>> synchronizingLabels; - - // Get some data from the model for convenient access. - storm::storage::SparseMatrix<T> const& transitionMatrix = model.getTransitionMatrix(); - storm::storage::SparseMatrix<T> backwardTransitions = model.getBackwardTransitions(); - - // Compute the set of labels that may precede a given action. - for (auto currentState : relevancyInformation.relevantStates) { - for (auto currentChoice : relevancyInformation.relevantChoicesForRelevantStates.at(currentState)) { - - // If the choice is a synchronization choice, we need to record it. - if (labelSets[currentChoice].size() > 1) { - for (auto label : labelSets[currentChoice]) { - synchronizingLabels[label].emplace(labelSets[currentChoice]); - } - } // Iterate over predecessors and add all choices that target the current state to the preceding // label set of all labels of all relevant choices of the current state. @@ -596,7 +392,7 @@ namespace storm { } if (choiceTargetsCurrentState) { - precedingLabelSets[labelSets.at(currentChoice)].insert(labelSets.at(predecessorChoice)); + precedingLabels[labelSets.at(currentChoice)].insert(labelSets.at(predecessorChoice)); } } } @@ -608,7 +404,7 @@ namespace storm { // cuts. std::unique_ptr<storm::solver::SmtSolver> localSolver(new storm::solver::Z3SmtSolver(program.getManager())); storm::expressions::ExpressionManager const& localManager = program.getManager(); - + // Then add the constraints for bounds of the integer variables.. for (auto const& integerVariable : program.getGlobalIntegerVariables()) { localSolver->add(integerVariable.getExpressionVariable() >= integerVariable.getLowerBoundExpression()); @@ -628,8 +424,7 @@ namespace storm { std::map<boost::container::flat_set<uint_fast64_t>, std::set<boost::container::flat_set<uint_fast64_t>>> backwardImplications; // Now check for possible backward cuts. - for (auto const& labelSetAndPrecedingLabelSetsPair : precedingLabelSets) { - + for (auto const& labelSetAndPrecedingLabelSetsPair : precedingLabels) { // Find out the commands for the currently considered label set. std::vector<std::reference_wrapper<storm::prism::Command const>> currentCommandVector; for (uint_fast64_t moduleIndex = 0; moduleIndex < program.getNumberOfModules(); ++moduleIndex) { @@ -637,7 +432,7 @@ namespace storm { for (uint_fast64_t commandIndex = 0; commandIndex < module.getNumberOfCommands(); ++commandIndex) { storm::prism::Command const& command = module.getCommand(commandIndex); - + // If the current command is one of the commands we need to consider, store a reference to it in the container. if (labelSetAndPrecedingLabelSetsPair.first.find(command.getGlobalIndex()) != labelSetAndPrecedingLabelSetsPair.first.end()) { currentCommandVector.push_back(command); @@ -657,7 +452,13 @@ namespace storm { storm::solver::SmtSolver::CheckResult checkResult = localSolver->check(); localSolver->pop(); localSolver->push(); - + +// std::cout << "combi" << std::endl; +// for (auto const& e : labelSetAndPrecedingLabelSetsPair.first) { +// std::cout << e << ", "; +// } +// std::cout << std::endl; + // If the solver reports unsat, then we know that the current selection is not enabled in the initial state. if (checkResult == storm::solver::SmtSolver::CheckResult::Unsat) { STORM_LOG_DEBUG("Selection not enabled in initial state."); @@ -680,21 +481,21 @@ namespace storm { STORM_LOG_ASSERT(false, "Choice label set is empty."); } - STORM_LOG_DEBUG("About to assert disjunction of negated guards."); - storm::expressions::Expression guardExpression = localManager.boolean(false); - bool firstAssignment = true; - for (auto const& command : currentCommandVector) { - if (firstAssignment) { - guardExpression = !command.get().getGuardExpression(); - } else { - guardExpression = guardExpression || !command.get().getGuardExpression(); - } - } - localSolver->add(guardExpression); + STORM_LOG_DEBUG("About to assert that combination is not enabled in the current state."); +// std::cout << "negated guard expr " << !guardConjunction << std::endl; + localSolver->add(!guardConjunction); STORM_LOG_DEBUG("Asserted disjunction of negated guards."); // Now check the possible preceding label sets for the essential ones. for (auto const& precedingLabelSet : labelSetAndPrecedingLabelSetsPair.second) { + if (labelSetAndPrecedingLabelSetsPair.first == precedingLabelSet) continue; + +// std::cout << "new preceeding label set" << std::endl; +// for (auto const& e : precedingLabelSet) { +// std::cout << e << ", "; +// } +// std::cout << std::endl; + // Create a restore point so we can easily pop-off all weakest precondition expressions. localSolver->push(); @@ -715,6 +516,7 @@ namespace storm { // Assert all the guards of the preceding command set. for (auto const& command : currentPrecedingCommandVector) { +// std::cout << "command guard " << command.get().getGuardExpression() << std::endl; localSolver->add(command.get().getGuardExpression()); } @@ -736,6 +538,7 @@ namespace storm { STORM_LOG_DEBUG("About to assert a weakest precondition."); storm::expressions::Expression wp = guardConjunction.substitute(currentUpdateCombinationMap); +// std::cout << "wp: " << wp << std::endl; formulae.push_back(wp); STORM_LOG_DEBUG("Asserted weakest precondition."); @@ -756,53 +559,160 @@ namespace storm { done = true; } } - + // Now assert the disjunction of all weakest preconditions of all considered update combinations. assertDisjunction(*localSolver, formulae, localManager); STORM_LOG_DEBUG("Asserted disjunction of all weakest preconditions."); if (localSolver->check() == storm::solver::SmtSolver::CheckResult::Sat) { +// std::cout << "sat" << std::endl; backwardImplications[labelSetAndPrecedingLabelSetsPair.first].insert(precedingLabelSet); } +// else { +// std::cout << "unsat" << std::endl; +// } localSolver->pop(); } // Popping the disjunction of negated guards from the solver stack. localSolver->pop(); + } else { + STORM_LOG_DEBUG("Selection is enabled in initial state."); } } - // Compute the sets of labels such that the transitions labeled with this set possess at least one known successor. + // Compute the sets of labels such that the transitions labeled with this set possess at least one known label. + boost::container::flat_set<boost::container::flat_set<uint_fast64_t>> hasKnownSuccessor; + for (auto const& labelSetFollowingSetsPair : followingLabels) { + for (auto const& set : labelSetFollowingSetsPair.second) { + if (std::includes(relevancyInformation.knownLabels.begin(), relevancyInformation.knownLabels.end(), set.begin(), set.end())) { + hasKnownSuccessor.insert(set); + break; + } + } + } + + // Compute the sets of labels such that the transitions labeled with this set possess at least one known predecessor. boost::container::flat_set<boost::container::flat_set<uint_fast64_t>> hasKnownPredecessor; for (auto const& labelSetImplicationsPair : backwardImplications) { for (auto const& set : labelSetImplicationsPair.second) { if (std::includes(relevancyInformation.knownLabels.begin(), relevancyInformation.knownLabels.end(), set.begin(), set.end())) { - // Should this be labelSetImplicationsPair.first??? - // hasKnownPredecessor.insert(set); hasKnownPredecessor.insert(labelSetImplicationsPair.first); break; } } } + - STORM_LOG_DEBUG("Asserting taken labels are preceded by another label if they are not an initial label."); - // Now assert that for each non-target label, we take a following label. - for (auto const& labelSetImplicationsPair : backwardImplications) { + STORM_LOG_DEBUG("Successfully gathered data for cuts."); + + STORM_LOG_DEBUG("Asserting initial combination is taken."); + { std::vector<storm::expressions::Expression> formulae; - // Only build a constraint if the combination no predecessor set is already known. - if (hasKnownPredecessor.find(labelSetImplicationsPair.first) == hasKnownPredecessor.end()) { - - // Compute the set of unknown labels on the left-hand side of the implication. - boost::container::flat_set<uint_fast64_t> unknownLhsLabels; - std::set_difference(labelSetImplicationsPair.first.begin(), labelSetImplicationsPair.first.end(), relevancyInformation.knownLabels.begin(), relevancyInformation.knownLabels.end(), std::inserter(unknownLhsLabels, unknownLhsLabels.end())); - for (auto label : unknownLhsLabels) { - formulae.push_back(!variableInformation.labelVariables.at(variableInformation.labelToIndexMap.at(label))); + // Start by asserting that we take at least one initial label. We may do so only if there is no initial + // combination that is already known. Otherwise this condition would be too strong. + bool initialCombinationKnown = false; + for (auto const& combination : initialCombinations) { + boost::container::flat_set<uint_fast64_t> tmpSet; + std::set_difference(combination.begin(), combination.end(), relevancyInformation.knownLabels.begin(), relevancyInformation.knownLabels.end(), std::inserter(tmpSet, tmpSet.end())); + if (tmpSet.size() == 0) { + initialCombinationKnown = true; + break; + } else { + storm::expressions::Expression conj = variableInformation.manager->boolean(true); + for (auto label : tmpSet) { + conj = conj && variableInformation.labelVariables.at(variableInformation.labelToIndexMap.at(label)); + } + formulae.push_back(conj); } + } + if (!initialCombinationKnown) { + assertDisjunction(solver, formulae, *variableInformation.manager); + } + } + + STORM_LOG_DEBUG("Asserting target combination is taken."); + { + std::vector<storm::expressions::Expression> formulae; + + // Likewise, if no target combination is known, we may assert that there is at least one. + bool targetCombinationKnown = false; + for (auto const& combination : targetCombinations) { + boost::container::flat_set<uint_fast64_t> tmpSet; + std::set_difference(combination.begin(), combination.end(), relevancyInformation.knownLabels.begin(), relevancyInformation.knownLabels.end(), std::inserter(tmpSet, tmpSet.end())); + if (tmpSet.size() == 0) { + targetCombinationKnown = true; + break; + } else { + storm::expressions::Expression conj = variableInformation.manager->boolean(true); + for (auto label : tmpSet) { + conj = conj && variableInformation.labelVariables.at(variableInformation.labelToIndexMap.at(label)); + } + formulae.push_back(conj); + } + } + if (!targetCombinationKnown) { + assertDisjunction(solver, formulae, *variableInformation.manager); + } + } + + STORM_LOG_DEBUG("Asserting taken labels are followed and preceeded by another label if they are not a target label or an initial label, respectively."); + boost::container::flat_map<boost::container::flat_set<uint_fast64_t>, storm::expressions::Expression> labelSetToFormula; + for (auto const& labelSet : relevancyInformation.relevantLabelSets) { + storm::expressions::Expression labelSetFormula = variableInformation.manager->boolean(false); + + // Compute the set of unknown labels on the left-hand side of the implication. + boost::container::flat_set<uint_fast64_t> unknownLhsLabels; + std::set_difference(labelSet.begin(), labelSet.end(), relevancyInformation.knownLabels.begin(), relevancyInformation.knownLabels.end(), std::inserter(unknownLhsLabels, unknownLhsLabels.end())); + for (auto label : unknownLhsLabels) { + labelSetFormula = labelSetFormula || !variableInformation.labelVariables.at(variableInformation.labelToIndexMap.at(label)); + } + + // Only build a constraint if the combination does not lead to a target state and + // no successor set is already known. + storm::expressions::Expression successorExpression; + if (targetCombinations.find(labelSet) == targetCombinations.end() && hasKnownSuccessor.find(labelSet) == hasKnownSuccessor.end()) { + successorExpression = variableInformation.manager->boolean(false); + + auto const& followingLabelSets = followingLabels.at(labelSet); + + for (auto const& followingSet : followingLabelSets) { + boost::container::flat_set<uint_fast64_t> tmpSet; + + // Check which labels of the current following set are not known. This set must be non-empty, because + // otherwise a successor combination would already be known and control cannot reach this point. + std::set_difference(followingSet.begin(), followingSet.end(), relevancyInformation.knownLabels.begin(), relevancyInformation.knownLabels.end(), std::inserter(tmpSet, tmpSet.end())); + + // Construct an expression that enables all unknown labels of the current following set. + storm::expressions::Expression conj = variableInformation.manager->boolean(true); + for (auto label : tmpSet) { + conj = conj && variableInformation.labelVariables.at(variableInformation.labelToIndexMap.at(label)); + } + successorExpression = successorExpression || conj; + } + } else { + successorExpression = variableInformation.manager->boolean(true); + } + // Constructed following cuts at this point. + + // Only build a constraint if the combination is no initial combination and no + // predecessor set is already known. + storm::expressions::Expression predecessorExpression; + if (initialCombinations.find(labelSet) == initialCombinations.end() && hasKnownPredecessor.find(labelSet) == hasKnownPredecessor.end()) { + predecessorExpression = variableInformation.manager->boolean(false); - for (auto const& preceedingSet : labelSetImplicationsPair.second) { +// std::cout << "labelSet" << std::endl; +// for (auto const& e : labelSet) { +// std::cout << e << ", "; +// } +// std::cout << std::endl; + + auto const& preceedingLabelSets = backwardImplications.at(labelSet); + + for (auto const& preceedingSet : preceedingLabelSets) { boost::container::flat_set<uint_fast64_t> tmpSet; // Check which labels of the current following set are not known. This set must be non-empty, because @@ -814,67 +724,54 @@ namespace storm { for (auto label : tmpSet) { conj = conj && variableInformation.labelVariables.at(variableInformation.labelToIndexMap.at(label)); } - formulae.push_back(conj); + predecessorExpression = predecessorExpression || conj; } - - if (labelSetImplicationsPair.first.size() > 1) { - // Taking all commands of a combination does not necessarily mean that a predecessor label set needs to be taken. - // This is because it could be that the commands are taken to enable other synchronizations. Therefore, we need - // to add an additional clause that says that the right-hand side of the implication is also true if all commands - // of the current choice have enabled synchronization options. - storm::expressions::Expression finalDisjunct = variableInformation.manager->boolean(false); - for (auto label : labelSetImplicationsPair.first) { - storm::expressions::Expression alternativeExpressionForLabel = variableInformation.manager->boolean(false); - std::set<boost::container::flat_set<uint_fast64_t>> const& synchsForCommand = synchronizingLabels.at(label); - - for (auto const& synchSet : synchsForCommand) { - storm::expressions::Expression alternativeExpression = variableInformation.manager->boolean(true); - - // If the current synchSet is the same as left-hand side of the implication, we need to skip it. - if (synchSet == labelSetImplicationsPair.first) continue; - - // Now that we have the labels that are unknown and "missing", we still need to check whether this other - // synchronizing set already has a known predecessor. - if (hasKnownPredecessor.find(synchSet) == hasKnownPredecessor.end()) { - // If not, we can assert that we take one of its possible predecessors. - boost::container::flat_set<uint_fast64_t> unknownSynchs; - std::set_difference(synchSet.begin(), synchSet.end(), relevancyInformation.knownLabels.begin(), relevancyInformation.knownLabels.end(), std::inserter(unknownSynchs, unknownSynchs.end())); - unknownSynchs.erase(label); - - for (auto label : unknownSynchs) { - alternativeExpression = alternativeExpression && variableInformation.labelVariables.at(variableInformation.labelToIndexMap.at(label)); - } - - storm::expressions::Expression disjunctionOverPredecessors = variableInformation.manager->boolean(false); - auto precedingLabelSetsIterator = precedingLabelSets.find(synchSet); - if (precedingLabelSetsIterator != precedingLabelSets.end()) { - for (auto preceedingSet : precedingLabelSetsIterator->second) { - storm::expressions::Expression conjunctionOverLabels = variableInformation.manager->boolean(true); - for (auto label : preceedingSet) { - if (relevancyInformation.knownLabels.find(label) == relevancyInformation.knownLabels.end()) { - conjunctionOverLabels = conjunctionOverLabels && variableInformation.labelVariables.at(variableInformation.labelToIndexMap.at(label)); - } - } - disjunctionOverPredecessors = disjunctionOverPredecessors || conjunctionOverLabels; - } - } - - alternativeExpression = alternativeExpression && disjunctionOverPredecessors; - } - - alternativeExpressionForLabel = alternativeExpressionForLabel || alternativeExpression; - } - - finalDisjunct = finalDisjunct && alternativeExpressionForLabel; - } + } else { + predecessorExpression = variableInformation.manager->boolean(true); + } + + labelSetFormula = labelSetFormula || (successorExpression && predecessorExpression); + + labelSetToFormula[labelSet] = labelSetFormula; + } + + for (auto const& labelSetFormula : labelSetToFormula) { + solver.add(labelSetFormula.second || getOtherSynchronizationEnabledFormula(labelSetFormula.first, synchronizingLabels, labelSetToFormula, variableInformation, relevancyInformation)); + } - formulae.push_back(finalDisjunct); + STORM_LOG_DEBUG("Asserting synchronization cuts."); + // Finally, assert that if we take one of the synchronizing labels, we also take one of the combinations + // the label appears in. + for (auto const& labelSynchronizingSetsPair : synchronizingLabels) { + std::vector<storm::expressions::Expression> formulae; + + if (relevancyInformation.knownLabels.find(labelSynchronizingSetsPair.first) == relevancyInformation.knownLabels.end()) { + formulae.push_back(!variableInformation.labelVariables.at(variableInformation.labelToIndexMap.at(labelSynchronizingSetsPair.first))); + } + + // We need to be careful, because there may be one synchronisation set out of which all labels are + // known, which means we must not assert anything. + bool allImplicantsKnownForOneSet = false; + for (auto const& synchronizingSet : labelSynchronizingSetsPair.second) { + storm::expressions::Expression currentCombination = variableInformation.manager->boolean(true); + bool allImplicantsKnownForCurrentSet = true; + for (auto label : synchronizingSet) { + if (relevancyInformation.knownLabels.find(label) == relevancyInformation.knownLabels.end() && label != labelSynchronizingSetsPair.first) { + currentCombination = currentCombination && variableInformation.labelVariables.at(variableInformation.labelToIndexMap.at(label)); + } } - - if (formulae.size() > 0) { - assertDisjunction(solver, formulae, *variableInformation.manager); + formulae.push_back(currentCombination); + + // If all implicants of the current set are known, we do not need to further build the constraint. + if (allImplicantsKnownForCurrentSet) { + allImplicantsKnownForOneSet = true; + break; } } + + if (!allImplicantsKnownForOneSet) { + assertDisjunction(solver, formulae, *variableInformation.manager); + } } } @@ -1353,6 +1250,10 @@ namespace storm { // try with an increased bound. while (solver.checkWithAssumptions({assumption}) == storm::solver::SmtSolver::CheckResult::Unsat) { STORM_LOG_DEBUG("Constraint system is unsatisfiable with at most " << currentBound << " taken commands; increasing bound."); +#ifndef NDEBUG + STORM_LOG_DEBUG("Sanity check to see whether constraint system is still satisfiable."); + STORM_LOG_ASSERT(solver.check() == storm::solver::SmtSolver::CheckResult::Sat, "Constraint system is not satisfiable anymore."); +#endif solver.add(variableInformation.auxiliaryVariables.back()); variableInformation.auxiliaryVariables.push_back(assertLessOrEqualKRelaxed(solver, variableInformation, ++currentBound)); assumption = !variableInformation.auxiliaryVariables.back(); @@ -1729,10 +1630,8 @@ namespace storm { // (6) Add constraints that cut off a lot of suboptimal solutions. STORM_LOG_DEBUG("Asserting cuts."); - assertExplicitCuts(model, labelSets, psiStates, variableInformation, relevancyInformation, *solver); - STORM_LOG_DEBUG("Asserted explicit cuts."); - assertSymbolicCuts(program, model, labelSets, variableInformation, relevancyInformation, *solver); - STORM_LOG_DEBUG("Asserted symbolic cuts."); + assertCuts(program, model, labelSets, psiStates, variableInformation, relevancyInformation, *solver); + STORM_LOG_DEBUG("Asserted cuts."); if (includeReachabilityEncoding) { assertReachabilityCuts(model, labelSets, psiStates, variableInformation, relevancyInformation, *solver); STORM_LOG_DEBUG("Asserted reachability cuts.");