diff --git a/src/storm/modelchecker/multiobjective/SparseMultiObjectivePreprocessor.cpp b/src/storm/modelchecker/multiobjective/SparseMultiObjectivePreprocessor.cpp index 0596303c8..09f0fdc6a 100644 --- a/src/storm/modelchecker/multiobjective/SparseMultiObjectivePreprocessor.cpp +++ b/src/storm/modelchecker/multiobjective/SparseMultiObjectivePreprocessor.cpp @@ -69,9 +69,7 @@ namespace storm { template SparseMultiObjectivePreprocessor::PreprocessorData::PreprocessorData(SparseModelType const& model) : originalModel(model) { - storm::storage::MemoryStructureBuilder memoryBuilder(1, model); - memoryBuilder.setTransition(0,0, storm::storage::BitVector(model.getNumberOfStates(), true)); - memory = std::make_shared(memoryBuilder.build()); + memory = std::make_shared(storm::storage::MemoryStructureBuilder::buildTrivialMemoryStructure(model)); // The memoryLabelPrefix should not be a prefix of a state label of the given model to ensure uniqueness of label names memoryLabelPrefix = "mem"; diff --git a/src/storm/models/sparse/NondeterministicModel.cpp b/src/storm/models/sparse/NondeterministicModel.cpp index e823d455a..3382bfd95 100644 --- a/src/storm/models/sparse/NondeterministicModel.cpp +++ b/src/storm/models/sparse/NondeterministicModel.cpp @@ -2,6 +2,9 @@ #include "storm/models/sparse/StandardRewardModel.h" #include "storm/models/sparse/MarkovAutomaton.h" +#include "storm/storage/Scheduler.h" +#include "storm/storage/memorystructure/MemoryStructureBuilder.h" +#include "storm/storage/memorystructure/SparseModelMemoryProduct.h" #include "storm/adapters/RationalFunctionAdapter.h" @@ -46,6 +49,26 @@ namespace storm { } } + template + std::shared_ptr> NondeterministicModel::applyScheduler(storm::storage::Scheduler const& scheduler, bool dropUnreachableStates) { + if (scheduler.isMemorylessScheduler()) { + auto memStruct = storm::storage::MemoryStructureBuilder::buildTrivialMemoryStructure(*this); + auto memoryProduct = memStruct.product(*this); + if (!dropUnreachableStates) { + memoryProduct.setBuildFullProduct(); + } + return memoryProduct.build(scheduler); + } else { + boost::optional const& memStruct = scheduler.getMemoryStructure(); + STORM_LOG_ASSERT(memStruct, "Memoryless scheduler without memory structure."); + auto memoryProduct = memStruct->product(*this); + if (!dropUnreachableStates) { + memoryProduct.setBuildFullProduct(); + } + return memoryProduct.build(scheduler); + } + } + template void NondeterministicModel::printModelInformationToStream(std::ostream& out) const { this->printModelInformationHeaderToStream(out); diff --git a/src/storm/models/sparse/NondeterministicModel.h b/src/storm/models/sparse/NondeterministicModel.h index 1f2bd157a..ab0a21a04 100644 --- a/src/storm/models/sparse/NondeterministicModel.h +++ b/src/storm/models/sparse/NondeterministicModel.h @@ -5,6 +5,13 @@ #include "storm/utility/OsDetection.h" namespace storm { + + // Forward declare Scheduler class. + namespace storage { + template + class Scheduler; + } + namespace models { namespace sparse { @@ -48,6 +55,13 @@ namespace storm { virtual void reduceToStateBasedRewards() override; + /*! + * Applies the given scheduler to this model. + * @param scheduler the considered scheduler. + * @param dropUnreachableStates if set, the resulting model only considers the states that are reachable from an initial state + */ + std::shared_ptr> applyScheduler(storm::storage::Scheduler const& scheduler, bool dropUnreachableStates = true); + virtual void printModelInformationToStream(std::ostream& out) const override; virtual void writeDotToStream(std::ostream& outStream, bool includeLabeling = true, storm::storage::BitVector const* subsystem = nullptr, std::vector const* firstValue = nullptr, std::vector const* secondValue = nullptr, std::vector const* stateColoring = nullptr, std::vector const* colors = nullptr, std::vector* scheduler = nullptr, bool finalizeOutput = true) const override; diff --git a/src/storm/storage/Distribution.cpp b/src/storm/storage/Distribution.cpp index 0c2ac73db..9d0d89d19 100644 --- a/src/storm/storage/Distribution.cpp +++ b/src/storm/storage/Distribution.cpp @@ -156,6 +156,18 @@ namespace storm { return false; } + template + ValueType Distribution::getProbability(StateType const& state) const { + auto it = this->distribution.find(state); + if (it == this->distribution.end()) { + return storm::utility::zero(); + } else { + return it->second; + } + } + + + template class Distribution; template std::ostream& operator<<(std::ostream& out, Distribution const& distribution); template class Distribution; diff --git a/src/storm/storage/Distribution.h b/src/storm/storage/Distribution.h index ff4b0689b..328e87f98 100644 --- a/src/storm/storage/Distribution.h +++ b/src/storm/storage/Distribution.h @@ -73,7 +73,7 @@ namespace storm { * entry is removed. */ void shiftProbability(StateType const& fromState, StateType const& toState, ValueType const& probability, storm::utility::ConstantsComparator const& comparator = storm::utility::ConstantsComparator()); - + /*! * Retrieves an iterator to the elements in this distribution. * @@ -132,6 +132,14 @@ namespace storm { bool less(Distribution const& other, storm::utility::ConstantsComparator const& comparator) const; + + /*! + * Returns the probability of the given state + * @param state The state for which the probability is returned. + * @return The probability of the given state. + */ + ValueType getProbability(StateType const& state) const; + private: // A list of states and the probabilities that are assigned to them. container_type distribution; diff --git a/src/storm/storage/Scheduler.cpp b/src/storm/storage/Scheduler.cpp index a1dc1ecbd..7ddc7e457 100644 --- a/src/storm/storage/Scheduler.cpp +++ b/src/storm/storage/Scheduler.cpp @@ -86,6 +86,11 @@ namespace storm { return memoryStructure ? memoryStructure->getNumberOfStates() : 1; } + template + boost::optional const& Scheduler::getMemoryStructure() const { + return memoryStructure; + } + template void Scheduler::printToStream(std::ostream& out, std::shared_ptr> model, bool skipUniqueChoices) const { STORM_LOG_THROW(model == nullptr || model->getNumberOfStates() == schedulerChoices.front().size(), storm::exceptions::InvalidOperationException, "The given model is not compatible with this scheduler."); diff --git a/src/storm/storage/Scheduler.h b/src/storm/storage/Scheduler.h index c1c3a5f88..b51e308b9 100644 --- a/src/storm/storage/Scheduler.h +++ b/src/storm/storage/Scheduler.h @@ -70,6 +70,11 @@ namespace storm { * Retrieves the number of memory states this scheduler considers. */ uint_fast64_t getNumberOfMemoryStates() const; + + /*! + * Retrieves the memory structure associated with this scheduler + */ + boost::optional const& getMemoryStructure() const; /*! * Returns a copy of this scheduler with the new value type diff --git a/src/storm/storage/memorystructure/MemoryStructure.cpp b/src/storm/storage/memorystructure/MemoryStructure.cpp index 3d9348c44..499bc4db6 100644 --- a/src/storm/storage/memorystructure/MemoryStructure.cpp +++ b/src/storm/storage/memorystructure/MemoryStructure.cpp @@ -106,9 +106,9 @@ namespace storm { return MemoryStructure(std::move(resultTransitions), std::move(resultLabeling), std::move(resultInitialMemoryStates)); } - template - SparseModelMemoryProduct MemoryStructure::product(storm::models::sparse::Model const& sparseModel) const { - return SparseModelMemoryProduct(sparseModel, *this); + template + SparseModelMemoryProduct MemoryStructure::product(storm::models::sparse::Model const& sparseModel) const { + return SparseModelMemoryProduct(sparseModel, *this); } std::string MemoryStructure::toString() const { @@ -143,7 +143,9 @@ namespace storm { } template SparseModelMemoryProduct MemoryStructure::product(storm::models::sparse::Model const& sparseModel) const; + template SparseModelMemoryProduct> MemoryStructure::product(storm::models::sparse::Model> const& sparseModel) const; template SparseModelMemoryProduct MemoryStructure::product(storm::models::sparse::Model const& sparseModel) const; + template SparseModelMemoryProduct MemoryStructure::product(storm::models::sparse::Model const& sparseModel) const; } } diff --git a/src/storm/storage/memorystructure/MemoryStructure.h b/src/storm/storage/memorystructure/MemoryStructure.h index 9df320fd3..64bd7aaa9 100644 --- a/src/storm/storage/memorystructure/MemoryStructure.h +++ b/src/storm/storage/memorystructure/MemoryStructure.h @@ -6,11 +6,12 @@ #include "storm/logic/Formula.h" #include "storm/models/sparse/StateLabeling.h" #include "storm/models/sparse/Model.h" +#include "storm/models/sparse/StandardRewardModel.h" namespace storm { namespace storage { - template + template class SparseModelMemoryProduct; /*! @@ -53,8 +54,8 @@ namespace storm { * Builds the product of this memory structure and the given sparse model. * An exception is thrown if the state labelings of this memory structure and the given model are not disjoint. */ - template - SparseModelMemoryProduct product(storm::models::sparse::Model const& sparseModel) const; + template > + SparseModelMemoryProduct product(storm::models::sparse::Model const& sparseModel) const; std::string toString() const; diff --git a/src/storm/storage/memorystructure/MemoryStructureBuilder.cpp b/src/storm/storage/memorystructure/MemoryStructureBuilder.cpp index 9ee23eaa6..148cbf2ca 100644 --- a/src/storm/storage/memorystructure/MemoryStructureBuilder.cpp +++ b/src/storm/storage/memorystructure/MemoryStructureBuilder.cpp @@ -83,7 +83,15 @@ namespace storm { return MemoryStructure(std::move(transitions), std::move(stateLabeling), std::move(initialMemoryStates)); } + template + MemoryStructure MemoryStructureBuilder::buildTrivialMemoryStructure(storm::models::sparse::Model const& model) { + MemoryStructureBuilder memoryBuilder(1, model); + memoryBuilder.setTransition(0,0, storm::storage::BitVector(model.getNumberOfStates(), true)); + return memoryBuilder.build(); + } + template class MemoryStructureBuilder; + template class MemoryStructureBuilder>; template class MemoryStructureBuilder; template class MemoryStructureBuilder; diff --git a/src/storm/storage/memorystructure/MemoryStructureBuilder.h b/src/storm/storage/memorystructure/MemoryStructureBuilder.h index 9fbe65c78..1cb51f384 100644 --- a/src/storm/storage/memorystructure/MemoryStructureBuilder.h +++ b/src/storm/storage/memorystructure/MemoryStructureBuilder.h @@ -57,6 +57,11 @@ namespace storm { */ MemoryStructure build(); + /*! + * Builds a trivial memory structure for the given model (consisting of a single memory state) + */ + static MemoryStructure buildTrivialMemoryStructure(storm::models::sparse::Model const& model); + private: storm::models::sparse::Model const& model; diff --git a/src/storm/storage/memorystructure/SparseModelMemoryProduct.cpp b/src/storm/storage/memorystructure/SparseModelMemoryProduct.cpp index 56b3a6aa0..58f773cfe 100644 --- a/src/storm/storage/memorystructure/SparseModelMemoryProduct.cpp +++ b/src/storm/storage/memorystructure/SparseModelMemoryProduct.cpp @@ -17,17 +17,29 @@ namespace storm { namespace storage { - template - SparseModelMemoryProduct::SparseModelMemoryProduct(storm::models::sparse::Model const& sparseModel, storm::storage::MemoryStructure const& memoryStructure) : model(sparseModel), memory(memoryStructure) { - // intentionally left empty + template + SparseModelMemoryProduct::SparseModelMemoryProduct(storm::models::sparse::Model const& sparseModel, storm::storage::MemoryStructure const& memoryStructure) : model(sparseModel), memory(memoryStructure) { + reachableStates = storm::storage::BitVector(model.getNumberOfStates() * memory.getNumberOfStates(), false); } + + template + void SparseModelMemoryProduct::addReachableState(uint64_t const& modelState, uint64_t const& memoryState) { + reachableStates.set(modelState * memory.getNumberOfStates() + memoryState, true); + } + + template + void SparseModelMemoryProduct::setBuildFullProduct() { + reachableStates.clear(); + reachableStates.complement(); + } + + template + std::shared_ptr> SparseModelMemoryProduct::build(boost::optional> const& scheduler) { - template - std::shared_ptr> SparseModelMemoryProduct::build() { - uint_fast64_t modelStateCount = model.getNumberOfStates(); - uint_fast64_t memoryStateCount = memory.getNumberOfStates(); + uint64_t modelStateCount = model.getNumberOfStates(); + uint64_t memoryStateCount = memory.getNumberOfStates(); - std::vector memorySuccessors = computeMemorySuccessors(); + std::vector memorySuccessors = computeMemorySuccessors(); // Get the initial states and reachable states. A stateIndex s corresponds to the model state (s / memoryStateCount) and memory state (s % memoryStateCount) storm::storage::BitVector initialStates(modelStateCount * memoryStateCount, false); @@ -38,44 +50,49 @@ namespace storm { } STORM_LOG_ASSERT(memoryInitIt == memory.getInitialMemoryStates().end(), "Unexpected number of initial states."); - storm::storage::BitVector reachableStates = computeReachableStates(memorySuccessors, initialStates); + computeReachableStates(memorySuccessors, initialStates, scheduler); // Compute the mapping to the states of the result - uint_fast64_t reachableStateCount = 0; - toResultStateMapping = std::vector (model.getNumberOfStates() * memory.getNumberOfStates(), std::numeric_limits::max()); + uint64_t reachableStateCount = 0; + toResultStateMapping = std::vector (model.getNumberOfStates() * memory.getNumberOfStates(), std::numeric_limits::max()); for (auto const& reachableState : reachableStates) { toResultStateMapping[reachableState] = reachableStateCount; ++reachableStateCount; } // Build the model components - storm::storage::SparseMatrix transitionMatrix = model.getTransitionMatrix().hasTrivialRowGrouping() ? - buildDeterministicTransitionMatrix(reachableStates, memorySuccessors) : - buildNondeterministicTransitionMatrix(reachableStates, memorySuccessors); + storm::storage::SparseMatrix transitionMatrix; + if (scheduler) { + transitionMatrix = buildTransitionMatrixForScheduler(memorySuccessors, scheduler.get()); + } else if (model.getTransitionMatrix().hasTrivialRowGrouping()) { + transitionMatrix = buildDeterministicTransitionMatrix(memorySuccessors); + } else { + transitionMatrix = buildNondeterministicTransitionMatrix(memorySuccessors); + } storm::models::sparse::StateLabeling labeling = buildStateLabeling(transitionMatrix); - std::unordered_map> rewardModels = buildRewardModels(transitionMatrix, memorySuccessors); + std::unordered_map rewardModels = buildRewardModels(transitionMatrix, memorySuccessors, scheduler); // Add the label for the initial states. We need to translate the state indices w.r.t. the set of reachable states. labeling.addLabel("init", initialStates % reachableStates); - return buildResult(std::move(transitionMatrix), std::move(labeling), std::move(rewardModels)); + return buildResult(std::move(transitionMatrix), std::move(labeling), std::move(rewardModels), scheduler); } - template - uint_fast64_t const& SparseModelMemoryProduct::getResultState(uint_fast64_t const& modelState, uint_fast64_t const& memoryState) const { + template + uint64_t const& SparseModelMemoryProduct::getResultState(uint64_t const& modelState, uint64_t const& memoryState) const { return toResultStateMapping[modelState * memory.getNumberOfStates() + memoryState]; } - template - std::vector SparseModelMemoryProduct::computeMemorySuccessors() const { - uint_fast64_t modelTransitionCount = model.getTransitionMatrix().getEntryCount(); - uint_fast64_t memoryStateCount = memory.getNumberOfStates(); - std::vector result(modelTransitionCount * memoryStateCount, std::numeric_limits::max()); + template + std::vector SparseModelMemoryProduct::computeMemorySuccessors() const { + uint64_t modelTransitionCount = model.getTransitionMatrix().getEntryCount(); + uint64_t memoryStateCount = memory.getNumberOfStates(); + std::vector result(modelTransitionCount * memoryStateCount, std::numeric_limits::max()); - for (uint_fast64_t memoryState = 0; memoryState < memoryStateCount; ++memoryState) { - for (uint_fast64_t transitionGoal = 0; transitionGoal < memoryStateCount; ++transitionGoal) { + for (uint64_t memoryState = 0; memoryState < memoryStateCount; ++memoryState) { + for (uint64_t transitionGoal = 0; transitionGoal < memoryStateCount; ++transitionGoal) { auto const& memoryTransition = memory.getTransitionMatrix()[memoryState][transitionGoal]; if (memoryTransition) { for (auto const& modelTransitionIndex : memoryTransition.get()) { @@ -87,54 +104,76 @@ namespace storm { return result; } - template - storm::storage::BitVector SparseModelMemoryProduct::computeReachableStates(std::vector const& memorySuccessors, storm::storage::BitVector const& initialStates) const { - uint_fast64_t memoryStateCount = memory.getNumberOfStates(); + template + void SparseModelMemoryProduct::computeReachableStates(std::vector const& memorySuccessors, storm::storage::BitVector const& initialStates, boost::optional> const& scheduler) { + uint64_t memoryStateCount = memory.getNumberOfStates(); // Explore the reachable states via DFS. // A state s on the stack corresponds to the model state (s / memoryStateCount) and memory state (s % memoryStateCount) - storm::storage::BitVector reachableStates = initialStates; - std::vector stack(reachableStates.begin(), reachableStates.end()); - while (!stack.empty()) { - uint_fast64_t stateIndex = stack.back(); - stack.pop_back(); - uint_fast64_t modelState = stateIndex / memoryStateCount; - uint_fast64_t memoryState = stateIndex % memoryStateCount; - - auto const& rowGroup = model.getTransitionMatrix().getRowGroup(modelState); - for (auto modelTransitionIt = rowGroup.begin(); modelTransitionIt != rowGroup.end(); ++modelTransitionIt) { - if (!storm::utility::isZero(modelTransitionIt->getValue())) { - uint_fast64_t successorModelState = modelTransitionIt->getColumn(); - uint_fast64_t modelTransitionId = modelTransitionIt - model.getTransitionMatrix().begin(); - uint_fast64_t successorMemoryState = memorySuccessors[modelTransitionId * memoryStateCount + memoryState]; - uint_fast64_t successorStateIndex = successorModelState * memoryStateCount + successorMemoryState; - if (!reachableStates.get(successorStateIndex)) { - reachableStates.set(successorStateIndex, true); - stack.push_back(successorStateIndex); + reachableStates |= initialStates; + if (!reachableStates.full()) { + std::vector stack(reachableStates.begin(), reachableStates.end()); + while (!stack.empty()) { + uint64_t stateIndex = stack.back(); + stack.pop_back(); + uint64_t modelState = stateIndex / memoryStateCount; + uint64_t memoryState = stateIndex % memoryStateCount; + + if (scheduler) { + auto choices = scheduler->getChoice(modelState, memoryState).getChoiceAsDistribution(); + uint64_t groupStart = model.getTransitionMatrix().getRowGroupIndices()[modelState]; + for (auto const& choice : choices) { + STORM_LOG_ASSERT(groupStart + choice.first < model.getTransitionMatrix().getRowGroupIndices()[modelState + 1], "Invalid choice " << choice.first << " at model state " << modelState << "."); + auto const& row = model.getTransitionMatrix().getRow(groupStart + choice.first); + for (auto modelTransitionIt = row.begin(); modelTransitionIt != row.end(); ++modelTransitionIt) { + if (!storm::utility::isZero(modelTransitionIt->getValue())) { + uint64_t successorModelState = modelTransitionIt->getColumn(); + uint64_t modelTransitionId = modelTransitionIt - model.getTransitionMatrix().begin(); + uint64_t successorMemoryState = memorySuccessors[modelTransitionId * memoryStateCount + memoryState]; + uint64_t successorStateIndex = successorModelState * memoryStateCount + successorMemoryState; + if (!reachableStates.get(successorStateIndex)) { + reachableStates.set(successorStateIndex, true); + stack.push_back(successorStateIndex); + } + } + } + } + } else { + auto const& rowGroup = model.getTransitionMatrix().getRowGroup(modelState); + for (auto modelTransitionIt = rowGroup.begin(); modelTransitionIt != rowGroup.end(); ++modelTransitionIt) { + if (!storm::utility::isZero(modelTransitionIt->getValue())) { + uint64_t successorModelState = modelTransitionIt->getColumn(); + uint64_t modelTransitionId = modelTransitionIt - model.getTransitionMatrix().begin(); + uint64_t successorMemoryState = memorySuccessors[modelTransitionId * memoryStateCount + memoryState]; + uint64_t successorStateIndex = successorModelState * memoryStateCount + successorMemoryState; + if (!reachableStates.get(successorStateIndex)) { + reachableStates.set(successorStateIndex, true); + stack.push_back(successorStateIndex); + } + } } } } } - return reachableStates; } - - template - storm::storage::SparseMatrix SparseModelMemoryProduct::buildDeterministicTransitionMatrix(storm::storage::BitVector const& reachableStates, std::vector const& memorySuccessors) const { - uint_fast64_t memoryStateCount = memory.getNumberOfStates(); - uint_fast64_t numResStates = reachableStates.getNumberOfSetBits(); - uint_fast64_t numResTransitions = 0; + + template + storm::storage::SparseMatrix SparseModelMemoryProduct::buildDeterministicTransitionMatrix(std::vector const& memorySuccessors) const { + uint64_t memoryStateCount = memory.getNumberOfStates(); + uint64_t numResStates = reachableStates.getNumberOfSetBits(); + uint64_t numResTransitions = 0; for (auto const& stateIndex : reachableStates) { numResTransitions += model.getTransitionMatrix().getRow(stateIndex / memoryStateCount).getNumberOfEntries(); } storm::storage::SparseMatrixBuilder builder(numResStates, numResStates, numResTransitions, true); - uint_fast64_t currentRow = 0; + uint64_t currentRow = 0; for (auto const& stateIndex : reachableStates) { - uint_fast64_t modelState = stateIndex / memoryStateCount; - uint_fast64_t memoryState = stateIndex % memoryStateCount; + uint64_t modelState = stateIndex / memoryStateCount; + uint64_t memoryState = stateIndex % memoryStateCount; auto const& modelRow = model.getTransitionMatrix().getRow(modelState); for (auto entryIt = modelRow.begin(); entryIt != modelRow.end(); ++entryIt) { - uint_fast64_t transitionId = entryIt - model.getTransitionMatrix().begin(); - uint_fast64_t const& successorMemoryState = memorySuccessors[transitionId * memoryStateCount + memoryState]; + uint64_t transitionId = entryIt - model.getTransitionMatrix().begin(); + uint64_t const& successorMemoryState = memorySuccessors[transitionId * memoryStateCount + memoryState]; builder.addNextValue(currentRow, getResultState(entryIt->getColumn(), successorMemoryState), entryIt->getValue()); } ++currentRow; @@ -143,31 +182,31 @@ namespace storm { return builder.build(); } - template - storm::storage::SparseMatrix SparseModelMemoryProduct::buildNondeterministicTransitionMatrix(storm::storage::BitVector const& reachableStates, std::vector const& memorySuccessors) const { - uint_fast64_t memoryStateCount = memory.getNumberOfStates(); - uint_fast64_t numResStates = reachableStates.getNumberOfSetBits(); - uint_fast64_t numResChoices = 0; - uint_fast64_t numResTransitions = 0; + template + storm::storage::SparseMatrix SparseModelMemoryProduct::buildNondeterministicTransitionMatrix(std::vector const& memorySuccessors) const { + uint64_t memoryStateCount = memory.getNumberOfStates(); + uint64_t numResStates = reachableStates.getNumberOfSetBits(); + uint64_t numResChoices = 0; + uint64_t numResTransitions = 0; for (auto const& stateIndex : reachableStates) { - uint_fast64_t modelState = stateIndex / memoryStateCount; - for (uint_fast64_t modelRow = model.getTransitionMatrix().getRowGroupIndices()[modelState]; modelRow < model.getTransitionMatrix().getRowGroupIndices()[modelState + 1]; ++modelRow) { + uint64_t modelState = stateIndex / memoryStateCount; + for (uint64_t modelRow = model.getTransitionMatrix().getRowGroupIndices()[modelState]; modelRow < model.getTransitionMatrix().getRowGroupIndices()[modelState + 1]; ++modelRow) { ++numResChoices; numResTransitions += model.getTransitionMatrix().getRow(modelRow).getNumberOfEntries(); } } storm::storage::SparseMatrixBuilder builder(numResChoices, numResStates, numResTransitions, true, true, numResStates); - uint_fast64_t currentRow = 0; + uint64_t currentRow = 0; for (auto const& stateIndex : reachableStates) { - uint_fast64_t modelState = stateIndex / memoryStateCount; - uint_fast64_t memoryState = stateIndex % memoryStateCount; + uint64_t modelState = stateIndex / memoryStateCount; + uint64_t memoryState = stateIndex % memoryStateCount; builder.newRowGroup(currentRow); - for (uint_fast64_t modelRowIndex = model.getTransitionMatrix().getRowGroupIndices()[modelState]; modelRowIndex < model.getTransitionMatrix().getRowGroupIndices()[modelState + 1]; ++modelRowIndex) { + for (uint64_t modelRowIndex = model.getTransitionMatrix().getRowGroupIndices()[modelState]; modelRowIndex < model.getTransitionMatrix().getRowGroupIndices()[modelState + 1]; ++modelRowIndex) { auto const& modelRow = model.getTransitionMatrix().getRow(modelRowIndex); for (auto entryIt = modelRow.begin(); entryIt != modelRow.end(); ++entryIt) { - uint_fast64_t transitionId = entryIt - model.getTransitionMatrix().begin(); - uint_fast64_t const& successorMemoryState = memorySuccessors[transitionId * memoryStateCount + memoryState]; + uint64_t transitionId = entryIt - model.getTransitionMatrix().begin(); + uint64_t const& successorMemoryState = memorySuccessors[transitionId * memoryStateCount + memoryState]; builder.addNextValue(currentRow, getResultState(entryIt->getColumn(), successorMemoryState), entryIt->getValue()); } ++currentRow; @@ -177,20 +216,115 @@ namespace storm { return builder.build(); } - template - storm::models::sparse::StateLabeling SparseModelMemoryProduct::buildStateLabeling(storm::storage::SparseMatrix const& resultTransitionMatrix) const { - uint_fast64_t modelStateCount = model.getNumberOfStates(); - uint_fast64_t memoryStateCount = memory.getNumberOfStates(); + template + storm::storage::SparseMatrix SparseModelMemoryProduct::buildTransitionMatrixForScheduler(std::vector const& memorySuccessors, storm::storage::Scheduler const& scheduler) const { + uint64_t memoryStateCount = memory.getNumberOfStates(); + uint64_t numResStates = reachableStates.getNumberOfSetBits(); + uint64_t numResChoices = 0; + uint64_t numResTransitions = 0; + bool hasTrivialNondeterminism = true; + for (auto const& stateIndex : reachableStates) { + uint64_t modelState = stateIndex / memoryStateCount; + uint64_t memoryState = stateIndex % memoryStateCount; + storm::storage::SchedulerChoice choice = scheduler.getChoice(modelState, memoryState); + if (choice.isDefined()) { + ++numResChoices; + if (choice.isDeterministic()) { + uint64_t modelRow = model.getTransitionMatrix().getRowGroupIndices()[modelState] + choice.getDeterministicChoice(); + numResTransitions += model.getTransitionMatrix().getRow(modelRow).getNumberOfEntries(); + } else { + std::set successors; + for (auto const& choiceIndex : choice.getChoiceAsDistribution()) { + if (!storm::utility::isZero(choiceIndex.second)) { + uint64_t modelRow = model.getTransitionMatrix().getRowGroupIndices()[modelState] + choiceIndex.first; + for (auto const& entry : model.getTransitionMatrix().getRow(modelRow)) { + successors.insert(entry.getColumn()); + } + } + } + numResTransitions += successors.size(); + } + } else { + uint64_t modelRow = model.getTransitionMatrix().getRowGroupIndices()[modelState]; + uint64_t groupEnd = model.getTransitionMatrix().getRowGroupIndices()[modelState + 1]; + hasTrivialNondeterminism = hasTrivialNondeterminism && (groupEnd == modelRow + 1); + for (; groupEnd; ++modelRow) { + ++numResChoices; + numResTransitions += model.getTransitionMatrix().getRow(modelRow).getNumberOfEntries(); + } + } + } + + storm::storage::SparseMatrixBuilder builder(numResChoices, numResStates, numResTransitions, true, !hasTrivialNondeterminism, hasTrivialNondeterminism ? 0 : numResStates); + uint64_t currentRow = 0; + for (auto const& stateIndex : reachableStates) { + uint64_t modelState = stateIndex / memoryStateCount; + uint64_t memoryState = stateIndex % memoryStateCount; + if (!hasTrivialNondeterminism) { + builder.newRowGroup(currentRow); + } + storm::storage::SchedulerChoice choice = scheduler.getChoice(modelState, memoryState); + if (choice.isDefined()) { + if (choice.isDeterministic()) { + uint64_t modelRowIndex = model.getTransitionMatrix().getRowGroupIndices()[modelState] + choice.getDeterministicChoice(); + auto const& modelRow = model.getTransitionMatrix().getRow(modelRowIndex); + for (auto entryIt = modelRow.begin(); entryIt != modelRow.end(); ++entryIt) { + uint64_t transitionId = entryIt - model.getTransitionMatrix().begin(); + uint64_t const& successorMemoryState = memorySuccessors[transitionId * memoryStateCount + memoryState]; + builder.addNextValue(currentRow, getResultState(entryIt->getColumn(), successorMemoryState), entryIt->getValue()); + } + } else { + std::map transitions; + for (auto const& choiceIndex : choice.getChoiceAsDistribution()) { + if (!storm::utility::isZero(choiceIndex.second)) { + uint64_t modelRowIndex = model.getTransitionMatrix().getRowGroupIndices()[modelState] + choiceIndex.first; + auto const& modelRow = model.getTransitionMatrix().getRow(modelRowIndex); + for (auto entryIt = modelRow.begin(); entryIt != modelRow.end(); ++entryIt) { + uint64_t transitionId = entryIt - model.getTransitionMatrix().begin(); + uint64_t const& successorMemoryState = memorySuccessors[transitionId * memoryStateCount + memoryState]; + ValueType transitionValue = choiceIndex.second * entryIt->getValue(); + auto insertionRes = transitions.insert(std::make_pair(getResultState(entryIt->getColumn(), successorMemoryState), transitionValue)); + if (!insertionRes.second) { + insertionRes.first->second += transitionValue; + } + } + } + } + for (auto const& transition : transitions) { + builder.addNextValue(currentRow, transition.first, transition.second); + } + } + ++currentRow; + } else { + for (uint64_t modelRowIndex = model.getTransitionMatrix().getRowGroupIndices()[modelState]; modelRowIndex < model.getTransitionMatrix().getRowGroupIndices()[modelState + 1]; ++modelRowIndex) { + auto const& modelRow = model.getTransitionMatrix().getRow(modelRowIndex); + for (auto entryIt = modelRow.begin(); entryIt != modelRow.end(); ++entryIt) { + uint64_t transitionId = entryIt - model.getTransitionMatrix().begin(); + uint64_t const& successorMemoryState = memorySuccessors[transitionId * memoryStateCount + memoryState]; + builder.addNextValue(currentRow, getResultState(entryIt->getColumn(), successorMemoryState), entryIt->getValue()); + } + ++currentRow; + } + } + } + + return builder.build(); + } + + template + storm::models::sparse::StateLabeling SparseModelMemoryProduct::buildStateLabeling(storm::storage::SparseMatrix const& resultTransitionMatrix) const { + uint64_t modelStateCount = model.getNumberOfStates(); + uint64_t memoryStateCount = memory.getNumberOfStates(); - uint_fast64_t numResStates = resultTransitionMatrix.getRowGroupCount(); + uint64_t numResStates = resultTransitionMatrix.getRowGroupCount(); storm::models::sparse::StateLabeling resultLabeling(numResStates); for (std::string modelLabel : model.getStateLabeling().getLabels()) { if (modelLabel != "init") { storm::storage::BitVector resLabeledStates(numResStates, false); for (auto const& modelState : model.getStateLabeling().getStates(modelLabel)) { - for (uint_fast64_t memoryState = 0; memoryState < memoryStateCount; ++memoryState) { - uint_fast64_t const& resState = getResultState(modelState, memoryState); + for (uint64_t memoryState = 0; memoryState < memoryStateCount; ++memoryState) { + uint64_t const& resState = getResultState(modelState, memoryState); // Check if the state exists in the result (i.e. if it is reachable) if (resState < numResStates) { resLabeledStates.set(resState, true); @@ -204,8 +338,8 @@ namespace storm { STORM_LOG_THROW(!resultLabeling.containsLabel(memoryLabel), storm::exceptions::InvalidOperationException, "Failed to build the product of model and memory structure: State labelings are not disjoint as both structures contain the label " << memoryLabel << "."); storm::storage::BitVector resLabeledStates(numResStates, false); for (auto const& memoryState : memory.getStateLabeling().getStates(memoryLabel)) { - for (uint_fast64_t modelState = 0; modelState < modelStateCount; ++modelState) { - uint_fast64_t const& resState = getResultState(modelState, memoryState); + for (uint64_t modelState = 0; modelState < modelStateCount; ++modelState) { + uint64_t const& resState = getResultState(modelState, memoryState); // Check if the state exists in the result (i.e. if it is reachable) if (resState < numResStates) { resLabeledStates.set(resState, true); @@ -217,21 +351,24 @@ namespace storm { return resultLabeling; } - template - std::unordered_map> SparseModelMemoryProduct::buildRewardModels(storm::storage::SparseMatrix const& resultTransitionMatrix, std::vector const& memorySuccessors) const { - std::unordered_map> result; - uint_fast64_t memoryStateCount = memory.getNumberOfStates(); - uint_fast64_t numResStates = resultTransitionMatrix.getRowGroupCount(); + template + std::unordered_map SparseModelMemoryProduct::buildRewardModels(storm::storage::SparseMatrix const& resultTransitionMatrix, std::vector const& memorySuccessors, boost::optional> const& scheduler) const { + + typedef typename RewardModelType::ValueType RewardValueType; + + std::unordered_map result; + uint64_t memoryStateCount = memory.getNumberOfStates(); + uint64_t numResStates = resultTransitionMatrix.getRowGroupCount(); for (auto const& rewardModel : model.getRewardModels()) { - boost::optional> stateRewards; + boost::optional> stateRewards; if (rewardModel.second.hasStateRewards()) { - stateRewards = std::vector(numResStates, storm::utility::zero()); - uint_fast64_t modelState = 0; + stateRewards = std::vector(numResStates, storm::utility::zero()); + uint64_t modelState = 0; for (auto const& modelStateReward : rewardModel.second.getStateRewardVector()) { if (!storm::utility::isZero(modelStateReward)) { - for (uint_fast64_t memoryState = 0; memoryState < memoryStateCount; ++memoryState) { - uint_fast64_t const& resState = getResultState(modelState, memoryState); + for (uint64_t memoryState = 0; memoryState < memoryStateCount; ++memoryState) { + uint64_t const& resState = getResultState(modelState, memoryState); // Check if the state exists in the result (i.e. if it is reachable) if (resState < numResStates) { stateRewards.get()[resState] = modelStateReward; @@ -241,45 +378,78 @@ namespace storm { ++modelState; } } - boost::optional> stateActionRewards; + boost::optional> stateActionRewards; if (rewardModel.second.hasStateActionRewards()) { - stateActionRewards = std::vector(resultTransitionMatrix.getRowCount(), storm::utility::zero()); - uint_fast64_t modelState = 0; - uint_fast64_t modelRow = 0; + stateActionRewards = std::vector(resultTransitionMatrix.getRowCount(), storm::utility::zero()); + uint64_t modelState = 0; + uint64_t modelRow = 0; for (auto const& modelStateActionReward : rewardModel.second.getStateActionRewardVector()) { if (!storm::utility::isZero(modelStateActionReward)) { while (modelRow >= model.getTransitionMatrix().getRowGroupIndices()[modelState + 1]) { ++modelState; } - uint_fast64_t rowOffset = modelRow - model.getTransitionMatrix().getRowGroupIndices()[modelState]; - for (uint_fast64_t memoryState = 0; memoryState < memoryStateCount; ++memoryState) { - uint_fast64_t const& resState = getResultState(modelState, memoryState); + uint64_t rowOffset = modelRow - model.getTransitionMatrix().getRowGroupIndices()[modelState]; + for (uint64_t memoryState = 0; memoryState < memoryStateCount; ++memoryState) { + uint64_t const& resState = getResultState(modelState, memoryState); // Check if the state exists in the result (i.e. if it is reachable) if (resState < numResStates) { - stateActionRewards.get()[resultTransitionMatrix.getRowGroupIndices()[resState] + rowOffset] = modelStateActionReward; + if (scheduler && scheduler->getChoice(modelState, memoryState).isDefined()) { + ValueType factor = scheduler->getChoice(modelState, memoryState).getChoiceAsDistribution().getProbability(rowOffset); + stateActionRewards.get()[resultTransitionMatrix.getRowGroupIndices()[resState]] = factor * modelStateActionReward; + } else { + stateActionRewards.get()[resultTransitionMatrix.getRowGroupIndices()[resState] + rowOffset] = modelStateActionReward; + } } } } ++modelRow; } } - boost::optional> transitionRewards; + boost::optional> transitionRewards; if (rewardModel.second.hasTransitionRewards()) { - storm::storage::SparseMatrixBuilder builder(resultTransitionMatrix.getRowCount(), resultTransitionMatrix.getColumnCount()); - uint_fast64_t stateIndex = 0; + storm::storage::SparseMatrixBuilder builder(resultTransitionMatrix.getRowCount(), resultTransitionMatrix.getColumnCount()); + uint64_t stateIndex = 0; for (auto const& resState : toResultStateMapping) { if (resState < numResStates) { - uint_fast64_t modelState = stateIndex / memoryStateCount; - uint_fast64_t memoryState = stateIndex % memoryStateCount; - uint_fast64_t rowGroupSize = resultTransitionMatrix.getRowGroupSize(resState); - for (uint_fast64_t rowOffset = 0; rowOffset < rowGroupSize; ++rowOffset) { - uint_fast64_t resRowIndex = resultTransitionMatrix.getRowGroupIndices()[resState] + rowOffset; - uint_fast64_t modelRowIndex = model.getTransitionMatrix().getRowGroupIndices()[modelState] + rowOffset; - auto const& rewardMatrixRow = rewardModel.second.getTransitionRewardMatrix().getRow(modelRowIndex); - for (auto entryIt = rewardMatrixRow.begin(); entryIt != rewardMatrixRow.end(); ++entryIt) { - uint_fast64_t transitionId = entryIt - rewardModel.second.getTransitionRewardMatrix().begin(); - uint_fast64_t const& successorMemoryState = memorySuccessors[transitionId * memoryStateCount + memoryState]; - builder.addNextValue(resRowIndex, getResultState(entryIt->getColumn(), successorMemoryState), entryIt->getValue()); + uint64_t modelState = stateIndex / memoryStateCount; + uint64_t memoryState = stateIndex % memoryStateCount; + uint64_t rowGroupSize = resultTransitionMatrix.getRowGroupSize(resState); + if (scheduler && scheduler->getChoice(modelState, memoryState).isDefined()) { + std::map rewards; + for (uint64_t rowOffset = 0; rowOffset < rowGroupSize; ++rowOffset) { + uint64_t modelRowIndex = model.getTransitionMatrix().getRowGroupIndices()[modelState] + rowOffset; + auto transitionEntryIt = model.getTransitionMatrix().getRow(modelRowIndex).begin(); + for (auto const& rewardEntry : rewardModel.second.getTransitionRewardMatrix().getRow(modelRowIndex)) { + while (transitionEntryIt->getColumn() != rewardEntry.getColumn()) { + STORM_LOG_ASSERT(transitionEntryIt != model.getTransitionMatrix().getRow(modelRowIndex).end(), "The reward transition matrix is not a submatrix of the model transition matrix."); + ++transitionEntryIt; + } + uint64_t transitionId = transitionEntryIt - model.getTransitionMatrix().begin(); + uint64_t const& successorMemoryState = memorySuccessors[transitionId * memoryStateCount + memoryState]; + auto insertionRes = rewards.insert(std::make_pair(getResultState(rewardEntry.getColumn(), successorMemoryState), rewardEntry.getValue())); + if (!insertionRes.second) { + insertionRes.first->second += rewardEntry.getValue(); + } + } + } + uint64_t resRowIndex = resultTransitionMatrix.getRowGroupIndices()[resState]; + for (auto& reward : rewards) { + builder.addNextValue(resRowIndex, reward.first, reward.second); + } + } else { + for (uint64_t rowOffset = 0; rowOffset < rowGroupSize; ++rowOffset) { + uint64_t resRowIndex = resultTransitionMatrix.getRowGroupIndices()[resState] + rowOffset; + uint64_t modelRowIndex = model.getTransitionMatrix().getRowGroupIndices()[modelState] + rowOffset; + auto transitionEntryIt = model.getTransitionMatrix().getRow(modelRowIndex).begin(); + for (auto const& rewardEntry : rewardModel.second.getTransitionRewardMatrix().getRow(modelRowIndex)) { + while (transitionEntryIt->getColumn() != rewardEntry.getColumn()) { + STORM_LOG_ASSERT(transitionEntryIt != model.getTransitionMatrix().getRow(modelRowIndex).end(), "The reward transition matrix is not a submatrix of the model transition matrix."); + ++transitionEntryIt; + } + uint64_t transitionId = transitionEntryIt - model.getTransitionMatrix().begin(); + uint64_t const& successorMemoryState = memorySuccessors[transitionId * memoryStateCount + memoryState]; + builder.addNextValue(resRowIndex, getResultState(rewardEntry.getColumn(), successorMemoryState), rewardEntry.getValue()); + } } } } @@ -287,32 +457,32 @@ namespace storm { } transitionRewards = builder.build(); } - result.insert(std::make_pair(rewardModel.first, storm::models::sparse::StandardRewardModel(std::move(stateRewards), std::move(stateActionRewards), std::move(transitionRewards)))); + result.insert(std::make_pair(rewardModel.first, RewardModelType(std::move(stateRewards), std::move(stateActionRewards), std::move(transitionRewards)))); } return result; } - template - std::shared_ptr> SparseModelMemoryProduct::buildResult(storm::storage::SparseMatrix&& matrix, storm::models::sparse::StateLabeling&& labeling, std::unordered_map>&& rewardModels) const { - storm::storage::sparse::ModelComponents> components (std::move(matrix), std::move(labeling), std::move(rewardModels)); + template + std::shared_ptr> SparseModelMemoryProduct::buildResult(storm::storage::SparseMatrix&& matrix, storm::models::sparse::StateLabeling&& labeling, std::unordered_map&& rewardModels, boost::optional> const& scheduler) const { + storm::storage::sparse::ModelComponents components (std::move(matrix), std::move(labeling), std::move(rewardModels)); if (model.isOfType(storm::models::ModelType::Ctmc)) { components.rateTransitions = true; } else if (model.isOfType(storm::models::ModelType::MarkovAutomaton)) { // We also need to translate the exit rates and the Markovian states - uint_fast64_t numResStates = components.transitionMatrix.getRowGroupCount(); - uint_fast64_t memoryStateCount = memory.getNumberOfStates(); + uint64_t numResStates = components.transitionMatrix.getRowGroupCount(); + uint64_t memoryStateCount = memory.getNumberOfStates(); std::vector resultExitRates; resultExitRates.reserve(components.transitionMatrix.getRowGroupCount()); storm::storage::BitVector resultMarkovianStates(numResStates, false); - auto const& modelExitRates = dynamic_cast const&>(model).getExitRates(); - auto const& modelMarkovianStates = dynamic_cast const&>(model).getMarkovianStates(); + auto const& modelExitRates = dynamic_cast const&>(model).getExitRates(); + auto const& modelMarkovianStates = dynamic_cast const&>(model).getMarkovianStates(); - uint_fast64_t stateIndex = 0; + uint64_t stateIndex = 0; for (auto const& resState : toResultStateMapping) { if (resState < numResStates) { assert(resState == resultExitRates.size()); - uint_fast64_t modelState = stateIndex / memoryStateCount; + uint64_t modelState = stateIndex / memoryStateCount; resultExitRates.push_back(modelExitRates[modelState]); if (modelMarkovianStates.get(modelState)) { resultMarkovianStates.set(resState, true); @@ -324,12 +494,21 @@ namespace storm { components.exitRates = std::move(resultExitRates); } - return storm::utility::builder::buildModelFromComponents(model.getType(), std::move(components)); + storm::models::ModelType resultType = model.getType(); + if (scheduler && !scheduler->isPartialScheduler()) { + if (model.isOfType(storm::models::ModelType::Mdp)) { + resultType = storm::models::ModelType::Dtmc; + } + // Note that converting deterministic MAs to CTMCs via state elimination does not preserve all properties (e.g. step bounded) + } + + return storm::utility::builder::buildModelFromComponents(resultType, std::move(components)); } - template class SparseModelMemoryProduct; - template class SparseModelMemoryProduct; - template class SparseModelMemoryProduct; + template class SparseModelMemoryProduct; + template class SparseModelMemoryProduct>; + template class SparseModelMemoryProduct; + template class SparseModelMemoryProduct; } } diff --git a/src/storm/storage/memorystructure/SparseModelMemoryProduct.h b/src/storm/storage/memorystructure/SparseModelMemoryProduct.h index 0c60923e7..fb959daa5 100644 --- a/src/storm/storage/memorystructure/SparseModelMemoryProduct.h +++ b/src/storm/storage/memorystructure/SparseModelMemoryProduct.h @@ -8,61 +8,71 @@ #include "storm/storage/memorystructure/MemoryStructure.h" #include "storm/models/sparse/Model.h" #include "storm/models/sparse/StandardRewardModel.h" +#include "storm/storage/Scheduler.h" namespace storm { namespace storage { /*! * This class builds the product of the given sparse model and the given memory structure. * This is similar to the well-known product of a model with a deterministic rabin automaton. - * Note: we already do one memory-structure-step for the initial state, i.e., if s is an initial state of - * the given model and s satisfies memoryStructure.getTransitionMatrix[0][n], then (s,n) will be the corresponding - * initial state of the resulting model. + * The product contains only the reachable states of the product * * The states of the resulting sparse model will have the original state labels plus the labels of this * memory structure. * An exception is thrown if the state labelings are not disjoint. */ - template + template > class SparseModelMemoryProduct { public: - SparseModelMemoryProduct(storm::models::sparse::Model const& sparseModel, storm::storage::MemoryStructure const& memoryStructure); + SparseModelMemoryProduct(storm::models::sparse::Model const& sparseModel, storm::storage::MemoryStructure const& memoryStructure); - // Invokes the building of the product - std::shared_ptr> build(); + // Enforces that the given model and memory state as well as the successor(s) are considered reachable -- even if they are not reachable from an initial state. + void addReachableState(uint64_t const& modelState, uint64_t const& memoryState); + + // Enforces that every state is considered reachable. If this is set, the result has size #modelStates * #memoryStates + void setBuildFullProduct(); + + // Invokes the building of the product under the specified scheduler (if given). + std::shared_ptr> build(boost::optional> const& scheduler = boost::none); // Retrieves the state of the resulting model that represents the given memory and model state. - // An invalid index is returned, if the specifyied state does not exist (i.e., if it is not reachable). - uint_fast64_t const& getResultState(uint_fast64_t const& modelState, uint_fast64_t const& memoryState) const; + // Should only be called AFTER calling build(); + // An invalid index is returned, if the specifyied state does not exist (i.e., if it is not part of product). + uint64_t const& getResultState(uint64_t const& modelState, uint64_t const& memoryState) const; private: // Computes for each pair of memory state and model transition index the successor memory state // The resulting vector maps (modelTransition * memoryStateCount) + memoryState to the corresponding successor state of the memory structure - std::vector computeMemorySuccessors() const; + std::vector computeMemorySuccessors() const; // Computes the reachable states of the resulting model - storm::storage::BitVector computeReachableStates(std::vector const& memorySuccessors, storm::storage::BitVector const& initialStates) const; + void computeReachableStates(std::vector const& memorySuccessors, storm::storage::BitVector const& initialStates, boost::optional> const& scheduler); // Methods that build the model components // Matrix for deterministic models - storm::storage::SparseMatrix buildDeterministicTransitionMatrix(storm::storage::BitVector const& reachableStates, std::vector const& memorySuccessors) const; + storm::storage::SparseMatrix buildDeterministicTransitionMatrix(std::vector const& memorySuccessors) const; // Matrix for nondeterministic models - storm::storage::SparseMatrix buildNondeterministicTransitionMatrix(storm::storage::BitVector const& reachableStates, std::vector const& memorySuccessors) const; + storm::storage::SparseMatrix buildNondeterministicTransitionMatrix(std::vector const& memorySuccessors) const; + // Matrix for models that consider a scheduler + storm::storage::SparseMatrix buildTransitionMatrixForScheduler(std::vector const& memorySuccessors, storm::storage::Scheduler const& scheduler) const; // State labeling. Note: DOES NOT ADD A LABEL FOR THE INITIAL STATES storm::models::sparse::StateLabeling buildStateLabeling(storm::storage::SparseMatrix const& resultTransitionMatrix) const; // Reward models - std::unordered_map> buildRewardModels(storm::storage::SparseMatrix const& resultTransitionMatrix, std::vector const& memorySuccessors) const; + std::unordered_map buildRewardModels(storm::storage::SparseMatrix const& resultTransitionMatrix, std::vector const& memorySuccessors, boost::optional> const& scheduler) const; // Builds the resulting model - std::shared_ptr> buildResult(storm::storage::SparseMatrix&& matrix, storm::models::sparse::StateLabeling&& labeling, std::unordered_map>&& rewardModels) const; + std::shared_ptr> buildResult(storm::storage::SparseMatrix&& matrix, storm::models::sparse::StateLabeling&& labeling, std::unordered_map&& rewardModels, boost::optional> const& scheduler) const; // Maps (modelState * memoryStateCount) + memoryState to the state in the result that represents (memoryState,modelState) - std::vector toResultStateMapping; + std::vector toResultStateMapping; - - storm::models::sparse::Model const& model; + // Indicates which states are considered reachable. (s, m) is reachable if this BitVector is true at (s * memoryStateCount) + m + storm::storage::BitVector reachableStates; + + storm::models::sparse::Model const& model; storm::storage::MemoryStructure const& memory; };