#ifndef STORM_TRANSFORMER_STATEDUPLICATOR_H #define STORM_TRANSFORMER_STATEDUPLICATOR_H #include #include #include "src/models/sparse/StandardRewardModel.h" #include "src/utility/constants.h" #include "src/utility/graph.h" #include "src/utility/macros.h" #include "src/utility/vector.h" #include "src/models/sparse/Dtmc.h" #include "src/models/sparse/Mdp.h" #include "src/models/sparse/Ctmc.h" #include "src/models/sparse/MarkovAutomaton.h" namespace storm { namespace transformer { /* * Duplicates the state space of the given model and redirects the incoming transitions of gateStates of the first copy to the gateStates of the second copy. * Only states reachable from the initial states are kept. */ template class StateDuplicator { public: struct StateDuplicatorReturnType { std::shared_ptr model; // The resulting model storm::storage::BitVector firstCopy; // The states of the resulting model that correspond to the first copy storm::storage::BitVector secondCopy; // The states of the resulting model that correspond to the second copy storm::storage::BitVector gateStates; // The gate states of the resulting model std::vector newToOldStateIndexMapping; // Gives for each state in the resulting model the corresponding state in the original model std::vector firstCopyOldToNewStateIndexMapping; //Maps old indices of states in the first copy to their new indices std::vector secondCopyOldToNewStateIndexMapping; //Maps old indices of states in the second copy to their new indices storm::storage::BitVector duplicatedStates; // The states in the original model that have been duplicated storm::storage::BitVector reachableStates; // The states in the original model that are reachable from the initial state }; /* * Duplicates the state space of the given model and redirects the incoming transitions of gateStates of the first copy to the gateStates of the second copy. * * Note that only reachable states are kept. * Gate states will always belong to the second copy. * Rewards and labels are duplicated accordingly. * However, the non-gate-states in the second copy will not get the label for initial states. * * @param originalModel The model to be duplicated * @param gateStates The states for which the incoming transitions are redirected */ static StateDuplicatorReturnType transform(SparseModelType const& originalModel, storm::storage::BitVector const& gateStates) { STORM_LOG_DEBUG("Invoked state duplicator on model with " << originalModel.getNumberOfStates() << " states."); StateDuplicatorReturnType result; // Collect some data for the result initializeTransformation(originalModel, gateStates, result); // Transform the ingedients of the model storm::storage::SparseMatrix matrix = transformMatrix(originalModel.getTransitionMatrix(), result, gateStates); storm::models::sparse::StateLabeling labeling(matrix.getRowGroupCount()); for(auto const& label : originalModel.getStateLabeling().getLabels()){ storm::storage::BitVector newBitVectorForLabel = transformStateBitVector(originalModel.getStateLabeling().getStates(label), result); if(label=="init"){ newBitVectorForLabel &= (result.firstCopy | result.gateStates); } labeling.addLabel(label, std::move(newBitVectorForLabel)); } std::unordered_map rewardModels; for(auto const& rewardModel : originalModel.getRewardModels()){ rewardModels.insert(std::make_pair(rewardModel.first, transformRewardModel(rewardModel.second, originalModel.getTransitionMatrix().getRowGroupIndices(), result, gateStates))); } boost::optional> choiceLabeling; if(originalModel.hasChoiceLabeling()){ choiceLabeling = transformActionValueVector(originalModel.getChoiceLabeling(), originalModel.getTransitionMatrix().getRowGroupIndices(), result); } result.model = std::make_shared(createTransformedModel(originalModel, result, matrix, labeling, rewardModels, choiceLabeling)); STORM_LOG_DEBUG("State duplicator is done. Resulting model has " << result.model->getNumberOfStates() << " states, where " << result.firstCopy.getNumberOfSetBits() << " are in the first copy."); return result; } private: static void initializeTransformation(SparseModelType const& originalModel, storm::storage::BitVector const& gateStates, StateDuplicatorReturnType& result) { storm::storage::BitVector noStates(originalModel.getNumberOfStates(), false); // Get the states that are reachable without visiting a gateState storm::storage::BitVector statesForFirstCopy = storm::utility::graph::getReachableStates(originalModel.getTransitionMatrix(), originalModel.getInitialStates(), ~gateStates, noStates); // Get the states reachable from gateStates storm::storage::BitVector statesForSecondCopy = storm::utility::graph::getReachableStates(originalModel.getTransitionMatrix(), gateStates, ~noStates, noStates); result.duplicatedStates = statesForFirstCopy & statesForSecondCopy; result.reachableStates = statesForFirstCopy | statesForSecondCopy; uint_fast64_t numStates = statesForFirstCopy.getNumberOfSetBits() + statesForSecondCopy.getNumberOfSetBits(); result.firstCopy = statesForFirstCopy % result.reachableStates; // only consider reachable states result.firstCopy.resize(numStates, false); // the new states do NOT belong to the first copy result.secondCopy = (statesForSecondCopy & (~statesForFirstCopy)) % result.reachableStates; // only consider reachable states result.secondCopy.resize(numStates, true); // the new states DO belong to the second copy STORM_LOG_ASSERT((result.firstCopy^result.secondCopy).full(), "firstCopy and secondCopy do not partition the state space."); // Get the state mappings. // We initialize them with illegal values to assert that we don't get a valid // state when given e.g. an unreachable state or a state from the other copy. result.newToOldStateIndexMapping = std::vector(numStates, std::numeric_limits::max()); result.firstCopyOldToNewStateIndexMapping = std::vector(originalModel.getNumberOfStates(), std::numeric_limits::max()); result.secondCopyOldToNewStateIndexMapping = std::vector(originalModel.getNumberOfStates(), std::numeric_limits::max()); uint_fast64_t newState = 0; for(auto const& oldState : result.reachableStates){ result.newToOldStateIndexMapping[newState] = oldState; if(statesForFirstCopy.get(oldState)){ result.firstCopyOldToNewStateIndexMapping[oldState] = newState; } else { result.secondCopyOldToNewStateIndexMapping[oldState] = newState; } ++newState; } // The remaining states are duplicates. All these states belong to the second copy. for(auto const& oldState : result.duplicatedStates) { result.newToOldStateIndexMapping[newState] = oldState; result.secondCopyOldToNewStateIndexMapping[oldState] = newState; ++newState; } STORM_LOG_ASSERT(newState == numStates, "Unexpected state Indices"); result.gateStates = transformStateBitVector(gateStates, result); } template static typename std::enable_if>::value, RewardModelType>::type transformRewardModel(RewardModelType const& originalRewardModel, std::vector const& originalRowGroupIndices, StateDuplicatorReturnType const& result, storm::storage::BitVector const& gateStates) { boost::optional> stateRewardVector; boost::optional> stateActionRewardVector; boost::optional> transitionRewardMatrix; if(originalRewardModel.hasStateRewards()){ stateRewardVector = transformStateValueVector(originalRewardModel.getStateRewardVector(), result); } if(originalRewardModel.hasStateActionRewards()){ stateActionRewardVector = transformActionValueVector(originalRewardModel.getStateActionRewardVector(), originalRowGroupIndices, result); } if(originalRewardModel.hasTransitionRewards()){ transitionRewardMatrix = transformMatrix(originalRewardModel.getTransitionRewardMatrix(), result, gateStates); } return RewardModelType(std::move(stateRewardVector), std::move(stateActionRewardVector), std::move(transitionRewardMatrix)); } template static storm::storage::SparseMatrix transformMatrix(storm::storage::SparseMatrix const& originalMatrix, StateDuplicatorReturnType const& result, storm::storage::BitVector const& gateStates) { // Build the builder uint_fast64_t numStates = result.newToOldStateIndexMapping.size(); uint_fast64_t numRows = 0; uint_fast64_t numEntries = 0; for(auto const& oldState : result.newToOldStateIndexMapping){ numRows += originalMatrix.getRowGroupSize(oldState); numEntries += originalMatrix.getRowGroupEntryCount(oldState); } storm::storage::SparseMatrixBuilder builder(numRows, numStates, numEntries, true, !originalMatrix.hasTrivialRowGrouping(), originalMatrix.hasTrivialRowGrouping() ? 0 : numStates); // Fill in the data uint_fast64_t newRow = 0; for(uint_fast64_t newState = 0; newState < numStates; ++newState){ if(!originalMatrix.hasTrivialRowGrouping()){ builder.newRowGroup(newRow); } uint_fast64_t oldState = result.newToOldStateIndexMapping[newState]; for (uint_fast64_t oldRow = originalMatrix.getRowGroupIndices()[oldState]; oldRow < originalMatrix.getRowGroupIndices()[oldState+1]; ++oldRow){ for(auto const& entry : originalMatrix.getRow(oldRow)){ if(result.firstCopy.get(newState) && !gateStates.get(entry.getColumn())){ builder.addNextValue(newRow, result.firstCopyOldToNewStateIndexMapping[entry.getColumn()], entry.getValue()); } else if (!result.duplicatedStates.get(entry.getColumn())){ builder.addNextValue(newRow, result.secondCopyOldToNewStateIndexMapping[entry.getColumn()], entry.getValue()); } } //To add values in the right order, transitions to duplicated states have to be added in a second run. for(auto const& entry : originalMatrix.getRow(oldRow)){ if (result.secondCopy.get(newState) && result.duplicatedStates.get(entry.getColumn())){ builder.addNextValue(newRow, result.secondCopyOldToNewStateIndexMapping[entry.getColumn()], entry.getValue()); } } ++newRow; } } return builder.build(); } template static std::vector transformActionValueVector(std::vectorconst& originalVector, std::vector const& originalRowGroupIndices, StateDuplicatorReturnType const& result) { uint_fast64_t numActions = 0; for(auto const& oldState : result.newToOldStateIndexMapping){ numActions += originalRowGroupIndices[oldState+1] - originalRowGroupIndices[oldState]; } std::vector v; v.reserve(numActions); for(auto const& oldState : result.newToOldStateIndexMapping){ for (uint_fast64_t oldAction = originalRowGroupIndices[oldState]; oldAction < originalRowGroupIndices[oldState+1]; ++oldAction){ v.push_back(originalVector[oldAction]); } } STORM_LOG_ASSERT(v.size() == numActions, "Unexpected vector size."); return v; } template static std::vector transformStateValueVector(std::vector const& originalVector, StateDuplicatorReturnType const& result) { uint_fast64_t numStates = result.newToOldStateIndexMapping.size(); std::vector v; v.reserve(numStates); for(auto const& oldState : result.newToOldStateIndexMapping){ v.push_back(originalVector[oldState]); } STORM_LOG_ASSERT(v.size() == numStates, "Unexpected vector size."); return v; } static storm::storage::BitVector transformStateBitVector(storm::storage::BitVector const& originalBitVector, StateDuplicatorReturnType const& result) { uint_fast64_t numStates = result.newToOldStateIndexMapping.size(); storm::storage::BitVector bv(numStates); for(uint_fast64_t newState = 0; newState < numStates; ++newState){ uint_fast64_t oldState = result.newToOldStateIndexMapping[newState]; bv.set(newState, originalBitVector.get(oldState)); } return bv; } template static typename std::enable_if< std::is_same>::value || std::is_same>::value || std::is_same>::value, MT>::type createTransformedModel(MT const& originalModel, StateDuplicatorReturnType const& result, storm::storage::SparseMatrix& matrix, storm::models::sparse::StateLabeling& stateLabeling, std::unordered_map& rewardModels, boost::optional>& choiceLabeling ) { return MT(std::move(matrix), std::move(stateLabeling), std::move(rewardModels), std::move(choiceLabeling)); } template static typename std::enable_if< std::is_same>::value, MT>::type createTransformedModel(MT const& originalModel, StateDuplicatorReturnType const& result, storm::storage::SparseMatrix& matrix, storm::models::sparse::StateLabeling& stateLabeling, std::unordered_map& rewardModels, boost::optional>& choiceLabeling ) { storm::storage::BitVector markovianStates = transformStateBitVector(originalModel.getMarkovianStates(), result); std::vector exitRates = transformStateValueVector(originalModel.getExitRates(), result); return MT(std::move(matrix), std::move(stateLabeling), std::move(markovianStates), std::move(exitRates), true, std::move(rewardModels), std::move(choiceLabeling)); } }; } } #endif // STORM_TRANSFORMER_STATEDUPLICATOR_H