Alexander Bork
6 years ago
5 changed files with 264 additions and 200 deletions
-
6src/storm-dft/builder/ExplicitDFTModelBuilder.cpp
-
197src/storm/models/sparse/MarkovAutomaton.cpp
-
1src/storm/models/sparse/MarkovAutomaton.h
-
233src/storm/transformer/NonMarkovianChainTransformer.cpp
-
27src/storm/transformer/NonMarkovianChainTransformer.h
@ -0,0 +1,233 @@ |
|||
#include <queue>
|
|||
|
|||
#include "NonMarkovianChainTransformer.h"
|
|||
|
|||
#include "storm/storage/sparse/ModelComponents.h"
|
|||
#include "storm/adapters/RationalFunctionAdapter.h"
|
|||
#include "storm/models/sparse/StandardRewardModel.h"
|
|||
#include "storm/utility/constants.h"
|
|||
#include "storm/utility/ConstantsComparator.h"
|
|||
#include "storm/utility/vector.h"
|
|||
#include "storm/utility/macros.h"
|
|||
#include "storm/utility/graph.h"
|
|||
|
|||
namespace storm { |
|||
namespace transformer { |
|||
|
|||
template<typename ValueType, typename RewardModelType> |
|||
std::shared_ptr<models::sparse::Model<ValueType, RewardModelType>> |
|||
NonMarkovianChainTransformer<ValueType, RewardModelType>::eliminateNonmarkovianStates( |
|||
std::shared_ptr<models::sparse::MarkovAutomaton<ValueType, RewardModelType>> ma, |
|||
bool preserveLabels) { |
|||
// TODO reward models
|
|||
|
|||
STORM_LOG_WARN("State elimination is currently not label preserving!"); |
|||
STORM_LOG_WARN("Reward Models and Choice Labelings are ignored!"); |
|||
if (ma->isClosed() && ma->getMarkovianStates().full()) { |
|||
storm::storage::sparse::ModelComponents<ValueType, RewardModelType> components( |
|||
ma->getTransitionMatrix(), ma->getStateLabeling(), ma->getRewardModels(), false); |
|||
components.exitRates = ma->getExitRates(); |
|||
if (ma->hasChoiceLabeling()) { |
|||
components.choiceLabeling = ma->getChoiceLabeling(); |
|||
} |
|||
if (ma->hasStateValuations()) { |
|||
components.stateValuations = ma->getStateValuations(); |
|||
} |
|||
if (ma->hasChoiceOrigins()) { |
|||
components.choiceOrigins = ma->getChoiceOrigins(); |
|||
} |
|||
return std::make_shared<storm::models::sparse::MarkovAutomaton<ValueType, RewardModelType>>( |
|||
std::move(components)); |
|||
} |
|||
|
|||
std::map<uint_fast64_t, uint_fast64_t> eliminationMapping; |
|||
std::set<uint_fast64_t> statesToKeep; |
|||
std::queue<uint_fast64_t> changedStates; |
|||
std::queue<uint_fast64_t> queue; |
|||
|
|||
storm::storage::SparseMatrix<ValueType> backwards = ma->getBackwardTransitions(); |
|||
|
|||
// Determine the state remapping
|
|||
// TODO Consider state labels
|
|||
for (uint_fast64_t base_state = 0; base_state < ma->getNumberOfStates(); ++base_state) { |
|||
STORM_LOG_ASSERT(!ma->isHybridState(base_state), "Base state is hybrid."); |
|||
if (ma->isMarkovianState(base_state)) { |
|||
queue.push(base_state); |
|||
|
|||
while (!queue.empty()) { |
|||
auto currState = queue.front(); |
|||
queue.pop(); |
|||
// Get predecessors from matrix
|
|||
typename storm::storage::SparseMatrix<ValueType>::rows entriesInRow = backwards.getRow( |
|||
currState); |
|||
for (auto entryIt = entriesInRow.begin(), entryIte = entriesInRow.end(); |
|||
entryIt != entryIte; ++entryIt) { |
|||
uint_fast64_t predecessor = entryIt->getColumn(); |
|||
if (!ma->isMarkovianState(predecessor) && !statesToKeep.count(predecessor)) { |
|||
if (!eliminationMapping.count(predecessor)) { |
|||
eliminationMapping[predecessor] = base_state; |
|||
queue.push(predecessor); |
|||
} else if (eliminationMapping[predecessor] != base_state) { |
|||
eliminationMapping.erase(predecessor); |
|||
statesToKeep.insert(predecessor); |
|||
changedStates.push(predecessor); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
// Correct the mapping with the states which have to be kept
|
|||
while (!changedStates.empty()) { |
|||
uint_fast64_t base_state = changedStates.front(); |
|||
queue.push(base_state); |
|||
|
|||
while (!queue.empty()) { |
|||
auto currState = queue.front(); |
|||
queue.pop(); |
|||
// Get predecessors from matrix
|
|||
typename storm::storage::SparseMatrix<ValueType>::rows entriesInRow = backwards.getRow( |
|||
currState); |
|||
for (auto entryIt = entriesInRow.begin(), entryIte = entriesInRow.end(); |
|||
entryIt != entryIte; ++entryIt) { |
|||
uint_fast64_t predecessor = entryIt->getColumn(); |
|||
if (!ma->isMarkovianState(predecessor) && !statesToKeep.count(predecessor)) { |
|||
if (!eliminationMapping.count(predecessor)) { |
|||
eliminationMapping[predecessor] = base_state; |
|||
queue.push(predecessor); |
|||
} else if (eliminationMapping[predecessor] != base_state) { |
|||
eliminationMapping.erase(predecessor); |
|||
statesToKeep.insert(predecessor); |
|||
changedStates.push(predecessor); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
changedStates.pop(); |
|||
} |
|||
|
|||
// At ma point, we hopefully have a valid mapping which eliminates a lot of states
|
|||
|
|||
/*STORM_PRINT("Elimination Mapping" << std::endl)
|
|||
for (auto entry : eliminationMapping) { |
|||
STORM_PRINT(std::to_string(entry.first) << " -> " << std::to_string(entry.second) << std::endl) |
|||
}*/ |
|||
STORM_PRINT("Eliminating " << eliminationMapping.size() << " states" << std::endl) |
|||
|
|||
// TODO explore if one can construct elimination mapping and state remapping in one step
|
|||
|
|||
// Construct a mapping of old state space to new one
|
|||
std::vector<uint_fast64_t> stateRemapping(ma->getNumberOfStates(), -1); |
|||
uint_fast64_t currentNewState = 0; |
|||
for (uint_fast64_t state = 0; state < ma->getNumberOfStates(); ++state) { |
|||
if (eliminationMapping.count(state) > 0) { |
|||
if (stateRemapping[eliminationMapping[state]] == uint_fast64_t(-1)) { |
|||
stateRemapping[eliminationMapping[state]] = currentNewState; |
|||
stateRemapping[state] = currentNewState; |
|||
++currentNewState; |
|||
queue.push(eliminationMapping[state]); |
|||
} else { |
|||
stateRemapping[state] = stateRemapping[eliminationMapping[state]]; |
|||
} |
|||
} else if (stateRemapping[state] == uint_fast64_t(-1)) { |
|||
stateRemapping[state] = currentNewState; |
|||
queue.push(state); |
|||
++currentNewState; |
|||
} |
|||
} |
|||
|
|||
// Build the new MA
|
|||
storm::storage::SparseMatrix<ValueType> newTransitionMatrix; |
|||
storm::models::sparse::StateLabeling newStateLabeling( |
|||
ma->getNumberOfStates() - eliminationMapping.size()); |
|||
storm::storage::BitVector newMarkovianStates(ma->getNumberOfStates() - eliminationMapping.size(), |
|||
false); |
|||
std::vector<ValueType> newExitRates; |
|||
//TODO choice labeling
|
|||
boost::optional<storm::models::sparse::ChoiceLabeling> choiceLabeling; |
|||
|
|||
// Initialize the matrix builder and helper variables
|
|||
storm::storage::SparseMatrixBuilder<ValueType> matrixBuilder = storm::storage::SparseMatrixBuilder<ValueType>( |
|||
0, 0, 0, false, true, 0); |
|||
uint_fast64_t currentRow = 0; |
|||
uint_fast64_t state = 0; |
|||
while (!queue.empty()) { |
|||
state = queue.front(); |
|||
queue.pop(); |
|||
|
|||
for (auto const &label : ma->getLabelsOfState(state)) { |
|||
if (!newStateLabeling.containsLabel(label)) { |
|||
newStateLabeling.addLabel(label); |
|||
} |
|||
|
|||
newStateLabeling.addLabelToState(label, stateRemapping[state]); |
|||
} |
|||
|
|||
// Use a set to not include redundant rows
|
|||
std::set<std::map<uint_fast64_t, ValueType>> rowSet; |
|||
for (uint_fast64_t row = 0; row < ma->getTransitionMatrix().getRowGroupSize(state); ++row) { |
|||
std::map<uint_fast64_t, ValueType> transitions; |
|||
for (typename storm::storage::SparseMatrix<ValueType>::const_iterator itEntry = ma->getTransitionMatrix().getRow( |
|||
state, row).begin(); |
|||
itEntry != ma->getTransitionMatrix().getRow(state, row).end(); ++itEntry) { |
|||
uint_fast64_t newId = stateRemapping[itEntry->getColumn()]; |
|||
if (transitions.count(newId) == 0) { |
|||
transitions[newId] = itEntry->getValue(); |
|||
} else { |
|||
transitions[newId] += itEntry->getValue(); |
|||
} |
|||
} |
|||
rowSet.insert(transitions); |
|||
} |
|||
|
|||
// correctly set rates
|
|||
auto rate = storm::utility::zero<ValueType>(); |
|||
|
|||
if (ma->isMarkovianState(state)) { |
|||
newMarkovianStates.set(stateRemapping[state], true); |
|||
rate = ma->getExitRates().at(state); |
|||
} |
|||
|
|||
newExitRates.push_back(rate); |
|||
// Build matrix
|
|||
matrixBuilder.newRowGroup(currentRow); |
|||
for (auto const &row : rowSet) { |
|||
for (auto const &transition : row) { |
|||
matrixBuilder.addNextValue(currentRow, transition.first, transition.second); |
|||
//STORM_PRINT(stateRemapping[state] << "->" << transition.first << " : " << transition.second << std::endl)
|
|||
} |
|||
++currentRow; |
|||
} |
|||
} |
|||
newTransitionMatrix = matrixBuilder.build(); |
|||
|
|||
storm::storage::sparse::ModelComponents<ValueType, RewardModelType> newComponents = storm::storage::sparse::ModelComponents<ValueType, RewardModelType>( |
|||
std::move(newTransitionMatrix), std::move(newStateLabeling)); |
|||
|
|||
newComponents.rateTransitions = false; |
|||
newComponents.markovianStates = std::move(newMarkovianStates); |
|||
newComponents.exitRates = std::move(newExitRates); |
|||
auto model = std::make_shared<storm::models::sparse::MarkovAutomaton<ValueType, RewardModelType >>( |
|||
std::move(newComponents)); |
|||
if (model->isConvertibleToCtmc()) { |
|||
return model->convertToCtmc(); |
|||
} else { |
|||
return model; |
|||
} |
|||
} |
|||
|
|||
|
|||
template |
|||
class NonMarkovianChainTransformer<double>; |
|||
|
|||
#ifdef STORM_HAVE_CARL
|
|||
|
|||
template |
|||
class NonMarkovianChainTransformer<storm::RationalFunction>; |
|||
|
|||
#endif
|
|||
} |
|||
} |
|||
|
@ -0,0 +1,27 @@ |
|||
#include "storm/models/sparse/MarkovAutomaton.h" |
|||
|
|||
namespace storm { |
|||
namespace transformer { |
|||
/** |
|||
* Transformer for eliminating chains of non-Markovian states (instantaneous path fragment leading to the same outcome) from Markov Automata |
|||
*/ |
|||
template<typename ValueType, typename RewardModelType = storm::models::sparse::StandardRewardModel<ValueType>> |
|||
class NonMarkovianChainTransformer { |
|||
public: |
|||
/** |
|||
* Generates a model with the same basic behavior as the input, but eliminates non-Markovian chains. |
|||
* If no non-determinism occurs, a CTMC is generated. |
|||
* |
|||
* @param ma the input Markov Automaton |
|||
* @param preserveLabels if set, the procedure considers the labels of non-Markovian states when eliminating states |
|||
* @return a reference to the new Mmodel after eliminating non-Markovian states |
|||
*/ |
|||
static std::shared_ptr< |
|||
models::sparse::Model < ValueType, RewardModelType>> eliminateNonmarkovianStates(std::shared_ptr< |
|||
models::sparse::MarkovAutomaton < ValueType, RewardModelType>> ma, |
|||
bool preserveLabels = true |
|||
); |
|||
}; |
|||
} |
|||
} |
|||
|
Write
Preview
Loading…
Cancel
Save
Reference in new issue