diff --git a/src/builder/jit/ExplicitJitJaniModelBuilder.cpp b/src/builder/jit/ExplicitJitJaniModelBuilder.cpp index 6efb68661..d77fab585 100644 --- a/src/builder/jit/ExplicitJitJaniModelBuilder.cpp +++ b/src/builder/jit/ExplicitJitJaniModelBuilder.cpp @@ -158,6 +158,57 @@ namespace storm { private: std::queue storage; }; + + {% for edge in nonSynchronizingEdges %}static bool enabled_{$edge.name}(StateType const& state) { + if ({$edge.guard}) { + return true; + } + return false; + } + + static void perform_{$edge.name}(StateType const& state) { + + } + + static void lowestLevel_{$edge.name}() { + return {$edge.lowestLevel}; + } + + static void highestLevel_{$edge.name}() { + return {$edge.highestLevel}; + } + {% endfor %} + + + {% for edge in nonSynchronizingEdges %}class Edge_{$edge.name} { + public: + bool isEnabled(StateType const& state) { + if ({$edge.guard}) { + return true; + } + return false; + } + + {% for destination in edge.destinations %}class Destination_{$destination.name} { + public: + int_fast64_t lowestLevel() const { + return {$destination.lowestLevel}; + } + + int_fast64_t highestLevel() const { + return {$destination.highestLevel}; + } + + void performAssignments(int_fast64_t level, StateType& state) { + {% for level in destination.levels %}if (level == {$level.index}) { + {% for assignment in level.nonTransientAssignments %}state.{$assignment.variable} = {$assignment.value}; + {% endfor %} + }{% endfor %} + } + }; + {% endfor %} + }; + {% endfor %} class JitBuilder : public JitModelBuilderInterface { public: @@ -166,8 +217,7 @@ namespace storm { StateType state; {% for assignment in state %}state.{$assignment.variable} = {$assignment.value}; {% endfor %}initialStates.push_back(state); - } - {% endfor %} + }{% endfor %} } virtual storm::models::sparse::Model>* build() override { @@ -243,22 +293,12 @@ namespace storm { {% for expression in terminalExpressions %}if ({$expression}) { return true; } + {% endfor %} return false; } void exploreNonSynchronizingEdges(StateType const& sourceState, IndexType const& currentIndex, StateBehaviour& behaviour, StateSet& statesToExplore) { - {% for edge in nonSynchronizingEdges %}if ({$edge.guard}) { - Choice& choice = behaviour.addChoice(); - {% for destination in edge.destinations %}{ - StateType targetState(sourceState); - {% for assignment in destination.nonTransientAssignments %}targetState.{$assignment.variable} = {$assignment.value}; - {% endfor %} - IndexType targetStateIndex = getOrAddIndex(targetState, statesToExplore); - choice.add(targetStateIndex, {$destination.value}); - } - {% endfor %} - } - {% endfor %} + } IndexType getOrAddIndex(StateType const& state, StateSet& statesToExplore) { @@ -444,7 +484,14 @@ namespace storm { std::string variableName = getQualifiedVariableName(variable); variableToName[variable.getExpressionVariable()] = variableName; - uint64_t range = static_cast(variable.getUpperBound().evaluateAsInt() - variable.getLowerBound().evaluateAsInt() + 1); + int_fast64_t lowerBound = variable.getLowerBound().evaluateAsInt(); + int_fast64_t upperBound = variable.getUpperBound().evaluateAsInt(); + + lowerBounds[variable.getExpressionVariable()] = lowerBound; + if (lowerBound != 0) { + lowerBoundShiftSubstitution[variable.getExpressionVariable()] = variable.getExpressionVariable() + model.getManager().integer(lowerBound); + } + uint64_t range = static_cast(upperBound - lowerBound + 1); uint64_t numberOfBits = static_cast(std::ceil(std::log2(range))); boundedIntegerVariable["name"] = variableName; @@ -463,8 +510,15 @@ namespace storm { cpptempl::data_map boundedIntegerVariable; std::string variableName = getQualifiedVariableName(automaton, variable); variableToName[variable.getExpressionVariable()] = variableName; - - uint64_t range = static_cast(variable.getUpperBound().evaluateAsInt() - variable.getLowerBound().evaluateAsInt()); + + int_fast64_t lowerBound = variable.getLowerBound().evaluateAsInt(); + int_fast64_t upperBound = variable.getUpperBound().evaluateAsInt(); + + lowerBounds[variable.getExpressionVariable()] = lowerBound; + if (lowerBound != 0) { + lowerBoundShiftSubstitution[variable.getExpressionVariable()] = variable.getExpressionVariable() + model.getManager().integer(lowerBound); + } + uint64_t range = static_cast(upperBound - lowerBound); uint64_t numberOfBits = static_cast(std::ceil(std::log2(range))); boundedIntegerVariable["name"] = variableName; @@ -499,7 +553,7 @@ namespace storm { if (this->options.isBuildAllLabelsSet() || this->options.getLabelNames().find(variable->getName()) != this->options.getLabelNames().end()) { cpptempl::data_map label; label["name"] = variable->getName(); - label["predicate"] = expressionTranslator.translate(model.getLabelExpression(variable->asBooleanVariable(), locationVariables), storm::expressions::ToCppTranslationOptions("state.")); + label["predicate"] = expressionTranslator.translate(shiftVariablesWrtLowerBound(model.getLabelExpression(variable->asBooleanVariable(), locationVariables)), storm::expressions::ToCppTranslationOptions("state.")); labels.push_back(label); } } @@ -508,7 +562,7 @@ namespace storm { for (auto const& expression : this->options.getExpressionLabels()) { cpptempl::data_map label; label["name"] = expression.toString(); - label["predicate"] = expressionTranslator.translate(expression, storm::expressions::ToCppTranslationOptions("state.")); + label["predicate"] = expressionTranslator.translate(shiftVariablesWrtLowerBound(expression), storm::expressions::ToCppTranslationOptions("state.")); labels.push_back(label); } @@ -531,10 +585,10 @@ namespace storm { if (terminalEntry.second) { labelExpression = !labelExpression; } - terminalExpressions.push_back(expressionTranslator.translate(labelExpression, storm::expressions::ToCppTranslationOptions("state."))); + terminalExpressions.push_back(expressionTranslator.translate(shiftVariablesWrtLowerBound(labelExpression), storm::expressions::ToCppTranslationOptions("state."))); } else { auto expression = terminalEntry.second ? labelOrExpression.getExpression() : !labelOrExpression.getExpression(); - terminalExpressions.push_back(expressionTranslator.translate(expression, storm::expressions::ToCppTranslationOptions("state."))); + terminalExpressions.push_back(expressionTranslator.translate(shiftVariablesWrtLowerBound(expression), storm::expressions::ToCppTranslationOptions("state."))); } } @@ -545,10 +599,12 @@ namespace storm { cpptempl::data_list ExplicitJitJaniModelBuilder::generateNonSynchronizingEdges() { cpptempl::data_list edges; for (auto const& automaton : this->model.getAutomata()) { + uint64_t edgeIndex = 0; for (auto const& edge : automaton.getEdges()) { if (edge.getActionIndex() == storm::jani::Model::SILENT_ACTION_INDEX) { - edges.push_back(generateEdge(edge)); + edges.push_back(generateEdge(automaton, edgeIndex, edge)); } + ++edgeIndex; } } @@ -556,31 +612,74 @@ namespace storm { } template - cpptempl::data_map ExplicitJitJaniModelBuilder::generateEdge(storm::jani::Edge const& edge) { + cpptempl::data_map ExplicitJitJaniModelBuilder::generateEdge(storm::jani::Automaton const& automaton, uint64_t edgeIndex, storm::jani::Edge const& edge) { cpptempl::data_map edgeData; cpptempl::data_list destinations; + uint64_t destinationIndex = 0; for (auto const& destination : edge.getDestinations()) { - destinations.push_back(generateDestination(destination)); + destinations.push_back(generateDestination(destinationIndex, destination)); + ++destinationIndex; } - edgeData["guard"] = expressionTranslator.translate(edge.getGuard(), storm::expressions::ToCppTranslationOptions("sourceState.")); + edgeData["guard"] = expressionTranslator.translate(shiftVariablesWrtLowerBound(edge.getGuard()), storm::expressions::ToCppTranslationOptions("state.")); edgeData["destinations"] = cpptempl::make_data(destinations); + edgeData["name"] = automaton.getName() + "_" + std::to_string(edgeIndex); return edgeData; } template - cpptempl::data_map ExplicitJitJaniModelBuilder::generateDestination(storm::jani::EdgeDestination const& destination) { + cpptempl::data_map ExplicitJitJaniModelBuilder::generateDestination(uint64_t destinationIndex, storm::jani::EdgeDestination const& destination) { cpptempl::data_map destinationData; - cpptempl::data_list nonTransientAssignments; - for (auto const& assignment : destination.getOrderedAssignments().getNonTransientAssignments()) { - nonTransientAssignments.push_back(generateAssignment(assignment, "targetState.")); + cpptempl::data_list levels = generateLevels(destination.getOrderedAssignments()); + destinationData["name"] = asString(destinationIndex); + destinationData["levels"] = cpptempl::make_data(levels); + destinationData["lowestLevel"] = asString(destination.getOrderedAssignments().getLowestLevel()); + destinationData["highestLevel"] = asString(destination.getOrderedAssignments().getHighestLevel()); + + return destinationData; + } + + template + cpptempl::data_list ExplicitJitJaniModelBuilder::generateLevels(storm::jani::OrderedAssignments const& orderedAssignments) { + cpptempl::data_list levels; + + auto const& assignments = orderedAssignments.getAllAssignments(); + if (!assignments.empty()) { + int_fast64_t currentLevel = assignments.begin()->getLevel(); + + cpptempl::data_list nonTransientAssignmentData; + cpptempl::data_list transientAssignmentData; + for (auto const& assignment : assignments) { + if (assignment.getLevel() != currentLevel) { + cpptempl::data_map level; + level["nonTransientAssignments"] = cpptempl::make_data(nonTransientAssignmentData); + level["transientAssignments"] = cpptempl::make_data(transientAssignmentData); + level["index"] = asString(currentLevel); + levels.push_back(level); + + nonTransientAssignmentData = cpptempl::data_list(); + transientAssignmentData = cpptempl::data_list(); + currentLevel = assignment.getLevel(); + } + + if (assignment.isTransient()) { + transientAssignmentData.push_back(generateAssignment(assignment, "state.")); + } else { + nonTransientAssignmentData.push_back(generateAssignment(assignment, "state.")); + } + } + + // Close the last (open) level. + cpptempl::data_map level; + level["nonTransientAssignments"] = cpptempl::make_data(nonTransientAssignmentData); + level["transientAssignments"] = cpptempl::make_data(transientAssignmentData); + level["index"] = asString(currentLevel); + levels.push_back(level); } - destinationData["nonTransientAssignments"] = cpptempl::make_data(nonTransientAssignments); - destinationData["value"] = expressionTranslator.translate(destination.getProbability(), storm::expressions::ToCppTranslationOptions("sourceState.", "double")); - return destinationData; + return levels; } template @@ -604,7 +703,18 @@ namespace storm { cpptempl::data_map ExplicitJitJaniModelBuilder::generateAssignment(storm::jani::Assignment const& assignment, std::string const& prefix) { cpptempl::data_map result; result["variable"] = getVariableName(assignment.getExpressionVariable()); - result["value"] = expressionTranslator.translate(assignment.getAssignedExpression(), prefix); + auto lowerBoundIt = lowerBounds.find(assignment.getExpressionVariable()); + if (lowerBoundIt != lowerBounds.end()) { + if (lowerBoundIt->second < 0) { + result["value"] = expressionTranslator.translate(shiftVariablesWrtLowerBound(assignment.getAssignedExpression()) + model.getManager().integer(lowerBoundIt->second), prefix); + } else if (lowerBoundIt->second == 0) { + result["value"] = expressionTranslator.translate(shiftVariablesWrtLowerBound(assignment.getAssignedExpression()), prefix); + } else { + result["value"] = expressionTranslator.translate(shiftVariablesWrtLowerBound(assignment.getAssignedExpression()) - model.getManager().integer(lowerBoundIt->second), prefix); + } + } else { + result["value"] = expressionTranslator.translate(shiftVariablesWrtLowerBound(assignment.getAssignedExpression()), prefix); + } return result; } @@ -640,6 +750,11 @@ namespace storm { return out.str(); } + template + storm::expressions::Expression ExplicitJitJaniModelBuilder::shiftVariablesWrtLowerBound(storm::expressions::Expression const& expression) { + return expression.substitute(lowerBoundShiftSubstitution); + } + template template std::string ExplicitJitJaniModelBuilder::asString(ValueTypePrime value) const { diff --git a/src/builder/jit/ExplicitJitJaniModelBuilder.h b/src/builder/jit/ExplicitJitJaniModelBuilder.h index 5cc26167f..dd4085954 100644 --- a/src/builder/jit/ExplicitJitJaniModelBuilder.h +++ b/src/builder/jit/ExplicitJitJaniModelBuilder.h @@ -55,9 +55,10 @@ namespace storm { cpptempl::data_list generateLabels(); cpptempl::data_list generateTerminalExpressions(); cpptempl::data_list generateNonSynchronizingEdges(); + cpptempl::data_list generateLevels(storm::jani::OrderedAssignments const& assignments); - cpptempl::data_map generateEdge(storm::jani::Edge const& edge); - cpptempl::data_map generateDestination(storm::jani::EdgeDestination const& destination); + cpptempl::data_map generateEdge(storm::jani::Automaton const& automaton, uint64_t edgeIndex, storm::jani::Edge const& edge); + cpptempl::data_map generateDestination(uint64_t destinationIndex, storm::jani::EdgeDestination const& destination); template cpptempl::data_map generateAssignment(storm::jani::Variable const& variable, ValueTypePrime value) const; @@ -73,7 +74,8 @@ namespace storm { std::string getQualifiedVariableName(storm::jani::Automaton const& automaton, storm::expressions::Variable const& variable) const; std::string getLocationVariableName(storm::jani::Automaton const& automaton) const; std::string asString(bool value) const; - + storm::expressions::Expression shiftVariablesWrtLowerBound(storm::expressions::Expression const& expression); + template std::string asString(ValueTypePrime value) const; @@ -87,6 +89,8 @@ namespace storm { std::unordered_map variableToName; storm::expressions::ToCppVisitor expressionTranslator; + std::map lowerBoundShiftSubstitution; + std::map lowerBounds; }; } diff --git a/src/generator/JaniNextStateGenerator.cpp b/src/generator/JaniNextStateGenerator.cpp index 80473c2f9..a6407acdd 100644 --- a/src/generator/JaniNextStateGenerator.cpp +++ b/src/generator/JaniNextStateGenerator.cpp @@ -92,7 +92,7 @@ namespace storm { storm::jani::Variable const& variable = model.getGlobalVariables().getVariable(expressionOrLabelAndBool.first.getLabel()); STORM_LOG_THROW(variable.isBooleanVariable(), storm::exceptions::InvalidSettingsException, "Terminal states refer to non-boolean variable '" << expressionOrLabelAndBool.first.getLabel() << "'."); STORM_LOG_THROW(variable.isTransient(), storm::exceptions::InvalidSettingsException, "Terminal states refer to non-transient variable '" << expressionOrLabelAndBool.first.getLabel() << "'."); - + this->terminalStates.push_back(std::make_pair(this->model.getLabelExpression(variable.asBooleanVariable(), locationVariables), expressionOrLabelAndBool.second)); } } @@ -188,7 +188,7 @@ namespace storm { blockingExpression = blockingExpression.isInitialized() ? blockingExpression || localBlockingExpression : localBlockingExpression; initialState.setFromInt(integerVariable.bitOffset, integerVariable.bitWidth, static_cast(variableValue - integerVariable.lowerBound)); } - + // Gather iterators to the initial locations of all the automata. std::vector::const_iterator> initialLocationsIterators; uint64_t currentLocationVariable = 0; @@ -270,7 +270,7 @@ namespace storm { // Check that we processed all assignments. STORM_LOG_ASSERT(assignmentIt == assignmentIte, "Not all assignments were consumed."); - + return newState; } @@ -281,7 +281,7 @@ namespace storm { // Retrieve the locations from the state. std::vector locations = getLocations(*this->state); - + // First, construct the state rewards, as we may return early if there are no choices later and we already // need the state rewards then. std::vector stateRewards(this->rewardVariables.size(), storm::utility::zero()); @@ -327,7 +327,7 @@ namespace storm { // For CTMCs, we need to keep track of the total exit rate to scale the action rewards later. For DTMCs // this is equal to the number of choices, which is why we initialize it like this here. ValueType totalExitRate = this->isDiscreteTimeModel() ? static_cast(totalNumberOfChoices) : storm::utility::zero(); - + // Iterate over all choices and combine the probabilities/rates into one choice. for (auto const& choice : allChoices) { for (auto const& stateProbabilityPair : choice) { @@ -346,7 +346,7 @@ namespace storm { globalChoice.addLabels(choice.getLabels()); } } - + std::vector stateActionRewards(rewardVariables.size(), storm::utility::zero()); for (auto const& choice : allChoices) { for (uint_fast64_t rewardVariableIndex = 0; rewardVariableIndex < rewardVariables.size(); ++rewardVariableIndex) { @@ -354,7 +354,7 @@ namespace storm { } } globalChoice.addRewards(std::move(stateActionRewards)); - + // Move the newly fused choice in place. allChoices.clear(); allChoices.push_back(std::move(globalChoice)); @@ -404,20 +404,23 @@ namespace storm { // Iterate over all updates of the current command. ValueType probabilitySum = storm::utility::zero(); for (auto const& destination : edge.getDestinations()) { - // Obtain target state index and add it to the list of known states. If it has not yet been - // seen, we also add it to the set of states that have yet to be explored. - StateType stateIndex = stateToIdCallback(applyUpdate(state, destination, this->variableInformation.locationVariables[automatonIndex])); - - // Update the choice by adding the probability/target state to it. ValueType probability = this->evaluator->asRational(destination.getProbability()); - probability = exitRate ? exitRate.get() * probability : probability; - choice.addProbability(stateIndex, probability); - probabilitySum += probability; + + if (probability != storm::utility::zero()) { + // Obtain target state index and add it to the list of known states. If it has not yet been + // seen, we also add it to the set of states that have yet to be explored. + StateType stateIndex = stateToIdCallback(applyUpdate(state, destination, this->variableInformation.locationVariables[automatonIndex])); + + // Update the choice by adding the probability/target state to it. + probability = exitRate ? exitRate.get() * probability : probability; + choice.addProbability(stateIndex, probability); + probabilitySum += probability; + } } // Create the state-action reward for the newly created choice. performTransientAssignments(edge.getAssignments().getTransientAssignments(), [&choice] (ValueType const& value) { choice.addReward(value); } ); - + // Check that the resulting distribution is in fact a distribution. STORM_LOG_THROW(!this->isDiscreteTimeModel() || this->comparator.isOne(probabilitySum), storm::exceptions::WrongFormatException, "Probabilities do not sum to one for edge (actually sum to " << probabilitySum << ")."); } @@ -467,11 +470,14 @@ namespace storm { // If the new state was already found as a successor state, update the probability // and otherwise insert it. - auto targetStateIt = newTargetStates->find(newTargetState); - if (targetStateIt != newTargetStates->end()) { - targetStateIt->second += stateProbabilityPair.second * this->evaluator->asRational(destination.getProbability()); - } else { - newTargetStates->emplace(newTargetState, stateProbabilityPair.second * this->evaluator->asRational(destination.getProbability())); + auto probability = stateProbabilityPair.second * this->evaluator->asRational(destination.getProbability()); + if (probability != storm::utility::zero()) { + auto targetStateIt = newTargetStates->find(newTargetState); + if (targetStateIt != newTargetStates->end()) { + targetStateIt->second += probability; + } else { + newTargetStates->emplace(newTargetState, probability); + } } } @@ -500,7 +506,7 @@ namespace storm { // Add the rewards to the choice. choice.addRewards(std::move(stateActionRewards)); - + // Add the probabilities/rates to the newly created choice. ValueType probabilitySum = storm::utility::zero(); for (auto const& stateProbabilityPair : *newTargetStates) { @@ -510,7 +516,7 @@ namespace storm { } // Check that the resulting distribution is in fact a distribution. - STORM_LOG_THROW(!this->isDiscreteTimeModel() || !this->comparator.isConstant(probabilitySum) || this->comparator.isOne(probabilitySum), storm::exceptions::WrongFormatException, "Sum of update probabilities do not some to one for some command (actually sum to " << probabilitySum << ")."); + STORM_LOG_THROW(!this->isDiscreteTimeModel() || !this->comparator.isConstant(probabilitySum) || this->comparator.isOne(probabilitySum), storm::exceptions::WrongFormatException, "Sum of update probabilities do not sum to one for some command (actually sum to " << probabilitySum << ")."); // Dispose of the temporary maps. delete currentTargetStates; @@ -689,7 +695,7 @@ namespace storm { } } } - + for (auto const& edge : automaton.getEdges()) { auto rewardVariableIt = rewardVariables.begin(); auto rewardVariableIte = rewardVariables.end(); @@ -717,35 +723,35 @@ namespace storm { #ifdef STORM_HAVE_CARL if (!std::is_same::value && model.hasUndefinedConstants()) { #else - if (model.hasUndefinedConstants()) { + if (model.hasUndefinedConstants()) { #endif - std::vector> undefinedConstants = model.getUndefinedConstants(); - std::stringstream stream; - bool printComma = false; - for (auto const& constant : undefinedConstants) { - if (printComma) { - stream << ", "; - } else { - printComma = true; + std::vector> undefinedConstants = model.getUndefinedConstants(); + std::stringstream stream; + bool printComma = false; + for (auto const& constant : undefinedConstants) { + if (printComma) { + stream << ", "; + } else { + printComma = true; + } + stream << constant.get().getName() << " (" << constant.get().getType() << ")"; } - stream << constant.get().getName() << " (" << constant.get().getType() << ")"; + stream << "."; + STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Program still contains these undefined constants: " + stream.str()); } - stream << "."; - STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Program still contains these undefined constants: " + stream.str()); - } #ifdef STORM_HAVE_CARL - else if (std::is_same::value && !model.undefinedConstantsAreGraphPreserving()) { - STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "The input model contains undefined constants that influence the graph structure of the underlying model, which is not allowed."); - } + else if (std::is_same::value && !model.undefinedConstantsAreGraphPreserving()) { + STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "The input model contains undefined constants that influence the graph structure of the underlying model, which is not allowed."); + } #endif - } - - template class JaniNextStateGenerator; - + } + + template class JaniNextStateGenerator; + #ifdef STORM_HAVE_CARL - template class JaniNextStateGenerator; - template class JaniNextStateGenerator; + template class JaniNextStateGenerator; + template class JaniNextStateGenerator; #endif + } } -} diff --git a/src/generator/PrismNextStateGenerator.cpp b/src/generator/PrismNextStateGenerator.cpp index e344ab13a..35dc1f7a2 100644 --- a/src/generator/PrismNextStateGenerator.cpp +++ b/src/generator/PrismNextStateGenerator.cpp @@ -395,15 +395,17 @@ namespace storm { ValueType probabilitySum = storm::utility::zero(); for (uint_fast64_t k = 0; k < command.getNumberOfUpdates(); ++k) { storm::prism::Update const& update = command.getUpdate(k); - - // Obtain target state index and add it to the list of known states. If it has not yet been - // seen, we also add it to the set of states that have yet to be explored. - StateType stateIndex = stateToIdCallback(applyUpdate(state, update)); - - // Update the choice by adding the probability/target state to it. + ValueType probability = this->evaluator->asRational(update.getLikelihoodExpression()); - choice.addProbability(stateIndex, probability); - probabilitySum += probability; + if (probability != storm::utility::zero()) { + // Obtain target state index and add it to the list of known states. If it has not yet been + // seen, we also add it to the set of states that have yet to be explored. + StateType stateIndex = stateToIdCallback(applyUpdate(state, update)); + + // Update the choice by adding the probability/target state to it. + choice.addProbability(stateIndex, probability); + probabilitySum += probability; + } } // Create the state-action reward for the newly created choice. @@ -458,16 +460,20 @@ namespace storm { storm::prism::Update const& update = command.getUpdate(j); for (auto const& stateProbabilityPair : *currentTargetStates) { - // Compute the new state under the current update and add it to the set of new target states. - CompressedState newTargetState = applyUpdate(stateProbabilityPair.first, update); + auto probability = stateProbabilityPair.second * this->evaluator->asRational(update.getLikelihoodExpression()); - // If the new state was already found as a successor state, update the probability - // and otherwise insert it. - auto targetStateIt = newTargetStates->find(newTargetState); - if (targetStateIt != newTargetStates->end()) { - targetStateIt->second += stateProbabilityPair.second * this->evaluator->asRational(update.getLikelihoodExpression()); - } else { - newTargetStates->emplace(newTargetState, stateProbabilityPair.second * this->evaluator->asRational(update.getLikelihoodExpression())); + if (probability != storm::utility::zero()) { + // Compute the new state under the current update and add it to the set of new target states. + CompressedState newTargetState = applyUpdate(stateProbabilityPair.first, update); + + // If the new state was already found as a successor state, update the probability + // and otherwise insert it. + auto targetStateIt = newTargetStates->find(newTargetState); + if (targetStateIt != newTargetStates->end()) { + targetStateIt->second += probability; + } else { + newTargetStates->emplace(newTargetState, probability); + } } } } diff --git a/src/settings/SettingsManager.cpp b/src/settings/SettingsManager.cpp index 2aba99492..9a5afe650 100644 --- a/src/settings/SettingsManager.cpp +++ b/src/settings/SettingsManager.cpp @@ -34,6 +34,7 @@ #include "src/settings/modules/TopologicalValueIterationEquationSolverSettings.h" #include "src/settings/modules/ExplorationSettings.h" #include "src/settings/modules/JaniExportSettings.h" +#include "src/settings/modules/JitBuilderSettings.h" #include "src/utility/macros.h" #include "src/settings/Option.h" @@ -526,6 +527,7 @@ namespace storm { storm::settings::addModule(); storm::settings::addModule(); storm::settings::addModule(); + storm::settings::addModule(); } } diff --git a/src/settings/modules/JitBuilderSettings.cpp b/src/settings/modules/JitBuilderSettings.cpp new file mode 100644 index 000000000..217ee61e0 --- /dev/null +++ b/src/settings/modules/JitBuilderSettings.cpp @@ -0,0 +1,42 @@ +#include "src/settings/modules/JitBuilderSettings.h" + +#include "src/settings/SettingsManager.h" +#include "src/settings/SettingMemento.h" +#include "src/settings/Option.h" +#include "src/settings/OptionBuilder.h" +#include "src/settings/ArgumentBuilder.h" +#include "src/settings/Argument.h" + +#include "src/exceptions/InvalidSettingsException.h" + +namespace storm { + namespace settings { + namespace modules { + const std::string JitBuilderSettings::moduleName = "jitbuilder"; + + const std::string JitBuilderSettings::noJitOptionName = "nojit"; + const std::string JitBuilderSettings::doctorOptionName = "doctor"; + + JitBuilderSettings::JitBuilderSettings() : ModuleSettings(moduleName) { +// this->addOption(storm::settings::OptionBuilder(moduleName, noJitOptionName, false, "Don't use the jit-based explicit model builder.").build()); + this->addOption(storm::settings::OptionBuilder(moduleName, doctorOptionName, false, "Show information on why the jit-based explicit model builder is not usable on the current system.").build()); + } + +// bool JitBuilderSettings::isNoJitSet() const { +// return this->getOption(noJitOptionName).getHasOptionBeenSet(); +// } + + bool JitBuilderSettings::isDoctorSet() const { + return this->getOption(doctorOptionName).getHasOptionBeenSet(); + } + + void JitBuilderSettings::finalize() { + // Intentionally left empty. + } + + bool JitBuilderSettings::check() const { + return true; + } + } + } +} diff --git a/src/settings/modules/JitBuilderSettings.h b/src/settings/modules/JitBuilderSettings.h new file mode 100644 index 000000000..1080e89b6 --- /dev/null +++ b/src/settings/modules/JitBuilderSettings.h @@ -0,0 +1,32 @@ +#pragma once + +#include "src/settings/modules/ModuleSettings.h" + +namespace storm { + namespace settings { + namespace modules { + + class JitBuilderSettings : public ModuleSettings { + public: + /*! + * Creates a new JitBuilder settings object. + */ + JitBuilderSettings(); + +// bool isNoJitSet() const; + + bool isDoctorSet() const; + + bool check() const override; + void finalize() override; + + static const std::string moduleName; + + private: + static const std::string noJitOptionName; + static const std::string doctorOptionName; + }; + + } + } +} diff --git a/src/storage/jani/Assignment.cpp b/src/storage/jani/Assignment.cpp index ddf65afe4..99f2a09b1 100644 --- a/src/storage/jani/Assignment.cpp +++ b/src/storage/jani/Assignment.cpp @@ -48,20 +48,20 @@ namespace storm { return stream; } - bool AssignmentPartialOrderByVariable::operator()(Assignment const& left, Assignment const& right) const { - return left.getExpressionVariable() < right.getExpressionVariable(); + bool AssignmentPartialOrderByLevelAndVariable::operator()(Assignment const& left, Assignment const& right) const { + return left.getLevel() < right.getLevel() || (left.getLevel() == right.getLevel() && left.getExpressionVariable() < right.getExpressionVariable()); } - bool AssignmentPartialOrderByVariable::operator()(Assignment const& left, std::shared_ptr const& right) const { - return left.getExpressionVariable() < right->getExpressionVariable(); + bool AssignmentPartialOrderByLevelAndVariable::operator()(Assignment const& left, std::shared_ptr const& right) const { + return left.getLevel() < right->getLevel() || (left.getLevel() == right->getLevel() && left.getExpressionVariable() < right->getExpressionVariable()); } - bool AssignmentPartialOrderByVariable::operator()(std::shared_ptr const& left, std::shared_ptr const& right) const { - return left->getExpressionVariable() < right->getExpressionVariable(); + bool AssignmentPartialOrderByLevelAndVariable::operator()(std::shared_ptr const& left, std::shared_ptr const& right) const { + return left->getLevel() < right->getLevel() || (left->getLevel() == right->getLevel() && left->getExpressionVariable() < right->getExpressionVariable()); } - bool AssignmentPartialOrderByVariable::operator()(std::shared_ptr const& left, Assignment const& right) const { - return left->getExpressionVariable() < right.getExpressionVariable(); + bool AssignmentPartialOrderByLevelAndVariable::operator()(std::shared_ptr const& left, Assignment const& right) const { + return left->getLevel() < right.getLevel() || (left->getLevel() == right.getLevel() && left->getExpressionVariable() < right.getExpressionVariable()); } } -} \ No newline at end of file +} diff --git a/src/storage/jani/Assignment.h b/src/storage/jani/Assignment.h index 030c1f68f..7886a5b5b 100644 --- a/src/storage/jani/Assignment.h +++ b/src/storage/jani/Assignment.h @@ -66,9 +66,10 @@ namespace storm { }; /*! - * This functor enables ordering the assignments by variable. Note that this is a partial order. + * This functor enables ordering the assignments first by the assignment level and then by variable. + * Note that this is a partial order. */ - struct AssignmentPartialOrderByVariable { + struct AssignmentPartialOrderByLevelAndVariable { bool operator()(Assignment const& left, Assignment const& right) const; bool operator()(Assignment const& left, std::shared_ptr const& right) const; bool operator()(std::shared_ptr const& left, std::shared_ptr const& right) const; @@ -76,4 +77,4 @@ namespace storm { }; } -} \ No newline at end of file +} diff --git a/src/storage/jani/OrderedAssignments.cpp b/src/storage/jani/OrderedAssignments.cpp index 933054100..b98d543c5 100644 --- a/src/storage/jani/OrderedAssignments.cpp +++ b/src/storage/jani/OrderedAssignments.cpp @@ -70,18 +70,26 @@ namespace storm { } bool OrderedAssignments::hasMultipleLevels() const { - if(allAssignments.empty()) { + if (allAssignments.empty()) { return false; } uint64_t firstLevel = allAssignments.front()->getLevel(); - for(auto const& assignment : allAssignments) { - if(assignment->getLevel() != firstLevel) { + for (auto const& assignment : allAssignments) { + if (assignment->getLevel() != firstLevel) { return true; } } return false; } + int_fast64_t OrderedAssignments::getLowestLevel() const { + return allAssignments.front()->getLevel(); + } + + int_fast64_t OrderedAssignments::getHighestLevel() const { + return allAssignments.back()->getLevel(); + } + bool OrderedAssignments::contains(Assignment const& assignment) const { auto it = lowerBound(assignment, allAssignments); if (it != allAssignments.end() && assignment == **it) { @@ -126,8 +134,8 @@ namespace storm { } std::vector>::const_iterator OrderedAssignments::lowerBound(Assignment const& assignment, std::vector> const& assignments) { - return std::lower_bound(assignments.begin(), assignments.end(), assignment, storm::jani::AssignmentPartialOrderByVariable()); + return std::lower_bound(assignments.begin(), assignments.end(), assignment, storm::jani::AssignmentPartialOrderByLevelAndVariable()); } } -} \ No newline at end of file +} diff --git a/src/storage/jani/OrderedAssignments.h b/src/storage/jani/OrderedAssignments.h index afbf66cad..f21b09fb8 100644 --- a/src/storage/jani/OrderedAssignments.h +++ b/src/storage/jani/OrderedAssignments.h @@ -42,6 +42,18 @@ namespace storm { */ bool hasMultipleLevels() const; + /*! + * Retrieves the lowest level among all assignments. Note that this may only be called if there is at least + * one assignment. + */ + int_fast64_t getLowestLevel() const; + + /*! + * Retrieves the highest level among all assignments. Note that this may only be called if there is at least + * one assignment. + */ + int_fast64_t getHighestLevel() const; + /*! * Retrieves whether the given assignment is contained in this set of assignments. */ @@ -97,4 +109,4 @@ namespace storm { }; } -} \ No newline at end of file +} diff --git a/src/storage/prism/Command.cpp b/src/storage/prism/Command.cpp index 5cad634e7..09c59197f 100644 --- a/src/storage/prism/Command.cpp +++ b/src/storage/prism/Command.cpp @@ -73,14 +73,13 @@ namespace storm { return true; } - Command Command::removeIdentityAssignmentsFromUpdates() const { + Command Command::simplify() const { std::vector newUpdates; newUpdates.reserve(this->getNumberOfUpdates()); for (auto const& update : this->getUpdates()) { newUpdates.emplace_back(update.removeIdentityAssignments()); } return copyWithNewUpdates(std::move(newUpdates)); - } Command Command::copyWithNewUpdates(std::vector && newUpdates) const { diff --git a/src/storage/prism/Command.h b/src/storage/prism/Command.h index 658cce67d..473ca07a4 100644 --- a/src/storage/prism/Command.h +++ b/src/storage/prism/Command.h @@ -128,12 +128,11 @@ namespace storm { friend std::ostream& operator<<(std::ostream& stream, Command const& command); - /** - * Removes identity assignments from the updates - * @return The resulting command + /*! + * Simplifies this command. */ - Command removeIdentityAssignmentsFromUpdates() const; - + Command simplify() const; + private: // The index of the action associated with this command. uint_fast64_t actionIndex; diff --git a/src/storage/prism/Program.cpp b/src/storage/prism/Program.cpp index f187ecc08..095e54fee 100644 --- a/src/storage/prism/Program.cpp +++ b/src/storage/prism/Program.cpp @@ -1231,7 +1231,7 @@ namespace storm { std::vector newCommands; for (auto const& command : module.getCommands()) { if (!command.getGuardExpression().isFalse()) { - newCommands.emplace_back(command.removeIdentityAssignmentsFromUpdates()); + newCommands.emplace_back(command.simplify()); } } diff --git a/test/functional/builder/ExplicitJitJaniModelBuilderTest.cpp b/test/functional/builder/ExplicitJitJaniModelBuilderTest.cpp index b2f06a905..0086008a6 100644 --- a/test/functional/builder/ExplicitJitJaniModelBuilderTest.cpp +++ b/test/functional/builder/ExplicitJitJaniModelBuilderTest.cpp @@ -2,11 +2,11 @@ #include "storm-config.h" #include "src/parser/PrismParser.h" #include "src/storage/jani/Model.h" -#include "src/builder/ExplicitJitJaniModelBuilder.h" +#include "src/builder/jit/ExplicitJitJaniModelBuilder.h" TEST(ExplicitJitJaniModelBuilderTest, Dtmc) { storm::prism::Program program = storm::parser::PrismParser::parse(STORM_CPP_TESTS_BASE_PATH "/functional/builder/die.pm"); storm::jani::Model janiModel = program.toJani(); - storm::builder::ExplicitJitJaniModelBuilder(janiModel).build(); + storm::builder::jit::ExplicitJitJaniModelBuilder(janiModel).build(); }