Browse Source

more work wrt cleaner model exploration

Former-commit-id: f24d618bdf
tempestpy_adaptions
dehnert 9 years ago
parent
commit
a75e0f5323
  1. 184
      src/builder/ExplicitPrismModelBuilder.cpp
  2. 7
      src/builder/ExplicitPrismModelBuilder.h
  3. 8
      src/generator/NextStateGenerator.h
  4. 136
      src/generator/PrismNextStateGenerator.cpp
  5. 7
      src/generator/PrismNextStateGenerator.h
  6. 37
      src/generator/VariableInformation.cpp
  7. 9
      src/generator/VariableInformation.h
  8. 229
      src/storage/BitVector.cpp
  9. 7
      src/storage/BitVector.h

184
src/builder/ExplicitPrismModelBuilder.cpp

@ -11,6 +11,8 @@
#include "src/settings/modules/GeneralSettings.h"
#include "src/generator/PrismNextStateGenerator.h"
#include "src/utility/prism.h"
#include "src/utility/macros.h"
#include "src/utility/ConstantsComparator.h"
@ -37,7 +39,7 @@ namespace storm {
stateRewardVector.resize(rowGroupCount);
optionalStateRewardVector = std::move(stateRewardVector);
}
boost::optional<std::vector<ValueType>> optionalStateActionRewardVector;
if (hasStateActionRewards) {
stateActionRewardVector.resize(rowCount);
@ -75,12 +77,12 @@ namespace storm {
ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::InternalStateInformation::InternalStateInformation(uint64_t bitsPerState) : stateStorage(bitsPerState, 10000000), bitsPerState(bitsPerState), numberOfStates() {
// Intentionally left empty.
}
template <typename ValueType, typename RewardModelType, typename StateType>
ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::ModelComponents::ModelComponents() : transitionMatrix(), stateLabeling(), rewardModels(), choiceLabeling() {
// Intentionally left empty.
}
template <typename ValueType, typename RewardModelType, typename StateType>
ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::Options::Options() : buildCommandLabels(false), buildAllRewardModels(true), buildStateInformation(false), rewardModelsToBuild(), constantDefinitions(), buildAllLabels(true), labelsToBuild(), expressionLabels(), terminalStates(), negatedTerminalStates() {
// Intentionally left empty.
@ -135,7 +137,7 @@ namespace storm {
}
}
}
template <typename ValueType, typename RewardModelType, typename StateType>
void ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::Options::preserveFormula(storm::logic::Formula const& formula) {
// If we already had terminal states, we need to erase them.
@ -180,7 +182,7 @@ namespace storm {
}
template <typename ValueType, typename RewardModelType, typename StateType>
ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::ExplicitPrismModelBuilder(storm::prism::Program const& program, Options const& options) : options(options), program(program) {
ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::ExplicitPrismModelBuilder(storm::prism::Program const& program, Options const& options) : options(options), program(program), variableInformation(program), internalStateInformation(variableInformation.getTotalBitOffset()) {
// Start by defining the undefined constants in the model.
if (options.constantDefinitions) {
this->program = program.defineUndefinedConstants(options.constantDefinitions.get());
@ -189,56 +191,48 @@ namespace storm {
}
// If the program still contains undefined constants and we are not in a parametric setting, assemble an appropriate error message.
#ifdef STORM_HAVE_CARL
// If the program either has undefined constants or we are building a parametric model, but the parameters
// not only appear in the probabilities, we re
if (!std::is_same<ValueType, storm::RationalFunction>::value && this->program.hasUndefinedConstants()) {
#else
if (this->program->hasUndefinedConstants()) {
#endif
std::vector<std::reference_wrapper<storm::prism::Constant const>> undefinedConstants = this->program.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() << ")";
std::vector<std::reference_wrapper<storm::prism::Constant const>> undefinedConstants = this->program.getUndefinedConstants();
std::stringstream stream;
bool printComma = false;
for (auto const& constant : undefinedConstants) {
if (printComma) {
stream << ", ";
} else {
printComma = true;
}
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<ValueType, storm::RationalFunction>::value && !this->program.hasUndefinedConstantsOnlyInUpdateProbabilitiesAndRewards()) {
STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "The program contains undefined constants that appear in some places other than update probabilities and reward value expressions, which is not admitted.");
#endif
stream << constant.get().getName() << " (" << constant.get().getType() << ")";
}
// If the set of labels we are supposed to built is restricted, we need to remove the other labels from the program.
if (options.labelsToBuild) {
if (!options.buildAllLabels) {
this->program.filterLabels(options.labelsToBuild.get());
}
stream << ".";
STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "Program still contains these undefined constants: " + stream.str());
} else if (std::is_same<ValueType, storm::RationalFunction>::value && !this->program.hasUndefinedConstantsOnlyInUpdateProbabilitiesAndRewards()) {
STORM_LOG_THROW(false, storm::exceptions::InvalidArgumentException, "The program contains undefined constants that appear in some places other than update probabilities and reward value expressions, which is not admitted.");
}
// If the set of labels we are supposed to built is restricted, we need to remove the other labels from the program.
if (options.labelsToBuild) {
if (!options.buildAllLabels) {
this->program.filterLabels(options.labelsToBuild.get());
}
}
// If we need to build labels for expressions that may appear in some formula, we need to add appropriate
// labels to the program.
if (options.expressionLabels) {
std::map<storm::expressions::Variable, storm::expressions::Expression> constantsSubstitution = this->program.getConstantsSubstitution();
// If we need to build labels for expressions that may appear in some formula, we need to add appropriate
// labels to the program.
if (options.expressionLabels) {
std::map<storm::expressions::Variable, storm::expressions::Expression> constantsSubstitution = this->program.getConstantsSubstitution();
for (auto const& expression : options.expressionLabels.get()) {
std::stringstream stream;
stream << expression.substitute(constantsSubstitution);
std::string name = stream.str();
if (!this->program.hasLabel(name)) {
this->program.addLabel(name, expression);
}
for (auto const& expression : options.expressionLabels.get()) {
std::stringstream stream;
stream << expression.substitute(constantsSubstitution);
std::string name = stream.str();
if (!this->program.hasLabel(name)) {
this->program.addLabel(name, expression);
}
}
// Now that the program is fixed, we we need to substitute all constants with their concrete value.
this->program = program.substituteConstants();
}
// Now that the program is fixed, we we need to substitute all constants with their concrete value.
this->program = program.substituteConstants();
}
template <typename ValueType, typename RewardModelType, typename StateType>
@ -255,15 +249,15 @@ namespace storm {
template <typename ValueType, typename RewardModelType, typename StateType>
std::shared_ptr<storm::models::sparse::Model<ValueType, RewardModelType>> ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::translate() {
STORM_LOG_DEBUG("Building representation of program:" << std::endl << *program << std::endl);
// Select the appropriate reward models (after the constants have been substituted).
std::vector<std::reference_wrapper<storm::prism::RewardModel const>> selectedRewardModels;
// First, we make sure that all selected reward models actually exist.
for (auto const& rewardModelName : options.rewardModelsToBuild) {
STORM_LOG_THROW(rewardModelName.empty() || program.hasRewardModel(rewardModelName), storm::exceptions::InvalidArgumentException, "Model does not possess a reward model with the name '" << rewardModelName << "'.");
}
for (auto const& rewardModel : program.getRewardModels()) {
if (options.buildAllRewardModels || options.rewardModelsToBuild.find(rewardModel.getName()) != options.rewardModelsToBuild.end()) {
selectedRewardModels.push_back(rewardModel);
@ -274,7 +268,7 @@ namespace storm {
if (selectedRewardModels.empty() && program.getNumberOfRewardModels() == 1 && options.rewardModelsToBuild.size() == 1 && *options.rewardModelsToBuild.begin() == "") {
selectedRewardModels.push_back(program.getRewardModel(0));
}
ModelComponents modelComponents = buildModelComponents(*program, selectedRewardModels, options);
std::shared_ptr<storm::models::sparse::Model<ValueType, RewardModelType>> result;
@ -295,10 +289,10 @@ namespace storm {
return result;
}
template <typename ValueType, typename RewardModelType, typename StateType>
storm::expressions::SimpleValuation ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::unpackStateIntoValuation(storm::storage::BitVector const& currentState) {
storm::expressions::SimpleValuation result(variableInformation.manager.getSharedPointer());
storm::expressions::SimpleValuation result(program.getManager().getSharedPointer());
for (auto const& booleanVariable : variableInformation.booleanVariables) {
result.setBooleanValue(booleanVariable.variable, currentState.get(booleanVariable.bitOffset));
}
@ -322,17 +316,54 @@ namespace storm {
return actualIndexBucketPair.first;
}
template <typename ValueType, typename RewardModelType, typename StateType>
boost::optional<std::vector<boost::container::flat_set<uint_fast64_t>>> ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::buildMatrices(std::vector<std::reference_wrapper<storm::prism::RewardModel const>> const& selectedRewardModels, InternalStateInformation& internalStateInformation, storm::storage::SparseMatrixBuilder<ValueType>& transitionMatrixBuilder, std::vector<RewardModelBuilder<typename RewardModelType::ValueType>>& rewardModelBuilders, boost::optional<storm::expressions::Expression> const& terminalExpression) {
boost::optional<std::vector<boost::container::flat_set<uint_fast64_t>>> ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::buildMatrices(std::vector<std::reference_wrapper<storm::prism::RewardModel const>> const& selectedRewardModels, storm::storage::SparseMatrixBuilder<ValueType>& transitionMatrixBuilder, std::vector<RewardModelBuilder<typename RewardModelType::ValueType>>& rewardModelBuilders, boost::optional<storm::expressions::Expression> const& terminalExpression) {
// Create choice labels, if requested,
boost::optional<std::vector<boost::container::flat_set<uint_fast64_t>>> choiceLabels;
if (options.buildCommandLabels) {
choiceLabels = std::vector<boost::container::flat_set<uint_fast64_t>>();
}
// Create a generator that is able to expand states.
storm::generator::PrismNextStateGenerator<ValueType, StateType> generator(program, variableInformation, options.buildCommandLabels);
for (auto const& rewardModel : selectedRewardModels) {
generator.addRewardModel(rewardModel.get());
}
// Create a callback for the next-state generator to enable it to request the index of states.
std::function<StateType (CompressedState const&)> stateToIdCallback = std::bind1st(&ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::getOrAddStateIndex, this);
// A comparator that can be used to check whether we actually have distributions.
storm::utility::ConstantsComparator<ValueType> comparator;
// Let the generator create all initial states.
generator.getInitialStates(stateToIdCallback);
// Now explore the current state until there is no more reachable state.
uint_fast64_t currentRow = 0;
// Perform a DFS through the model.
while (!statesToExplore.empty()) {
// Get the first state in the queue.
std::pair<CompressedState, StateType> currentState = internalStateInformation.stateStorage.getBucketAndValue(statesToExplore.front());
statesToExplore.pop();
STORM_LOG_TRACE("Exploring state with id " << currentState.second << ".");
bool deadlockOnPurpose = false;
if (terminalExpression && evaluator.asBool(terminalExpression.get())) {
// Nothing to do in this case.
deadlockOnPurpose = true;
} else {
// Retrieve all choices for the current state.
allUnlabeledChoices = getUnlabeledTransitions(program, discreteTimeModel, internalStateInformation, variableInformation, currentState, commandLabels, evaluator, stateQueue, comparator);
allLabeledChoices = getLabeledTransitions(program, discreteTimeModel, internalStateInformation, variableInformation, currentState, commandLabels, evaluator, stateQueue, comparator);
}
}
// Initialize a queue and insert the initial state.
std::queue<storm::storage::BitVector> stateQueue;
@ -420,7 +451,7 @@ namespace storm {
std::cout << unpackStateIntoValuation(currentState, variableInformation).toString(true) << std::endl;
STORM_LOG_THROW(false, storm::exceptions::WrongFormatException,
"Error while creating sparse matrix from probabilistic program: found deadlock state. For fixing these, please provide the appropriate option.");
}
} else if (totalNumberOfChoices == 1) {
if (!deterministicModel) {
@ -468,7 +499,7 @@ namespace storm {
// or compose them to one choice.
if (deterministicModel) {
Choice<ValueType> globalChoice;
// We need to prepare the entries of those vectors that are going to be used.
auto builderIt = rewardModelBuilders.begin();
for (auto rewardModelIt = selectedRewardModels.begin(), rewardModelIte = selectedRewardModels.end(); rewardModelIt != rewardModelIte; ++rewardModelIt, ++builderIt) {
@ -571,7 +602,7 @@ namespace storm {
} else {
// If the model is nondeterministic, we add all choices individually.
transitionMatrixBuilder.newRowGroup(currentRow);
auto builderIt = rewardModelBuilders.begin();
for (auto rewardModelIt = selectedRewardModels.begin(), rewardModelIte = selectedRewardModels.end(); rewardModelIt != rewardModelIte; ++rewardModelIt, ++builderIt) {
if (rewardModelIt->get().hasStateRewards()) {
@ -651,36 +682,7 @@ namespace storm {
typename ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::ModelComponents ExplicitPrismModelBuilder<ValueType, RewardModelType, StateType>::buildModelComponents(storm::prism::Program const& program, std::vector<std::reference_wrapper<storm::prism::RewardModel const>> const& selectedRewardModels, Options const& options) {
ModelComponents modelComponents;
uint_fast64_t bitOffset = 0;
VariableInformation variableInformation(program.getManager());
for (auto const& booleanVariable : program.getGlobalBooleanVariables()) {
variableInformation.booleanVariables.emplace_back(booleanVariable.getExpressionVariable(), booleanVariable.getInitialValueExpression().evaluateAsBool(), bitOffset);
++bitOffset;
variableInformation.booleanVariableToIndexMap[booleanVariable.getExpressionVariable()] = variableInformation.booleanVariables.size() - 1;
}
for (auto const& integerVariable : program.getGlobalIntegerVariables()) {
int_fast64_t lowerBound = integerVariable.getLowerBoundExpression().evaluateAsInt();
int_fast64_t upperBound = integerVariable.getUpperBoundExpression().evaluateAsInt();
uint_fast64_t bitwidth = static_cast<uint_fast64_t>(std::ceil(std::log2(upperBound - lowerBound + 1)));
variableInformation.integerVariables.emplace_back(integerVariable.getExpressionVariable(), integerVariable.getInitialValueExpression().evaluateAsInt(), lowerBound, upperBound, bitOffset, bitwidth);
bitOffset += bitwidth;
variableInformation.integerVariableToIndexMap[integerVariable.getExpressionVariable()] = variableInformation.integerVariables.size() - 1;
}
for (auto const& module : program.getModules()) {
for (auto const& booleanVariable : module.getBooleanVariables()) {
variableInformation.booleanVariables.emplace_back(booleanVariable.getExpressionVariable(), booleanVariable.getInitialValueExpression().evaluateAsBool(), bitOffset);
++bitOffset;
variableInformation.booleanVariableToIndexMap[booleanVariable.getExpressionVariable()] = variableInformation.booleanVariables.size() - 1;
}
for (auto const& integerVariable : module.getIntegerVariables()) {
int_fast64_t lowerBound = integerVariable.getLowerBoundExpression().evaluateAsInt();
int_fast64_t upperBound = integerVariable.getUpperBoundExpression().evaluateAsInt();
uint_fast64_t bitwidth = static_cast<uint_fast64_t>(std::ceil(std::log2(upperBound - lowerBound + 1)));
variableInformation.integerVariables.emplace_back(integerVariable.getExpressionVariable(), integerVariable.getInitialValueExpression().evaluateAsInt(), lowerBound, upperBound, bitOffset, bitwidth);
bitOffset += bitwidth;
variableInformation.integerVariableToIndexMap[integerVariable.getExpressionVariable()] = variableInformation.integerVariables.size() - 1;
}
}
VariableInformation variableInformation(program);
// Create the structure for storing the reachable state space.
uint64_t bitsPerState = ((bitOffset / 64) + 1) * 64;

7
src/builder/ExplicitPrismModelBuilder.h

@ -242,7 +242,7 @@ namespace storm {
* @return A tuple containing a vector with all rows at which the nondeterministic choices of each state begin
* and a vector containing the labels associated with each choice.
*/
boost::optional<std::vector<boost::container::flat_set<uint_fast64_t>>> buildMatrices(std::vector<std::reference_wrapper<storm::prism::RewardModel const>> const& selectedRewardModels, InternalStateInformation& internalStateInformation, storm::storage::SparseMatrixBuilder<ValueType>& transitionMatrixBuilder, std::vector<RewardModelBuilder<typename RewardModelType::ValueType>>& rewardModelBuilders, boost::optional<storm::expressions::Expression> const& terminalExpression);
boost::optional<std::vector<boost::container::flat_set<uint_fast64_t>>> buildMatrices(std::vector<std::reference_wrapper<storm::prism::RewardModel const>> const& selectedRewardModels, storm::storage::SparseMatrixBuilder<ValueType>& transitionMatrixBuilder, std::vector<RewardModelBuilder<typename RewardModelType::ValueType>>& rewardModelBuilders, boost::optional<storm::expressions::Expression> const& terminalExpression);
/*!
* Explores the state space of the given program and returns the components of the model as a result.
@ -280,8 +280,9 @@ namespace storm {
// successful build.
boost::optional<StateInformation> stateInformation;
// A queue of states that still need to be explored.
std::queue<storm::storage::BitVector> statesToExplore;
// A queue of states that still need to be explored. The indices in this queue are the bucket indices in the
// bit vector hash map holding the compressed states.
std::queue<std::size_t> statesToExplore;
};

8
src/generator/NextStateGenerator.h

@ -16,10 +16,10 @@ namespace storm {
template<typename ValueType, typename StateType = uint32_t>
class NextStateGenerator {
public:
typedef StateType (*StateToIdCallback)(CompressedState const&);
virtual std::vector<StateType> getInitialStates(StateToIdCallback stateToIdCallback) = 0;
virtual StateBehavior<ValueType, StateType> expand(CompressedState const& state, StateToIdCallback stateToIdCallback) = 0;
typedef std::function<StateType (CompressedState const&)> StateToIdCallback;
virtual std::vector<StateType> getInitialStates(StateToIdCallback const& stateToIdCallback) = 0;
virtual StateBehavior<ValueType, StateType> expand(CompressedState const& state, StateToIdCallback const& stateToIdCallback) = 0;
};
}
}

136
src/generator/PrismNextStateGenerator.cpp

@ -15,13 +15,108 @@ namespace storm {
template<typename ValueType, typename StateType>
void PrismNextStateGenerator<ValueType, StateType>::addRewardModel(storm::prism::RewardModel const& rewardModel) {
selectedRewardModels.push_back(rewardModel);
hasStateActionRewards |= rewardModel.hasStateActionRewards();
}
template<typename ValueType, typename StateType>
StateBehavior<ValueType, StateType> PrismNextStateGenerator<ValueType, StateType>::expand(CompressedState const& state, typename NextStateGenerator<ValueType, StateType>::StateToIdCallback stateToIdCallback) {
// TODO
std::vector<StateType> PrismNextStateGenerator<ValueType, StateType>::getInitialStates(StateToIdCallback const& stateToIdCallback) {
// FIXME, TODO, whatever
}
template<typename ValueType, typename StateType>
StateBehavior<ValueType, StateType> PrismNextStateGenerator<ValueType, StateType>::expand(CompressedState const& state, StateToIdCallback const& stateToIdCallback) {
// Start by unpacking the state into the evaluator so we can quickly evaluate expressions later.
unpackStateIntoEvaluator(state);
// Prepare the result, in case we return early.
StateBehavior<ValueType, StateType> result;
// First, construct the state rewards, as we may return early if there are no choices later and we already
// need the state rewards then.
for (auto const& rewardModel : selectedRewardModels) {
ValueType stateReward = storm::utility::zero<ValueType>();
if (rewardModel->hasStateRewards()) {
for (auto const& stateReward : rewardModel->getStateRewards()) {
if (evaluator.asBool(stateReward.getStatePredicateExpression())) {
stateReward += ValueType(evaluator.asRational(stateReward.getRewardValueExpression()));
}
}
}
result.addStateReward(stateReward);
}
// Get all choices for the state.
std::vector<Choice<ValueType>> allChoices = getUnlabeledChoices(state, stateToIdCallback);
std::vector<Choice<ValueType>> allLabeledChoices = getLabeledChoices(state, stateToIdCallback);
for (auto& choice : allLabeledChoices) {
allChoices.push_back(std::move(choice));
}
std::size_t totalNumberOfChoices = allChoices.size();
// If there is not a single choice, we return immediately, because the state has no behavior (other than
// the state reward).
if (totalNumberOfChoices == 0) {
return result;
}
// If the model is a deterministic model, we need to fuse the choices into one.
if (program.isDeterministicModel() && totalNumberOfChoices > 1) {
Choice<ValueType> globalChoice;
// 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 = static_cast<ValueType>(totalNumberOfChoices);
// Iterate over all choices and combine the probabilities/rates into one choice.
for (auto const& choice : allChoices) {
for (auto const& stateProbabilityPair : choice) {
if (program.isDiscreteTimeModel()) {
globalChoice.getOrAddEntry(stateProbabilityPair.first) += stateProbabilityPair.second / totalNumberOfChoices;
if (hasStateActionRewards) {
totalExitRate += choice.getTotalMass();
}
} else {
globalChoice.getOrAddEntry(stateProbabilityPair.first) += stateProbabilityPair.second;
}
}
if (buildChoiceLabeling) {
globalChoice.addChoiceLabels(choice.getChoiceLabels());
}
}
// Now construct the state-action reward for all selected reward models.
for (auto const& rewardModel : selectedRewardModels) {
ValueType stateActionReward = storm::utility::zero<ValueType>();
if (rewardModel->hasStateActionRewards()) {
for (auto const& stateActionReward : rewardModel->getStateActionRewards()) {
for (auto const& choice : allChoices) {
if (stateActionReward.getActionIndex() == choice.getActionIndex() && evaluator.asBool(stateActionReward.getStatePredicateExpression())) {
stateActionReward += ValueType(evaluator.asRational(stateActionReward.getRewardValueExpression())) / totalExitRate;
}
}
}
}
globalChoice.addChoiceReward(stateActionReward);
}
// Move the newly fused choice in place.
allChoices.clear();
allChoices.push_back(std::move(globalChoice));
}
// Move all remaining choices in place.
for (auto& choice : allChoices) {
result.addChoice(std::move(choice));
}
return result;
}
template<typename ValueType, typename StateType>
void PrismNextStateGenerator<ValueType, StateType>::unpackStateIntoEvaluator(storm::storage::BitVector const& state) {
for (auto const& booleanVariable : variableInformation.booleanVariables) {
@ -31,7 +126,7 @@ namespace storm {
evaluator.setIntegerValue(integerVariable.variable, state.getAsInt(integerVariable.bitOffset, integerVariable.bitWidth) + integerVariable.lowerBound);
}
}
template<typename ValueType, typename StateType>
CompressedState PrismNextStateGenerator<ValueType, StateType>::applyUpdate(CompressedState const& state, storm::prism::Update const& update) {
CompressedState newState(state);
@ -110,7 +205,7 @@ namespace storm {
}
template<typename ValueType, typename StateType>
std::vector<Choice<ValueType>> PrismNextStateGenerator<ValueType, StateType>::getUnlabeledTransitions(CompressedState const& state, StateToIdCallback stateToIdCallback) {
std::vector<Choice<ValueType>> PrismNextStateGenerator<ValueType, StateType>::getUnlabeledChoices(CompressedState const& state, StateToIdCallback stateToIdCallback) {
std::vector<Choice<ValueType>> result;
// Iterate over all modules.
@ -129,7 +224,7 @@ namespace storm {
continue;
}
result.push_back(Choice<ValueType>(0, buildChoiceLabeling));
result.push_back(Choice<ValueType>(command.getActionIndex()));
Choice<ValueType>& choice = result.back();
// Remember the command labels only if we were asked to.
@ -152,6 +247,19 @@ namespace storm {
probabilitySum += probability;
}
// Create the state-action reward for the newly created choice.
for (auto const& rewardModel : selectedRewardModels) {
ValueType stateActionReward = storm::utility::zero<ValueType>();
if (rewardModel->hasStateActionRewards()) {
for (auto const& stateActionReward : rewardModel->getStateActionRewards()) {
if (stateActionReward.getActionIndex() == choice.getActionIndex() && evaluator.asBool(stateActionReward.getStatePredicateExpression())) {
stateActionReward += ValueType(evaluator.asRational(stateActionReward.getRewardValueExpression())) * choice.getTotalMass();
}
}
}
choice.addChoiceReward(stateActionReward);
}
// Check that the resulting distribution is in fact a distribution.
STORM_LOG_THROW(!program.isDiscreteTimeModel() || comparator.isOne(probabilitySum), storm::exceptions::WrongFormatException, "Probabilities do not sum to one for command '" << command << "' (actually sum to " << probabilitySum << ").");
}
@ -161,7 +269,7 @@ namespace storm {
}
template<typename ValueType, typename StateType>
std::vector<Choice<ValueType>> PrismNextStateGenerator<ValueType, StateType>::getLabeledTransitions(CompressedState const& state, StateToIdCallback stateToIdCallback) {
std::vector<Choice<ValueType>> PrismNextStateGenerator<ValueType, StateType>::getLabeledChoices(CompressedState const& state, StateToIdCallback stateToIdCallback) {
std::vector<Choice<ValueType>> result;
for (uint_fast64_t actionIndex : program.getSynchronizingActionIndices()) {
@ -208,7 +316,7 @@ namespace storm {
// At this point, we applied all commands of the current command combination and newTargetStates
// contains all target states and their respective probabilities. That means we are now ready to
// add the choice to the list of transitions.
result.push_back(Choice<ValueType>(actionIndex, buildChoiceLabeling));
result.push_back(Choice<ValueType>(actionIndex));
// Now create the actual distribution.
Choice<ValueType>& choice = result.back();
@ -221,6 +329,7 @@ namespace storm {
}
}
// Add the probabilities/rates to the newly created choice.
ValueType probabilitySum = storm::utility::zero<ValueType>();
for (auto const& stateProbabilityPair : *newTargetStates) {
StateType actualIndex = stateToIdCallback(stateProbabilityPair.first);
@ -231,6 +340,19 @@ namespace storm {
// Check that the resulting distribution is in fact a distribution.
STORM_LOG_THROW(!program.isDiscreteTimeModel() || !comparator.isConstant(probabilitySum) || comparator.isOne(probabilitySum), storm::exceptions::WrongFormatException, "Sum of update probabilities do not some to one for some command (actually sum to " << probabilitySum << ").");
// Create the state-action reward for the newly created choice.
for (auto const& rewardModel : selectedRewardModels) {
ValueType stateActionReward = storm::utility::zero<ValueType>();
if (rewardModel->hasStateActionRewards()) {
for (auto const& stateActionReward : rewardModel->getStateActionRewards()) {
if (stateActionReward.getActionIndex() == choice.getActionIndex() && evaluator.asBool(stateActionReward.getStatePredicateExpression())) {
stateActionReward += ValueType(evaluator.asRational(stateActionReward.getRewardValueExpression())) * choice.getTotalMass();
}
}
}
choice.addChoiceReward(stateActionReward);
}
// Dispose of the temporary maps.
delete currentTargetStates;
delete newTargetStates;

7
src/generator/PrismNextStateGenerator.h

@ -22,8 +22,8 @@ namespace storm {
*/
void addRewardModel(storm::prism::RewardModel const& rewardModel);
virtual std::vector<StateType> getInitialStates(StateToIdCallback stateToIdCallback) = 0;
virtual StateBehavior<ValueType, StateType> expand(CompressedState const& state, StateToIdCallback stateToIdCallback) override;
virtual std::vector<StateType> getInitialStates(StateToIdCallback const& stateToIdCallback) override;
virtual StateBehavior<ValueType, StateType> expand(CompressedState const& state, StateToIdCallback const& stateToIdCallback) override;
private:
/*!
@ -82,6 +82,9 @@ namespace storm {
// The reward models that need to be considered.
std::vector<std::reference_wrapper<storm::prism::RewardModel const>> selectedRewardModels;
// A flag that stores whether at least one of the selected reward models has state-action rewards.
bool hasStateActionRewards;
// A flag that stores whether or not to build the choice labeling.
bool buildChoiceLabeling;

37
src/generator/VariableInformation.cpp

@ -1,4 +1,4 @@
#include "src/generator/prism/VariableInformation.h"
#include "src/generator/VariableInformation.h"
#include "src/utility/macros.h"
#include "src/exceptions/InvalidArgumentException.h"
@ -14,8 +14,39 @@ namespace storm {
// Intentionally left empty.
}
VariableInformation::VariableInformation(storm::expressions::ExpressionManager const& manager) : manager(manager) {
// Intentionally left empty.
VariableInformation::VariableInformation(storm::prism::Program const& program) : totalBitOffset(0) {
for (auto const& booleanVariable : program.getGlobalBooleanVariables()) {
booleanVariables.emplace_back(booleanVariable.getExpressionVariable(), booleanVariable.getInitialValueExpression().evaluateAsBool(), totalBitOffset);
++totalBitOffset;
booleanVariableToIndexMap[booleanVariable.getExpressionVariable()] = booleanVariables.size() - 1;
}
for (auto const& integerVariable : program.getGlobalIntegerVariables()) {
int_fast64_t lowerBound = integerVariable.getLowerBoundExpression().evaluateAsInt();
int_fast64_t upperBound = integerVariable.getUpperBoundExpression().evaluateAsInt();
uint_fast64_t bitwidth = static_cast<uint_fast64_t>(std::ceil(std::log2(upperBound - lowerBound + 1)));
integerVariables.emplace_back(integerVariable.getExpressionVariable(), integerVariable.getInitialValueExpression().evaluateAsInt(), lowerBound, upperBound, totalBitOffset, bitwidth);
totalBitOffset += bitwidth;
integerVariableToIndexMap[integerVariable.getExpressionVariable()] = integerVariables.size() - 1;
}
for (auto const& module : program.getModules()) {
for (auto const& booleanVariable : module.getBooleanVariables()) {
booleanVariables.emplace_back(booleanVariable.getExpressionVariable(), booleanVariable.getInitialValueExpression().evaluateAsBool(), totalBitOffset);
++totalBitOffset;
booleanVariableToIndexMap[booleanVariable.getExpressionVariable()] = booleanVariables.size() - 1;
}
for (auto const& integerVariable : module.getIntegerVariables()) {
int_fast64_t lowerBound = integerVariable.getLowerBoundExpression().evaluateAsInt();
int_fast64_t upperBound = integerVariable.getUpperBoundExpression().evaluateAsInt();
uint_fast64_t bitwidth = static_cast<uint_fast64_t>(std::ceil(std::log2(upperBound - lowerBound + 1)));
integerVariables.emplace_back(integerVariable.getExpressionVariable(), integerVariable.getInitialValueExpression().evaluateAsInt(), lowerBound, upperBound, totalBitOffset, bitwidth);
totalBitOffset += bitwidth;
integerVariableToIndexMap[integerVariable.getExpressionVariable()] = integerVariables.size() - 1;
}
}
}
uint_fast64_t VariableInformation::getTotalBitOffset() const {
return totalBitOffset;
}
uint_fast64_t VariableInformation::getBitOffset(storm::expressions::Variable const& variable) const {

9
src/generator/VariableInformation.h

@ -5,6 +5,7 @@
#include <boost/container/flat_map.hpp>
#include "src/storage/expressions/Variable.h"
#include "src/storage/prism/Program.h"
namespace storm {
namespace generator {
@ -48,12 +49,16 @@ namespace storm {
// A structure storing information about the used variables of the program.
struct VariableInformation {
VariableInformation(storm::expressions::ExpressionManager const& manager);
VariableInformation(storm::prism::Program const& program);
uint_fast64_t getTotalBitOffset() const;
// Provide methods to access the bit offset and width of variables in the compressed state.
uint_fast64_t getBitOffset(storm::expressions::Variable const& variable) const;
uint_fast64_t getBitWidth(storm::expressions::Variable const& variable) const;
// The total bit offset over all variables.
uint_fast64_t totalBitOffset;
// The known boolean variables.
boost::container::flat_map<storm::expressions::Variable, uint_fast64_t> booleanVariableToIndexMap;
std::vector<BooleanVariableInformation> booleanVariables;
@ -61,8 +66,6 @@ namespace storm {
// The known integer variables.
boost::container::flat_map<storm::expressions::Variable, uint_fast64_t> integerVariableToIndexMap;
std::vector<IntegerVariableInformation> integerVariables;
storm::expressions::ExpressionManager const& manager;
};
}

229
src/storage/BitVector.cpp

@ -60,7 +60,7 @@ namespace storm {
return currentIndex == other.currentIndex;
}
BitVector::BitVector() : bitCount(0), bucketVector() {
BitVector::BitVector() : bitCount(0), buckets(nullptr) {
// Intentionally left empty.
}
@ -73,10 +73,17 @@ namespace storm {
// Initialize the storage with the required values.
if (init) {
bucketVector = std::vector<uint64_t>(bucketCount, -1ll);
buckets = new uint64_t[bucketCount];
std::fill_n(buckets, bucketCount, -1ull);
truncateLastBucket();
} else {
bucketVector = std::vector<uint64_t>(bucketCount, 0);
buckets = new uint64_t[bucketCount]();
}
}
BitVector::~BitVector() {
if (buckets != nullptr) {
delete buckets;
}
}
@ -89,23 +96,22 @@ namespace storm {
// Intentionally left empty.
}
BitVector::BitVector(uint_fast64_t bucketCount, uint_fast64_t bitCount) : bitCount(bitCount), bucketVector(bucketCount) {
BitVector::BitVector(uint_fast64_t bucketCount, uint_fast64_t bitCount) : bitCount(bitCount) {
STORM_LOG_ASSERT((bucketCount << 6) == bitCount, "Bit count does not match number of buckets.");
buckets = new uint64_t[bucketCount]();
}
BitVector::BitVector(BitVector const& other) : bitCount(other.bitCount), bucketVector(other.bucketVector) {
// Intentionally left empty.
BitVector::BitVector(BitVector const& other) : bitCount(other.bitCount) {
buckets = new uint64_t[other.bucketCount()];
std::copy_n(other.buckets, other.bucketCount(), buckets);
}
//BitVector::BitVector(BitVector&& other) : bitCount(other.bitCount), bucketVector(std::move(other.bucketVector)) {
// Intentionally left empty.
//}
BitVector& BitVector::operator=(BitVector const& other) {
// Only perform the assignment if the source and target are not identical.
if (this != &other) {
bitCount = other.bitCount;
bucketVector = std::vector<uint64_t>(other.bucketVector);
buckets = new uint64_t[other.bucketCount()];
std::copy_n(other.buckets, other.bucketCount(), buckets);
}
return *this;
}
@ -117,9 +123,9 @@ namespace storm {
return false;
}
std::vector<uint64_t>::const_iterator first1 = this->bucketVector.begin();
std::vector<uint64_t>::const_iterator last1 = this->bucketVector.end();
std::vector<uint64_t>::const_iterator first2 = other.bucketVector.begin();
uint64_t* first1 = this->buckets;
uint64_t* last1 = this->buckets + this->bucketCount();
uint64_t* first2 = other.buckets;
for (; first1 != last1; ++first1, ++first2) {
if (*first1 < *first2) {
@ -135,7 +141,8 @@ namespace storm {
// Only perform the assignment if the source and target are not identical.
if (this != &other) {
bitCount = other.bitCount;
bucketVector = std::move(other.bucketVector);
this->buckets = other.buckets;
other.buckets = nullptr;
}
return *this;
@ -146,14 +153,7 @@ namespace storm {
if (this->bitCount != other.bitCount) return false;
// If the lengths match, we compare the buckets one by one.
for (std::vector<uint64_t>::const_iterator it1 = bucketVector.begin(), it2 = other.bucketVector.begin(); it1 != bucketVector.end(); ++it1, ++it2) {
if (*it1 != *it2) {
return false;
}
}
// All buckets were equal, so the bit vectors are equal.
return true;
return std::equal(this->buckets, this->buckets + this->bucketCount(), other.buckets);
}
bool BitVector::operator!=(BitVector const& other) const {
@ -166,9 +166,9 @@ namespace storm {
uint64_t mask = 1ull << (63 - (index & mod64mask));
if (value) {
bucketVector[bucket] |= mask;
buckets[bucket] |= mask;
} else {
bucketVector[bucket] &= ~mask;
buckets[bucket] &= ~mask;
}
}
@ -182,7 +182,7 @@ namespace storm {
bool BitVector::operator[](uint_fast64_t index) const {
uint64_t bucket = index >> 6;
uint64_t mask = 1ull << (63 - (index & mod64mask));
return (this->bucketVector[bucket] & mask) == mask;
return (this->buckets[bucket] & mask) == mask;
}
bool BitVector::get(uint_fast64_t index) const {
@ -197,89 +197,76 @@ namespace storm {
++newBucketCount;
}
if (newBucketCount > bucketVector.size()) {
if (newBucketCount > this->bucketCount()) {
uint64_t* newBuckets = new uint64_t[newBucketCount];
std::copy_n(buckets, this->bucketCount(), newBuckets);
if (init) {
bucketVector.back() |= ((1ull << (64 - (bitCount & mod64mask))) - 1ull);
bucketVector.resize(newBucketCount, -1ull);
newBuckets[this->bucketCount() - 1] |= ((1ull << (64 - (bitCount & mod64mask))) - 1ull);
std::fill_n(newBuckets, newBucketCount - this->bucketCount(), -1ull);
} else {
bucketVector.resize(newBucketCount, 0);
std::fill_n(newBuckets, newBucketCount - this->bucketCount(), 0);
}
delete buckets;
buckets = newBuckets;
bitCount = newLength;
} else {
// If the underlying storage does not need to grow, we have to insert the missing bits.
if (init) {
bucketVector.back() |= ((1ull << (64 - (bitCount & mod64mask))) - 1ull);
buckets[this->bucketCount() - 1] |= ((1ull << (64 - (bitCount & mod64mask))) - 1ull);
}
bitCount = newLength;
}
truncateLastBucket();
} else {
bitCount = newLength;
uint_fast64_t newBucketCount = newLength >> 6;
if ((newLength & mod64mask) != 0) {
++newBucketCount;
}
bucketVector.resize(newBucketCount);
// If the number of buckets needs to be reduced, we resize it now. Otherwise, we can just truncate the
// last bucket.
if (newBucketCount < this->bucketCount()) {
uint64_t* newBuckets = new uint64_t[newBucketCount];
std::copy_n(buckets, newBucketCount, newBuckets);
delete buckets;
buckets = newBuckets;
bitCount = newLength;
}
bitCount = newLength;
truncateLastBucket();
}
}
BitVector BitVector::operator&(BitVector const& other) const {
STORM_LOG_ASSERT(bitCount == other.bitCount, "Length of the bit vectors does not match.");
BitVector result(bitCount);
std::vector<uint64_t>::iterator it = result.bucketVector.begin();
for (std::vector<uint64_t>::const_iterator it1 = bucketVector.begin(), it2 = other.bucketVector.begin(); it != result.bucketVector.end(); ++it1, ++it2, ++it) {
*it = *it1 & *it2;
}
std::transform(this->buckets, this->buckets + this->bucketCount(), other.buckets, result.buckets, [] (uint64_t const& a, uint64_t const& b) { return a & b; });
return result;
}
BitVector& BitVector::operator&=(BitVector const& other) {
STORM_LOG_ASSERT(bitCount == other.bitCount, "Length of the bit vectors does not match.");
std::vector<uint64_t>::iterator it = bucketVector.begin();
for (std::vector<uint64_t>::const_iterator otherIt = other.bucketVector.begin(); it != bucketVector.end() && otherIt != other.bucketVector.end(); ++it, ++otherIt) {
*it &= *otherIt;
}
std::transform(this->buckets, this->buckets + this->bucketCount(), other.buckets, this->buckets, [] (uint64_t const& a, uint64_t const& b) { return a & b; });
return *this;
}
BitVector BitVector::operator|(BitVector const& other) const {
STORM_LOG_ASSERT(bitCount == other.bitCount, "Length of the bit vectors does not match.");
BitVector result(bitCount);
std::vector<uint64_t>::iterator it = result.bucketVector.begin();
for (std::vector<uint64_t>::const_iterator it1 = bucketVector.begin(), it2 = other.bucketVector.begin(); it != result.bucketVector.end(); ++it1, ++it2, ++it) {
*it = *it1 | *it2;
}
std::transform(this->buckets, this->buckets + this->bucketCount(), other.buckets, result.buckets, [] (uint64_t const& a, uint64_t const& b) { return a | b; });
return result;
}
BitVector& BitVector::operator|=(BitVector const& other) {
STORM_LOG_ASSERT(bitCount == other.bitCount, "Length of the bit vectors does not match.");
std::vector<uint64_t>::iterator it = bucketVector.begin();
for (std::vector<uint64_t>::const_iterator otherIt = other.bucketVector.begin(); it != bucketVector.end() && otherIt != other.bucketVector.end(); ++it, ++otherIt) {
*it |= *otherIt;
}
std::transform(this->buckets, this->buckets + this->bucketCount(), other.buckets, this->buckets, [] (uint64_t const& a, uint64_t const& b) { return a | b; });
return *this;
}
BitVector BitVector::operator^(BitVector const& other) const {
STORM_LOG_ASSERT(bitCount == other.bitCount, "Length of the bit vectors does not match.");
BitVector result(bitCount);
std::vector<uint64_t>::iterator it = result.bucketVector.begin();
for (std::vector<uint64_t>::const_iterator it1 = bucketVector.begin(), it2 = other.bucketVector.begin(); it != result.bucketVector.end(); ++it1, ++it2, ++it) {
*it = *it1 ^ *it2;
}
std::transform(this->buckets, this->buckets + this->bucketCount(), other.buckets, result.buckets, [] (uint64_t const& a, uint64_t const& b) { return a ^ b; });
result.truncateLastBucket();
return result;
}
@ -314,19 +301,13 @@ namespace storm {
BitVector BitVector::operator~() const {
BitVector result(this->bitCount);
std::vector<uint64_t>::iterator it = result.bucketVector.begin();
for (std::vector<uint64_t>::const_iterator it1 = bucketVector.begin(); it != result.bucketVector.end(); ++it1, ++it) {
*it = ~(*it1);
}
std::transform(this->buckets, this->buckets + this->bucketCount(), result.buckets, [] (uint64_t const& a) { return ~a; });
result.truncateLastBucket();
return result;
}
void BitVector::complement() {
for (auto& element : bucketVector) {
element = ~element;
}
std::transform(this->buckets, this->buckets + this->bucketCount(), this->buckets, [] (uint64_t const& a) { return ~a; });
truncateLastBucket();
}
@ -334,11 +315,7 @@ namespace storm {
STORM_LOG_ASSERT(bitCount == other.bitCount, "Length of the bit vectors does not match.");
BitVector result(bitCount);
std::vector<uint64_t>::iterator it = result.bucketVector.begin();
for (std::vector<uint64_t>::const_iterator it1 = bucketVector.begin(), it2 = other.bucketVector.begin(); it != result.bucketVector.end(); ++it1, ++it2, ++it) {
*it = ~(*it1) | *it2;
}
std::transform(this->buckets, this->buckets + this->bucketCount(), other.buckets, result.buckets, [] (uint64_t const& a, uint64_t const& b) { return (~a | b); });
result.truncateLastBucket();
return result;
}
@ -346,7 +323,11 @@ namespace storm {
bool BitVector::isSubsetOf(BitVector const& other) const {
STORM_LOG_ASSERT(bitCount == other.bitCount, "Length of the bit vectors does not match.");
for (std::vector<uint64_t>::const_iterator it1 = bucketVector.begin(), it2 = other.bucketVector.begin(); it1 != bucketVector.end(); ++it1, ++it2) {
uint64_t const* it1 = buckets;
uint64_t const* ite1 = buckets + bucketCount();
uint64_t const* it2 = other.buckets;
for (; it1 != ite1; ++it1, ++it2) {
if ((*it1 & *it2) != *it1) {
return false;
}
@ -357,7 +338,11 @@ namespace storm {
bool BitVector::isDisjointFrom(BitVector const& other) const {
STORM_LOG_ASSERT(bitCount == other.bitCount, "Length of the bit vectors does not match.");
for (std::vector<uint64_t>::const_iterator it1 = bucketVector.begin(), it2 = other.bucketVector.begin(); it1 != bucketVector.end(); ++it1, ++it2) {
uint64_t const* it1 = buckets;
uint64_t const* ite1 = buckets + bucketCount();
uint64_t const* it2 = other.buckets;
for (; it1 != ite1; ++it1, ++it2) {
if ((*it1 & *it2) != 0) {
return false;
}
@ -372,9 +357,9 @@ namespace storm {
// Compute the first bucket that needs to be checked and the number of buckets.
uint64_t index = bitIndex >> 6;
std::vector<uint64_t>::const_iterator first1 = bucketVector.begin() + index;
std::vector<uint64_t>::const_iterator first2 = other.bucketVector.begin();
std::vector<uint64_t>::const_iterator last2 = other.bucketVector.end();
uint64_t const* first1 = buckets + index;
uint64_t const* first2 = other.buckets;
uint64_t const* last2 = other.buckets + other.bucketCount();
for (; first2 != last2; ++first1, ++first2) {
if (*first1 != *first2) {
@ -391,10 +376,10 @@ namespace storm {
// Compute the first bucket that needs to be checked and the number of buckets.
uint64_t index = bitIndex >> 6;
std::vector<uint64_t>::iterator first1 = bucketVector.begin() + index;
std::vector<uint64_t>::const_iterator first2 = other.bucketVector.begin();
std::vector<uint64_t>::const_iterator last2 = other.bucketVector.end();
uint64_t* first1 = buckets + index;
uint64_t const* first2 = other.buckets;
uint64_t const* last2 = other.buckets + other.bucketCount();
for (; first2 != last2; ++first1, ++first2) {
*first1 = *first2;
}
@ -406,8 +391,8 @@ namespace storm {
STORM_LOG_ASSERT(index + numberOfBuckets <= this->bucketCount(), "Argument is out-of-range.");
storm::storage::BitVector result(numberOfBuckets, numberOfBits);
std::copy(this->bucketVector.begin() + index, this->bucketVector.begin() + index + numberOfBuckets, result.bucketVector.begin());
std::copy(this->buckets + index, this->buckets + index + numberOfBuckets, result.buckets);
result.truncateLastBucket();
return result;
}
@ -425,10 +410,10 @@ namespace storm {
if (bitIndexInBucket + numberOfBits < 64) {
// If the value stops before the end of the bucket, we need to erase some lower bits.
mask &= ~((1ull << (64 - (bitIndexInBucket + numberOfBits))) - 1ull);
return (bucketVector[bucket] & mask) >> (64 - (bitIndexInBucket + numberOfBits));
return (buckets[bucket] & mask) >> (64 - (bitIndexInBucket + numberOfBits));
} else if (bitIndexInBucket + numberOfBits > 64) {
// In this case, the integer "crosses" the bucket line.
uint64_t result = (bucketVector[bucket] & mask);
uint64_t result = (buckets[bucket] & mask);
++bucket;
// Compute the remaining number of bits.
@ -440,13 +425,13 @@ namespace storm {
// Strip away everything from the second bucket that is beyond the final index and add it to the
// intermediate result.
mask = ~((1ull << (64 - numberOfBits)) - 1ull);
uint64_t lowerBits = bucketVector[bucket] & mask;
uint64_t lowerBits = buckets[bucket] & mask;
result |= (lowerBits >> (64 - numberOfBits));
return result;
} else {
// In this case, it suffices to take the current mask.
return bucketVector[bucket] & mask;
return buckets[bucket] & mask;
}
}
@ -466,10 +451,10 @@ namespace storm {
if (bitIndexInBucket + numberOfBits < 64) {
// If the value stops before the end of the bucket, we need to erase some lower bits.
mask &= ~((1ull << (64 - (bitIndexInBucket + numberOfBits))) - 1ull);
bucketVector[bucket] = (bucketVector[bucket] & ~mask) | (value << (64 - (bitIndexInBucket + numberOfBits)));
buckets[bucket] = (buckets[bucket] & ~mask) | (value << (64 - (bitIndexInBucket + numberOfBits)));
} else if (bitIndexInBucket + numberOfBits > 64) {
// Write the part of the value that falls into the first bucket.
bucketVector[bucket] = (bucketVector[bucket] & ~mask) | (value >> (numberOfBits + (bitIndexInBucket - 64)));
buckets[bucket] = (buckets[bucket] & ~mask) | (value >> (numberOfBits + (bitIndexInBucket - 64)));
++bucket;
// Compute the remaining number of bits.
@ -480,45 +465,41 @@ namespace storm {
// Put the remaining bits in their place.
mask = ((1ull << (64 - numberOfBits)) - 1ull);
bucketVector[bucket] = (bucketVector[bucket] & mask) | value;
buckets[bucket] = (buckets[bucket] & mask) | value;
} else {
bucketVector[bucket] = (bucketVector[bucket] & ~mask) | value;
buckets[bucket] = (buckets[bucket] & ~mask) | value;
}
}
bool BitVector::empty() const {
for (auto& element : bucketVector) {
if (element != 0) {
return false;
}
}
return true;
uint64_t* last = buckets + bucketCount();
uint64_t* it = std::find(buckets, last, 0);
return it != last;
}
bool BitVector::full() const {
// Check that all buckets except the last one have all bits set.
for (uint_fast64_t index = 0; index < bucketVector.size() - 1; ++index) {
if (bucketVector[index] != -1ull) {
uint64_t* last = buckets + bucketCount() - 1;
for (uint64_t const* it = buckets; it != last; ++it) {
if (*it != -1ull) {
return false;
}
}
// Now check whether the relevant bits are set in the last bucket.
uint64_t mask = ~((1ull << (64 - (bitCount & mod64mask))) - 1ull);
if ((bucketVector.back() & mask) != mask) {
if ((*last & mask) != mask) {
return false;
}
return true;
}
void BitVector::clear() {
for (auto& element : bucketVector) {
element = 0;
}
std::fill_n(buckets, this->bucketCount(), 0);
}
uint_fast64_t BitVector::getNumberOfSetBits() const {
return getNumberOfSetBitsBeforeIndex(bucketVector.size() << 6);
return getNumberOfSetBitsBeforeIndex(bitCount);
}
uint_fast64_t BitVector::getNumberOfSetBitsBeforeIndex(uint_fast64_t index) const {
@ -529,14 +510,14 @@ namespace storm {
for (uint_fast64_t i = 0; i < bucket; ++i) {
// Check if we are using g++ or clang++ and, if so, use the built-in function
#if (defined (__GNUG__) || defined(__clang__))
result += __builtin_popcountll(bucketVector[i]);
result += __builtin_popcountll(buckets[i]);
#elif defined WINDOWS
#include <nmmintrin.h>
// If the target machine does not support SSE4, this will fail.
result += _mm_popcnt_u64(bucketVector[i]);
#else
uint_fast32_t cnt;
uint_fast64_t bitset = bucketVector[i];
uint_fast64_t bitset = buckets[i];
for (cnt = 0; bitset; cnt++) {
bitset &= bitset - 1;
}
@ -548,7 +529,7 @@ namespace storm {
uint64_t tmp = index & mod64mask;
if (tmp != 0) {
tmp = ~((1ll << (64 - (tmp & mod64mask))) - 1ll);
tmp &= bucketVector[bucket];
tmp &= buckets[bucket];
// Check if we are using g++ or clang++ and, if so, use the built-in function
#if (defined (__GNUG__) || defined(__clang__))
result += __builtin_popcountll(tmp);
@ -585,19 +566,19 @@ namespace storm {
}
std::size_t BitVector::getSizeInBytes() const {
return sizeof (*this) + sizeof (uint64_t) * bucketVector.size();
return sizeof (*this) + sizeof (uint64_t) * bucketCount();
}
BitVector::const_iterator BitVector::begin() const {
return const_iterator(bucketVector.data(), 0, bitCount);
return const_iterator(buckets, 0, bitCount);
}
BitVector::const_iterator BitVector::end() const {
return const_iterator(bucketVector.data(), bitCount, bitCount, false);
return const_iterator(buckets, bitCount, bitCount, false);
}
uint_fast64_t BitVector::getNextSetIndex(uint_fast64_t startingIndex) const {
return getNextSetIndex(bucketVector.data(), startingIndex, bitCount);
return getNextSetIndex(buckets, startingIndex, bitCount);
}
uint_fast64_t BitVector::getNextSetIndex(uint64_t const* dataPtr, uint_fast64_t startingIndex, uint_fast64_t endIndex) {
@ -641,17 +622,17 @@ namespace storm {
void BitVector::truncateLastBucket() {
if ((bitCount & mod64mask) != 0) {
bucketVector.back() &= ~((1ll << (64 - (bitCount & mod64mask))) - 1ll);
buckets[bucketCount() - 1] &= ~((1ll << (64 - (bitCount & mod64mask))) - 1ll);
}
}
size_t BitVector::bucketCount() const {
return bucketVector.size();
return bitCount >> 6;
}
std::ostream& operator<<(std::ostream& out, BitVector const& bitVector) {
out << "bit vector(" << bitVector.getNumberOfSetBits() << "/" << bitVector.bitCount << ") [";
for (auto index : bitVector) {
std::ostream& operator<<(std::ostream& out, BitVector const& bitvector) {
out << "bit vector(" << bitvector.getNumberOfSetBits() << "/" << bitvector.bitCount << ") [";
for (auto index : bitvector) {
out << index << " ";
}
out << "]";
@ -659,13 +640,13 @@ namespace storm {
return out;
}
std::size_t NonZeroBitVectorHash::operator()(storm::storage::BitVector const& bv) const {
STORM_LOG_ASSERT(bv.size() > 0, "Cannot hash bit vector of zero size.");
std::size_t NonZeroBitVectorHash::operator()(storm::storage::BitVector const& bitvector) const {
STORM_LOG_ASSERT(bitvector.size() > 0, "Cannot hash bit vector of zero size.");
std::size_t result = 0;
for (uint_fast64_t index = 0; index < bv.bucketVector.size(); ++index) {
for (uint_fast64_t index = 0; index < bitvector.bucketCount(); ++index) {
result ^= result << 3;
result ^= result >> bv.getAsInt(index << 6, 5);
result ^= result >> bitvector.getAsInt(index << 6, 5);
}
// Erase the last bit and add one to definitely make this hash value non-zero.

7
src/storage/BitVector.h

@ -104,6 +104,11 @@ namespace storm {
*/
BitVector();
/*
* Deconstructs a bit vector by deleting the underlying storage.
*/
~BitVector();
/*!
* Constructs a bit vector which can hold the given number of bits and initializes all bits with the
* provided truth value.
@ -494,7 +499,7 @@ namespace storm {
uint_fast64_t bitCount;
// The underlying storage of 64-bit buckets for all bits of this bit vector.
std::vector<uint64_t> bucketVector;
uint64_t* buckets;
// A bit mask that can be used to reduce a modulo 64 operation to a logical "and".
static const uint_fast64_t mod64mask = (1 << 6) - 1;

Loading…
Cancel
Save