You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
268 lines
18 KiB
268 lines
18 KiB
#ifndef STORM_TRANSFORMER_STATEDUPLICATOR_H
|
|
#define STORM_TRANSFORMER_STATEDUPLICATOR_H
|
|
|
|
|
|
#include <memory>
|
|
#include <boost/optional.hpp>
|
|
|
|
#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 <typename SparseModelType>
|
|
class StateDuplicator {
|
|
public:
|
|
|
|
struct StateDuplicatorReturnType {
|
|
std::shared_ptr<SparseModelType> 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<uint_fast64_t> newToOldStateIndexMapping; // Gives for each state in the resulting model the corresponding state in the original model
|
|
std::vector<uint_fast64_t> firstCopyOldToNewStateIndexMapping; //Maps old indices of states in the first copy to their new indices
|
|
std::vector<uint_fast64_t> 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<typename SparseModelType::ValueType> 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<std::string, typename SparseModelType::RewardModelType> rewardModels;
|
|
for(auto const& rewardModel : originalModel.getRewardModels()){
|
|
rewardModels.insert(std::make_pair(rewardModel.first, transformRewardModel(rewardModel.second, originalModel.getTransitionMatrix().getRowGroupIndices(), result, gateStates)));
|
|
}
|
|
boost::optional<std::vector<storm::models::sparse::LabelSet>> choiceLabeling;
|
|
if(originalModel.hasChoiceLabeling()){
|
|
choiceLabeling = transformActionValueVector<storm::models::sparse::LabelSet>(originalModel.getChoiceLabeling(), originalModel.getTransitionMatrix().getRowGroupIndices(), result);
|
|
}
|
|
|
|
result.model = std::make_shared<SparseModelType>(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<uint_fast64_t>(numStates, std::numeric_limits<uint_fast64_t>::max());
|
|
result.firstCopyOldToNewStateIndexMapping = std::vector<uint_fast64_t>(originalModel.getNumberOfStates(), std::numeric_limits<uint_fast64_t>::max());
|
|
result.secondCopyOldToNewStateIndexMapping = std::vector<uint_fast64_t>(originalModel.getNumberOfStates(), std::numeric_limits<uint_fast64_t>::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<typename ValueType = typename SparseModelType::ValueType, typename RewardModelType = typename SparseModelType::RewardModelType>
|
|
static typename std::enable_if<std::is_same<RewardModelType, storm::models::sparse::StandardRewardModel<ValueType>>::value, RewardModelType>::type
|
|
transformRewardModel(RewardModelType const& originalRewardModel, std::vector<uint_fast64_t> const& originalRowGroupIndices, StateDuplicatorReturnType const& result, storm::storage::BitVector const& gateStates) {
|
|
boost::optional<std::vector<ValueType>> stateRewardVector;
|
|
boost::optional<std::vector<ValueType>> stateActionRewardVector;
|
|
boost::optional<storm::storage::SparseMatrix<ValueType>> 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<typename ValueType = typename SparseModelType::ValueType>
|
|
static storm::storage::SparseMatrix<ValueType> transformMatrix(storm::storage::SparseMatrix<ValueType> 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<ValueType> 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<typename ValueType = typename SparseModelType::ValueType>
|
|
static std::vector<ValueType> transformActionValueVector(std::vector<ValueType>const& originalVector, std::vector<uint_fast64_t> const& originalRowGroupIndices, StateDuplicatorReturnType const& result) {
|
|
uint_fast64_t numActions = 0;
|
|
for(auto const& oldState : result.newToOldStateIndexMapping){
|
|
numActions += originalRowGroupIndices[oldState+1] - originalRowGroupIndices[oldState];
|
|
}
|
|
std::vector<ValueType> 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<typename ValueType = typename SparseModelType::ValueType>
|
|
static std::vector<ValueType> transformStateValueVector(std::vector<ValueType> const& originalVector, StateDuplicatorReturnType const& result) {
|
|
uint_fast64_t numStates = result.newToOldStateIndexMapping.size();
|
|
std::vector<ValueType> 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<typename MT = SparseModelType>
|
|
static typename std::enable_if<
|
|
std::is_same<MT,storm::models::sparse::Dtmc<typename SparseModelType::ValueType>>::value ||
|
|
std::is_same<MT,storm::models::sparse::Mdp<typename SparseModelType::ValueType>>::value ||
|
|
std::is_same<MT,storm::models::sparse::Ctmc<typename SparseModelType::ValueType>>::value,
|
|
MT>::type
|
|
createTransformedModel(MT const& originalModel,
|
|
StateDuplicatorReturnType const& result,
|
|
storm::storage::SparseMatrix<typename MT::ValueType>& matrix,
|
|
storm::models::sparse::StateLabeling& stateLabeling,
|
|
std::unordered_map<std::string,
|
|
typename MT::RewardModelType>& rewardModels,
|
|
boost::optional<std::vector<typename storm::models::sparse::LabelSet>>& choiceLabeling ) {
|
|
return MT(std::move(matrix), std::move(stateLabeling), std::move(rewardModels), std::move(choiceLabeling));
|
|
}
|
|
|
|
template<typename MT = SparseModelType>
|
|
static typename std::enable_if<
|
|
std::is_same<MT,storm::models::sparse::MarkovAutomaton<typename SparseModelType::ValueType>>::value,
|
|
MT>::type
|
|
createTransformedModel(MT const& originalModel,
|
|
StateDuplicatorReturnType const& result,
|
|
storm::storage::SparseMatrix<typename MT::ValueType>& matrix,
|
|
storm::models::sparse::StateLabeling& stateLabeling,
|
|
std::unordered_map<std::string,
|
|
typename MT::RewardModelType>& rewardModels,
|
|
boost::optional<std::vector<typename storm::models::sparse::LabelSet>>& choiceLabeling ) {
|
|
storm::storage::BitVector markovianStates = transformStateBitVector(originalModel.getMarkovianStates(), result);
|
|
std::vector<typename MT::ValueType> 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
|