#include "storm/modelchecker/multiobjective/rewardbounded/ProductModel.h" #include "storm/utility/macros.h" #include "storm/logic/Formulas.h" #include "storm/logic/CloneVisitor.h" #include "storm/storage/memorystructure/SparseModelMemoryProduct.h" #include "storm/storage/memorystructure/MemoryStructureBuilder.h" #include "storm/modelchecker/propositional/SparsePropositionalModelChecker.h" #include "storm/modelchecker/results/ExplicitQualitativeCheckResult.h" #include "storm/exceptions/UnexpectedException.h" #include "storm/exceptions/NotSupportedException.h" namespace storm { namespace modelchecker { namespace multiobjective { template ProductModel::ProductModel(storm::models::sparse::Mdp const& model, std::vector> const& objectives, std::vector> const& dimensions, std::vector const& objectiveDimensions, EpochManager const& epochManager, std::vector const& originalModelSteps) : dimensions(dimensions), objectiveDimensions(objectiveDimensions), epochManager(epochManager), memoryStateManager(dimensions.size()) { for (uint64_t dim = 0; dim < dimensions.size(); ++dim) { if (!dimensions[dim].memoryLabel) { memoryStateManager.setDimensionWithoutMemory(dim); } } storm::storage::MemoryStructure memory = computeMemoryStructure(model, objectives); assert(memoryStateManager.getMemoryStateCount() == memory.getNumberOfStates()); std::vector memoryStateMap = computeMemoryStateMap(memory); storm::storage::SparseModelMemoryProduct productBuilder(memory.product(model)); setReachableProductStates(productBuilder, originalModelSteps, memoryStateMap); product = productBuilder.build()->template as>(); uint64_t numModelStates = productBuilder.getOriginalModel().getNumberOfStates(); MemoryState upperMemStateBound = memoryStateManager.getUpperMemoryStateBound(); uint64_t numMemoryStates = memoryStateManager.getMemoryStateCount(); uint64_t numProductStates = getProduct().getNumberOfStates(); // Compute a mappings from product states to model/memory states and back modelMemoryToProductStateMap.resize(upperMemStateBound * numModelStates, std::numeric_limits::max()); productToModelStateMap.resize(numProductStates, std::numeric_limits::max()); productToMemoryStateMap.resize(numProductStates, std::numeric_limits::max()); for (uint64_t modelState = 0; modelState < numModelStates; ++modelState) { for (uint64_t memoryStateIndex = 0; memoryStateIndex < numMemoryStates; ++memoryStateIndex) { if (productBuilder.isStateReachable(modelState, memoryStateIndex)) { uint64_t productState = productBuilder.getResultState(modelState, memoryStateIndex); modelMemoryToProductStateMap[modelState * upperMemStateBound + memoryStateMap[memoryStateIndex]] = productState; productToModelStateMap[productState] = modelState; productToMemoryStateMap[productState] = memoryStateMap[memoryStateIndex]; } } } // Map choice indices of the product to the state where it origins choiceToStateMap.reserve(getProduct().getNumberOfChoices()); for (uint64_t productState = 0; productState < numProductStates; ++productState) { uint64_t groupSize = getProduct().getTransitionMatrix().getRowGroupSize(productState); for (uint64_t i = 0; i < groupSize; ++i) { choiceToStateMap.push_back(productState); } } // Compute the epoch steps for the product steps.resize(getProduct().getNumberOfChoices(), 0); for (uint64_t modelState = 0; modelState < numModelStates; ++modelState) { uint64_t numChoices = productBuilder.getOriginalModel().getTransitionMatrix().getRowGroupSize(modelState); uint64_t firstChoice = productBuilder.getOriginalModel().getTransitionMatrix().getRowGroupIndices()[modelState]; for (uint64_t choiceOffset = 0; choiceOffset < numChoices; ++choiceOffset) { Epoch const& step = originalModelSteps[firstChoice + choiceOffset]; if (step != 0) { for (MemoryState const& memoryState : memoryStateMap) { if (productStateExists(modelState, memoryState)) { uint64_t productState = getProductState(modelState, memoryState); uint64_t productChoice = getProduct().getTransitionMatrix().getRowGroupIndices()[productState] + choiceOffset; assert(productChoice < getProduct().getTransitionMatrix().getRowGroupIndices()[productState + 1]); steps[productChoice] = step; } } } } } // getProduct().writeDotToStream(std::cout); computeReachableStatesInEpochClasses(); } template storm::storage::MemoryStructure ProductModel::computeMemoryStructure(storm::models::sparse::Mdp const& model, std::vector> const& objectives) const { storm::modelchecker::SparsePropositionalModelChecker> mc(model); // Create a memory structure that remembers whether (sub)objectives are satisfied storm::storage::MemoryStructure memory = storm::storage::MemoryStructureBuilder::buildTrivialMemoryStructure(model); for (uint64_t objIndex = 0; objIndex < objectives.size(); ++objIndex) { if (!objectives[objIndex].formula->isProbabilityOperatorFormula()) { continue; } std::vector dimensionIndexMap; for (auto const& globalDimensionIndex : objectiveDimensions[objIndex]) { dimensionIndexMap.push_back(globalDimensionIndex); } bool objectiveContainsLowerBound = false; for (auto const& globalDimensionIndex : objectiveDimensions[objIndex]) { if (!dimensions[globalDimensionIndex].isUpperBounded) { objectiveContainsLowerBound = true; break; } } // collect the memory states for this objective std::vector objMemStates; storm::storage::BitVector m(dimensionIndexMap.size(), false); for (; !m.full(); m.increment()) { objMemStates.push_back(~m); } objMemStates.push_back(~m); assert(objMemStates.size() == 1ull << dimensionIndexMap.size()); // build objective memory auto objMemoryBuilder = storm::storage::MemoryStructureBuilder(objMemStates.size(), model); // Get the set of states that for all subobjectives satisfy either the left or the right subformula storm::storage::BitVector constraintStates(model.getNumberOfStates(), true); for (auto const& dim : objectiveDimensions[objIndex]) { auto const& dimension = dimensions[dim]; STORM_LOG_ASSERT(dimension.formula->isBoundedUntilFormula(), "Unexpected Formula type"); constraintStates &= (mc.check(dimension.formula->asBoundedUntilFormula().getLeftSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector() | mc.check(dimension.formula->asBoundedUntilFormula().getRightSubformula())->asExplicitQualitativeCheckResult().getTruthValuesVector()); } // Build the transitions between the memory states for (uint64_t memState = 0; memState < objMemStates.size(); ++memState) { auto const& memStateBV = objMemStates[memState]; for (uint64_t memStatePrime = 0; memStatePrime < objMemStates.size(); ++memStatePrime) { auto const& memStatePrimeBV = objMemStates[memStatePrime]; if (memStatePrimeBV.isSubsetOf(memStateBV)) { std::shared_ptr transitionFormula = storm::logic::Formula::getTrueFormula(); for (auto const& subObjIndex : memStateBV) { std::shared_ptr subObjFormula = dimensions[dimensionIndexMap[subObjIndex]].formula->asBoundedUntilFormula().getRightSubformula().asSharedPointer(); if (memStatePrimeBV.get(subObjIndex)) { subObjFormula = std::make_shared(storm::logic::UnaryBooleanStateFormula::OperatorType::Not, subObjFormula); } transitionFormula = std::make_shared(storm::logic::BinaryBooleanStateFormula::OperatorType::And, transitionFormula, subObjFormula); } storm::storage::BitVector transitionStates = mc.check(*transitionFormula)->asExplicitQualitativeCheckResult().getTruthValuesVector(); if (memStatePrimeBV.empty()) { transitionStates |= ~constraintStates; } else { transitionStates &= constraintStates; } objMemoryBuilder.setTransition(memState, memStatePrime, transitionStates); // Set the initial states if (memStateBV.full()) { storm::storage::BitVector initialTransitionStates = model.getInitialStates() & transitionStates; // At this point we can check whether there is an initial state that already satisfies all subObjectives. // Such a situation is not supported as we can not reduce this (easily) to an expected reward computation. STORM_LOG_THROW(!memStatePrimeBV.empty() || initialTransitionStates.empty() || objectiveContainsLowerBound || initialTransitionStates.isDisjointFrom(constraintStates), storm::exceptions::NotSupportedException, "The objective " << *objectives[objIndex].formula << " is already satisfied in an initial state. This special case is not supported."); for (auto const& initState : initialTransitionStates) { objMemoryBuilder.setInitialMemoryState(initState, memStatePrime); } } } } } // Build the memory labels for (uint64_t memState = 0; memState < objMemStates.size(); ++memState) { auto const& memStateBV = objMemStates[memState]; for (auto const& subObjIndex : memStateBV) { objMemoryBuilder.setLabel(memState, dimensions[dimensionIndexMap[subObjIndex]].memoryLabel.get()); } } auto objMemory = objMemoryBuilder.build(); memory = memory.product(objMemory); } return memory; } template std::vector::MemoryState> ProductModel::computeMemoryStateMap(storm::storage::MemoryStructure const& memory) const { // Compute a mapping between the different representations of memory states std::vector result; result.reserve(memory.getNumberOfStates()); for (uint64_t memStateIndex = 0; memStateIndex < memory.getNumberOfStates(); ++memStateIndex) { MemoryState memState = memoryStateManager.getInitialMemoryState(); std::set stateLabels = memory.getStateLabeling().getLabelsOfState(memStateIndex); for (uint64_t dim = 0; dim < epochManager.getDimensionCount(); ++dim) { if (dimensions[dim].memoryLabel) { if (stateLabels.find(dimensions[dim].memoryLabel.get()) != stateLabels.end()) { memoryStateManager.setRelevantDimension(memState, dim, true); } else { memoryStateManager.setRelevantDimension(memState, dim, false); } } } result.push_back(std::move(memState)); } return result; } template void ProductModel::setReachableProductStates(storm::storage::SparseModelMemoryProduct& productBuilder, std::vector const& originalModelSteps, std::vector const& memoryStateMap) const { std::vector inverseMemoryStateMap(memoryStateManager.getUpperMemoryStateBound(), std::numeric_limits::max()); for (uint64_t memStateIndex = 0; memStateIndex < memoryStateMap.size(); ++memStateIndex) { inverseMemoryStateMap[memoryStateMap[memStateIndex]] = memStateIndex; } auto const& memory = productBuilder.getMemory(); auto const& model = productBuilder.getOriginalModel(); auto const& modelTransitions = model.getTransitionMatrix(); std::vector reachableProductStates(memoryStateManager.getUpperMemoryStateBound()); for (auto const& memState : memoryStateMap) { reachableProductStates[memState] = storm::storage::BitVector(model.getNumberOfStates(), false); } // Initialize the reachable states with the initial states EpochClass initEpochClass = epochManager.getEpochClass(epochManager.getZeroEpoch()); auto memStateIt = memory.getInitialMemoryStates().begin(); for (auto const& initState : model.getInitialStates()) { uint64_t transformedMemoryState = transformMemoryState(memoryStateMap[*memStateIt], initEpochClass, memoryStateManager.getInitialMemoryState()); reachableProductStates[transformedMemoryState].set(initState, true); ++memStateIt; } assert(memStateIt == memory.getInitialMemoryStates().end()); // Find the reachable epoch classes std::set possibleSteps(originalModelSteps.begin(), originalModelSteps.end()); std::set> reachableEpochClasses(std::bind(&EpochManager::epochClassOrder, &epochManager, std::placeholders::_1, std::placeholders::_2)); collectReachableEpochClasses(reachableEpochClasses, possibleSteps); // Iterate over all epoch classes starting from the initial one (i.e., no bottom dimension). for (auto epochClassIt = reachableEpochClasses.rbegin(); epochClassIt != reachableEpochClasses.rend(); ++epochClassIt) { auto const& epochClass = *epochClassIt; // Find the remaining set of reachable states via DFS. std::vector> dfsStack; for (MemoryState const& memState : memoryStateMap) { for (auto const& modelState : reachableProductStates[memState]) { dfsStack.emplace_back(modelState, memState); } } while (!dfsStack.empty()) { uint64_t currentModelState = dfsStack.back().first; MemoryState currentMemoryState = dfsStack.back().second; uint64_t currentMemoryStateIndex = inverseMemoryStateMap[currentMemoryState]; dfsStack.pop_back(); for (uint64_t choice = modelTransitions.getRowGroupIndices()[currentModelState]; choice != modelTransitions.getRowGroupIndices()[currentModelState + 1]; ++choice) { for (auto transitionIt = modelTransitions.getRow(choice).begin(); transitionIt < modelTransitions.getRow(choice).end(); ++transitionIt) { MemoryState successorMemoryState = memoryStateMap[memory.getSuccessorMemoryState(currentMemoryStateIndex, transitionIt - modelTransitions.begin())]; successorMemoryState = transformMemoryState(successorMemoryState, epochClass, currentMemoryState); if (!reachableProductStates[successorMemoryState].get(transitionIt->getColumn())) { reachableProductStates[successorMemoryState].set(transitionIt->getColumn(), true); dfsStack.emplace_back(transitionIt->getColumn(), successorMemoryState); } } } } } for (uint64_t memStateIndex = 0; memStateIndex < memoryStateManager.getMemoryStateCount(); ++memStateIndex) { for (auto const& modelState : reachableProductStates[memoryStateMap[memStateIndex]]) { productBuilder.addReachableState(modelState, memStateIndex); } } } template storm::models::sparse::Mdp const& ProductModel::getProduct() const { return *product; } template std::vector::Epoch> const& ProductModel::getSteps() const { return steps; } template bool ProductModel::productStateExists(uint64_t const& modelState, MemoryState const& memoryState) const { return modelMemoryToProductStateMap[modelState * memoryStateManager.getUpperMemoryStateBound() + memoryState] < getProduct().getNumberOfStates(); } template uint64_t ProductModel::getProductState(uint64_t const& modelState, MemoryState const& memoryState) const { STORM_LOG_ASSERT(productStateExists(modelState, memoryState), "Tried to obtain a state in the model-memory-product that does not exist"); return modelMemoryToProductStateMap[modelState * memoryStateManager.getUpperMemoryStateBound() + memoryState]; } template uint64_t ProductModel::getInitialProductState(uint64_t const& initialModelState, storm::storage::BitVector const& initialModelStates) const { auto productInitStateIt = getProduct().getInitialStates().begin(); productInitStateIt += initialModelStates.getNumberOfSetBitsBeforeIndex(initialModelState); STORM_LOG_ASSERT(getModelState(*productInitStateIt) == initialModelState, "Could not find the corresponding initial state in the product model."); return transformProductState(*productInitStateIt, epochManager.getEpochClass(epochManager.getZeroEpoch()), memoryStateManager.getInitialMemoryState()); } template uint64_t ProductModel::getModelState(uint64_t const& productState) const { return productToModelStateMap[productState]; } template typename ProductModel::MemoryState ProductModel::getMemoryState(uint64_t const& productState) const { return productToMemoryStateMap[productState]; } template MemoryStateManager const& ProductModel::getMemoryStateManager() const { return memoryStateManager; } template uint64_t ProductModel::getProductStateFromChoice(uint64_t const& productChoice) const { return choiceToStateMap[productChoice]; } template std::vector> ProductModel::computeObjectiveRewards(EpochClass const& epochClass, std::vector> const& objectives) const { std::vector> objectiveRewards; objectiveRewards.reserve(objectives.size()); for (uint64_t objIndex = 0; objIndex < objectives.size(); ++objIndex) { auto const& formula = *objectives[objIndex].formula; if (formula.isProbabilityOperatorFormula()) { storm::modelchecker::SparsePropositionalModelChecker> mc(getProduct()); std::vector dimensionIndexMap; for (auto const& globalDimensionIndex : objectiveDimensions[objIndex]) { dimensionIndexMap.push_back(globalDimensionIndex); } std::shared_ptr sinkStatesFormula; for (auto const& dim : objectiveDimensions[objIndex]) { auto memLabelFormula = std::make_shared(dimensions[dim].memoryLabel.get()); if (sinkStatesFormula) { sinkStatesFormula = std::make_shared(storm::logic::BinaryBooleanStateFormula::OperatorType::Or, sinkStatesFormula, memLabelFormula); } else { sinkStatesFormula = memLabelFormula; } } sinkStatesFormula = std::make_shared(storm::logic::UnaryBooleanStateFormula::OperatorType::Not, sinkStatesFormula); std::vector objRew(getProduct().getTransitionMatrix().getRowCount(), storm::utility::zero()); storm::storage::BitVector relevantObjectives(objectiveDimensions[objIndex].getNumberOfSetBits()); while (!relevantObjectives.full()) { relevantObjectives.increment(); // find out whether objective reward should be earned within this epoch class bool collectRewardInEpoch = true; for (auto const& subObjIndex : relevantObjectives) { if (dimensions[dimensionIndexMap[subObjIndex]].isUpperBounded && epochManager.isBottomDimensionEpochClass(epochClass, dimensionIndexMap[subObjIndex])) { collectRewardInEpoch = false; break; } } if (collectRewardInEpoch) { std::shared_ptr relevantStatesFormula; std::shared_ptr goalStatesFormula = storm::logic::CloneVisitor().clone(*sinkStatesFormula); for (uint64_t subObjIndex = 0; subObjIndex < dimensionIndexMap.size(); ++subObjIndex) { std::shared_ptr memLabelFormula = std::make_shared(dimensions[dimensionIndexMap[subObjIndex]].memoryLabel.get()); if (relevantObjectives.get(subObjIndex)) { auto rightSubFormula = dimensions[dimensionIndexMap[subObjIndex]].formula->asBoundedUntilFormula().getRightSubformula().asSharedPointer(); goalStatesFormula = std::make_shared(storm::logic::BinaryBooleanStateFormula::OperatorType::And, goalStatesFormula, rightSubFormula); } else { memLabelFormula = std::make_shared(storm::logic::UnaryBooleanStateFormula::OperatorType::Not, memLabelFormula); } if (relevantStatesFormula) { relevantStatesFormula = std::make_shared(storm::logic::BinaryBooleanStateFormula::OperatorType::And, relevantStatesFormula, memLabelFormula); } else { relevantStatesFormula = memLabelFormula; } } storm::storage::BitVector relevantStates = mc.check(*relevantStatesFormula)->asExplicitQualitativeCheckResult().getTruthValuesVector(); storm::storage::BitVector relevantChoices = getProduct().getTransitionMatrix().getRowFilter(relevantStates); storm::storage::BitVector goalStates = mc.check(*goalStatesFormula)->asExplicitQualitativeCheckResult().getTruthValuesVector(); for (auto const& choice : relevantChoices) { objRew[choice] += getProduct().getTransitionMatrix().getConstrainedRowSum(choice, goalStates); } } } objectiveRewards.push_back(std::move(objRew)); } else if (formula.isRewardOperatorFormula()) { auto const& rewModel = getProduct().getRewardModel(formula.asRewardOperatorFormula().getRewardModelName()); STORM_LOG_THROW(!rewModel.hasTransitionRewards(), storm::exceptions::NotSupportedException, "Reward model has transition rewards which is not expected."); bool rewardCollectedInEpoch = true; if (formula.getSubformula().isCumulativeRewardFormula()) { for (auto const& dim : objectiveDimensions[objIndex]) { if (epochManager.isBottomDimensionEpochClass(epochClass, dim)) { rewardCollectedInEpoch = false; break; } } } else { STORM_LOG_THROW(formula.getSubformula().isTotalRewardFormula(), storm::exceptions::UnexpectedException, "Unexpected type of formula " << formula); } if (rewardCollectedInEpoch) { objectiveRewards.push_back(rewModel.getTotalRewardVector(getProduct().getTransitionMatrix())); } else { objectiveRewards.emplace_back(getProduct().getTransitionMatrix().getRowCount(), storm::utility::zero()); } } else { STORM_LOG_THROW(false, storm::exceptions::UnexpectedException, "Unexpected type of formula " << formula); } } return objectiveRewards; } template storm::storage::BitVector const& ProductModel::getInStates(EpochClass const& epochClass) const { STORM_LOG_ASSERT(inStates.find(epochClass) != inStates.end(), "Could not find InStates for the given epoch class"); return inStates.find(epochClass)->second; } template void ProductModel::computeReachableStatesInEpochClasses() { std::set possibleSteps(steps.begin(), steps.end()); std::set> reachableEpochClasses(std::bind(&EpochManager::epochClassOrder, &epochManager, std::placeholders::_1, std::placeholders::_2)); collectReachableEpochClasses(reachableEpochClasses, possibleSteps); for (auto epochClassIt = reachableEpochClasses.rbegin(); epochClassIt != reachableEpochClasses.rend(); ++epochClassIt) { std::vector predecessors; for (auto predecessorIt = reachableEpochClasses.rbegin(); predecessorIt != epochClassIt; ++predecessorIt) { if (epochManager.isPredecessorEpochClass(*predecessorIt, *epochClassIt)) { predecessors.push_back(*predecessorIt); } } computeReachableStates(*epochClassIt, predecessors); } } template void ProductModel::collectReachableEpochClasses(std::set>& reachableEpochClasses, std::set const& possibleSteps) const { Epoch startEpoch = epochManager.getZeroEpoch(); for (uint64_t dim = 0; dim < epochManager.getDimensionCount(); ++dim) { STORM_LOG_ASSERT(dimensions[dim].maxValue, "No max-value for dimension " << dim << " was given."); epochManager.setDimensionOfEpoch(startEpoch, dim, dimensions[dim].maxValue.get()); } std::set seenEpochs({startEpoch}); std::vector dfsStack({startEpoch}); reachableEpochClasses.insert(epochManager.getEpochClass(startEpoch)); // Perform a DFS to find all the reachable epochs while (!dfsStack.empty()) { Epoch currentEpoch = dfsStack.back(); dfsStack.pop_back(); for (auto const& step : possibleSteps) { Epoch successorEpoch = epochManager.getSuccessorEpoch(currentEpoch, step); if (seenEpochs.insert(successorEpoch).second) { reachableEpochClasses.insert(epochManager.getEpochClass(successorEpoch)); dfsStack.push_back(std::move(successorEpoch)); } } } } template void ProductModel::computeReachableStates(EpochClass const& epochClass, std::vector const& predecessors) { storm::storage::BitVector bottomDimensions(epochManager.getDimensionCount(), false); for (uint64_t dim = 0; dim < epochManager.getDimensionCount(); ++dim) { if (epochManager.isBottomDimensionEpochClass(epochClass, dim)) { bottomDimensions.set(dim, true); } } storm::storage::BitVector nonBottomDimensions = ~bottomDimensions; storm::storage::BitVector ecInStates(getProduct().getNumberOfStates(), false); if (!epochManager.hasBottomDimensionEpochClass(epochClass)) { for (auto const& initState : getProduct().getInitialStates()) { uint64_t transformedInitState = transformProductState(initState, epochClass, memoryStateManager.getInitialMemoryState()); ecInStates.set(transformedInitState, true); } } for (auto const& predecessor : predecessors) { storm::storage::BitVector positiveStepDimensions(epochManager.getDimensionCount(), false); for (uint64_t dim = 0; dim < epochManager.getDimensionCount(); ++dim) { if (!epochManager.isBottomDimensionEpochClass(predecessor, dim) && bottomDimensions.get(dim)) { positiveStepDimensions.set(dim, true); } } STORM_LOG_ASSERT(reachableStates.find(predecessor) != reachableStates.end(), "Could not find reachable states of predecessor epoch class."); storm::storage::BitVector predecessorStates = reachableStates.find(predecessor)->second; for (auto const& predecessorState : predecessorStates) { uint64_t predecessorMemoryState = getMemoryState(predecessorState); for (uint64_t choice = getProduct().getTransitionMatrix().getRowGroupIndices()[predecessorState]; choice < getProduct().getTransitionMatrix().getRowGroupIndices()[predecessorState + 1]; ++choice) { bool choiceLeadsToThisClass = false; Epoch const& choiceStep = getSteps()[choice]; for (auto const& dim : positiveStepDimensions) { if (epochManager.getDimensionOfEpoch(choiceStep, dim) > 0) { choiceLeadsToThisClass = true; } } if (choiceLeadsToThisClass) { for (auto const& transition : getProduct().getTransitionMatrix().getRow(choice)) { uint64_t successorState = transformProductState(transition.getColumn(), epochClass, predecessorMemoryState); ecInStates.set(successorState, true); } } } } } // Find all states reachable from an InState via DFS. storm::storage::BitVector ecReachableStates = ecInStates; std::vector dfsStack(ecReachableStates.begin(), ecReachableStates.end()); while (!dfsStack.empty()) { uint64_t currentState = dfsStack.back(); uint64_t currentMemoryState = getMemoryState(currentState); dfsStack.pop_back(); for (uint64_t choice = getProduct().getTransitionMatrix().getRowGroupIndices()[currentState]; choice != getProduct().getTransitionMatrix().getRowGroupIndices()[currentState + 1]; ++choice) { bool choiceLeadsOutsideOfEpoch = false; Epoch const& choiceStep = getSteps()[choice]; for (auto const& dim : nonBottomDimensions) { if (epochManager.getDimensionOfEpoch(choiceStep, dim) > 0) { choiceLeadsOutsideOfEpoch = true; break; } } for (auto const& transition : getProduct().getTransitionMatrix().getRow(choice)) { uint64_t successorState = transformProductState(transition.getColumn(), epochClass, currentMemoryState); if (choiceLeadsOutsideOfEpoch) { ecInStates.set(successorState, true); } if (!ecReachableStates.get(successorState)) { ecReachableStates.set(successorState, true); dfsStack.push_back(successorState); } } } } reachableStates[epochClass] = std::move(ecReachableStates); inStates[epochClass] = std::move(ecInStates); } template typename ProductModel::MemoryState ProductModel::transformMemoryState(MemoryState const& memoryState, EpochClass const& epochClass, MemoryState const& predecessorMemoryState) const { MemoryState memoryStatePrime = memoryState; for (auto const& objDimensions : objectiveDimensions) { for (auto const& dim : objDimensions) { auto const& dimension = dimensions[dim]; if (dimension.memoryLabel) { bool dimUpperBounded = dimension.isUpperBounded; bool dimBottom = epochManager.isBottomDimensionEpochClass(epochClass, dim); if (dimUpperBounded && dimBottom && memoryStateManager.isRelevantDimension(predecessorMemoryState, dim)) { STORM_LOG_ASSERT(objDimensions == dimension.dependentDimensions, "Unexpected set of dependent dimensions"); memoryStateManager.setRelevantDimensions(memoryStatePrime, objDimensions, false); break; } else if (!dimUpperBounded && !dimBottom && memoryStateManager.isRelevantDimension(predecessorMemoryState, dim)) { memoryStateManager.setRelevantDimensions(memoryStatePrime, dimension.dependentDimensions, true); } } } } // std::cout << "Transformed memory state " << memoryStateManager.toString(memoryState) << " at epoch class " << epochClass << " with predecessor " << memoryStateManager.toString(predecessorMemoryState) << " to " << memoryStateManager.toString(memoryStatePrime) << std::endl; return memoryStatePrime; } template uint64_t ProductModel::transformProductState(uint64_t const& productState, EpochClass const& epochClass, MemoryState const& predecessorMemoryState) const { return getProductState(getModelState(productState), transformMemoryState(getMemoryState(productState), epochClass, predecessorMemoryState)); } template class ProductModel; template class ProductModel; } } }