Storm
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
GoalStateMerger.cpp
Go to the documentation of this file.
2
3#include <limits>
4#include <memory>
6
15
18
19namespace storm {
20namespace transformer {
21
22template<typename SparseModelType>
23GoalStateMerger<SparseModelType>::GoalStateMerger(SparseModelType const& model) : originalModel(model) {
24 // Intentionally left empty
25}
26
27template<typename SparseModelType>
29 storm::storage::BitVector const& maybeStates, storm::storage::BitVector const& targetStates, storm::storage::BitVector const& sinkStates,
30 std::vector<std::string> const& selectedRewardModels, boost::optional<storm::storage::BitVector> const& choiceFilter) const {
31 STORM_LOG_THROW(maybeStates.isDisjointFrom(targetStates) && targetStates.isDisjointFrom(sinkStates) && sinkStates.isDisjointFrom(maybeStates),
32 storm::exceptions::InvalidArgumentException,
33 "maybestates, targetstates, and sinkstates are assumed to be disjoint when creating the submodel. However, this is not the case.");
34
35 auto result = initialize(maybeStates, targetStates, sinkStates, choiceFilter);
36
37 auto transitionMatrix = buildTransitionMatrix(maybeStates, result.first, result.second);
38 auto labeling = buildStateLabeling(maybeStates, targetStates, sinkStates, result.first);
39 auto rewardModels = buildRewardModels(maybeStates, result.first, selectedRewardModels);
40
41 result.first.model = buildOutputModel(maybeStates, result.first, std::move(transitionMatrix), std::move(labeling), std::move(rewardModels));
42
43 return result.first;
44}
45
46template<typename SparseModelType>
47std::pair<typename GoalStateMerger<SparseModelType>::ReturnType, uint_fast64_t> GoalStateMerger<SparseModelType>::initialize(
48 storm::storage::BitVector const& maybeStates, storm::storage::BitVector const& targetStates, storm::storage::BitVector const& sinkStates,
49 boost::optional<storm::storage::BitVector> const& choiceFilter) const {
50 storm::storage::SparseMatrix<typename SparseModelType::ValueType> const& origMatrix = originalModel.getTransitionMatrix();
51
52 ReturnType result;
53 result.keptChoices = storm::storage::BitVector(origMatrix.getRowCount(), false);
54 result.oldToNewStateIndexMapping =
55 std::vector<uint_fast64_t>(maybeStates.size(), std::numeric_limits<uint_fast64_t>::max()); // init with some invalid state
56
57 uint_fast64_t transitionCount(0), stateCount(0);
58 bool targetStateRequired = !originalModel.getInitialStates().isDisjointFrom(targetStates);
59 bool sinkStateRequired = !originalModel.getInitialStates().isDisjointFrom(sinkStates);
60 for (auto state : maybeStates) {
61 result.oldToNewStateIndexMapping[state] = stateCount;
62
63 auto const& endOfRowGroup = origMatrix.getRowGroupIndices()[state + 1];
64 bool stateIsDeadlock = true;
65 for (uint_fast64_t row = origMatrix.getRowGroupIndices()[state]; row < endOfRowGroup; ++row) {
66 uint_fast64_t transitionsToMaybeStates = 0;
67 bool keepThisRow(true), hasTransitionToTarget(false), hasTransitionToSink(false);
68 if (!choiceFilter || choiceFilter.get().get(row)) {
69 for (auto const& entry : origMatrix.getRow(row)) {
70 if (maybeStates.get(entry.getColumn())) {
71 ++transitionsToMaybeStates;
72 } else if (targetStates.get(entry.getColumn())) {
73 hasTransitionToTarget = true;
74 } else if (sinkStates.get(entry.getColumn())) {
75 hasTransitionToSink = true;
76 } else {
77 keepThisRow = false;
78 break;
79 }
80 }
81 if (keepThisRow) {
82 stateIsDeadlock = false;
83 result.keptChoices.set(row, true);
84 transitionCount += transitionsToMaybeStates;
85 if (hasTransitionToTarget) {
86 ++transitionCount;
87 targetStateRequired = true;
88 }
89 if (hasTransitionToSink) {
90 ++transitionCount;
91 sinkStateRequired = true;
92 }
93 }
94 }
95 }
96 STORM_LOG_THROW(!stateIsDeadlock, storm::exceptions::InvalidArgumentException, "Merging goal states leads to deadlocks!");
97 ++stateCount;
98 }
99
100 // Treat the target and sink states (if these states will exist)
101 if (targetStateRequired) {
102 result.targetState = stateCount;
103 ++stateCount;
104 ++transitionCount;
105 storm::utility::vector::setVectorValues(result.oldToNewStateIndexMapping, targetStates, *result.targetState);
106 }
107
108 if (sinkStateRequired) {
109 result.sinkState = stateCount;
110 ++stateCount;
111 ++transitionCount;
112 storm::utility::vector::setVectorValues(result.oldToNewStateIndexMapping, sinkStates, *result.sinkState);
113 }
114
115 return std::make_pair(std::move(result), std::move(transitionCount));
116}
117
118template<typename SparseModelType>
119storm::storage::SparseMatrix<typename SparseModelType::ValueType> GoalStateMerger<SparseModelType>::buildTransitionMatrix(
120 storm::storage::BitVector const& maybeStates, ReturnType const& resultData, uint_fast64_t transitionCount) const {
121 storm::storage::SparseMatrix<typename SparseModelType::ValueType> const& origMatrix = originalModel.getTransitionMatrix();
122
123 uint_fast64_t rowCount = resultData.keptChoices.getNumberOfSetBits() + (resultData.targetState ? 1 : 0) + (resultData.sinkState ? 1 : 0);
124 uint_fast64_t maybeStateCount = maybeStates.getNumberOfSetBits();
125 uint_fast64_t stateCount = maybeStateCount + (resultData.targetState ? 1 : 0) + (resultData.sinkState ? 1 : 0);
127 rowCount, stateCount, transitionCount, true, !origMatrix.hasTrivialRowGrouping(), origMatrix.hasTrivialRowGrouping() ? 0 : stateCount);
128
129 uint_fast64_t currRow = 0;
130 for (auto state : maybeStates) {
131 if (!origMatrix.hasTrivialRowGrouping()) {
132 builder.newRowGroup(currRow);
133 }
134 auto const& endOfRowGroup = origMatrix.getRowGroupIndices()[state + 1];
135 for (uint_fast64_t row = resultData.keptChoices.getNextSetIndex(origMatrix.getRowGroupIndices()[state]); row < endOfRowGroup;
136 row = resultData.keptChoices.getNextSetIndex(row + 1)) {
137 boost::optional<typename SparseModelType::ValueType> targetValue, sinkValue;
138 for (auto const& entry : origMatrix.getRow(row)) {
139 uint_fast64_t const& newColumn = resultData.oldToNewStateIndexMapping[entry.getColumn()];
140 if (newColumn < maybeStateCount) {
141 builder.addNextValue(currRow, newColumn, entry.getValue());
142 } else if (resultData.targetState && newColumn == resultData.targetState.get()) {
143 targetValue = targetValue.is_initialized() ? *targetValue + entry.getValue() : entry.getValue();
144 } else if (resultData.sinkState && newColumn == resultData.sinkState.get()) {
145 sinkValue = sinkValue.is_initialized() ? *sinkValue + entry.getValue() : entry.getValue();
146 } else {
147 STORM_LOG_THROW(false, storm::exceptions::UnexpectedException,
148 "There is a transition originating from a maybestate that does not lead to a maybe-, target-, or sinkstate.");
149 }
150 }
151 if (targetValue) {
152 builder.addNextValue(currRow, *resultData.targetState, storm::utility::simplify(*targetValue));
153 }
154 if (sinkValue) {
155 builder.addNextValue(currRow, *resultData.sinkState, storm::utility::simplify(*sinkValue));
156 }
157 ++currRow;
158 }
159 }
160
161 // Add the selfloops at target and sink
162 if (resultData.targetState) {
163 if (!origMatrix.hasTrivialRowGrouping()) {
164 builder.newRowGroup(currRow);
165 }
166 builder.addNextValue(currRow, *resultData.targetState, storm::utility::one<typename SparseModelType::ValueType>());
167 ++currRow;
168 }
169 if (resultData.sinkState) {
170 if (!origMatrix.hasTrivialRowGrouping()) {
171 builder.newRowGroup(currRow);
172 }
173 builder.addNextValue(currRow, *resultData.sinkState, storm::utility::one<typename SparseModelType::ValueType>());
174 ++currRow;
175 }
176
177 return builder.build();
178}
179
180template<typename SparseModelType>
181storm::models::sparse::StateLabeling GoalStateMerger<SparseModelType>::buildStateLabeling(storm::storage::BitVector const& maybeStates,
182 storm::storage::BitVector const& targetStates,
183 storm::storage::BitVector const& sinkStates,
184 ReturnType const& resultData) const {
185 uint_fast64_t stateCount = maybeStates.getNumberOfSetBits() + (resultData.targetState ? 1 : 0) + (resultData.sinkState ? 1 : 0);
186 storm::models::sparse::StateLabeling labeling(stateCount);
187
188 for (auto const& label : originalModel.getStateLabeling().getLabels()) {
189 storm::storage::BitVector const& oldStatesWithLabel = originalModel.getStates(label);
190 storm::storage::BitVector newStatesWithLabel = oldStatesWithLabel % maybeStates;
191 newStatesWithLabel.resize(stateCount, false);
192 if (!oldStatesWithLabel.isDisjointFrom(targetStates) && resultData.targetState.is_initialized()) {
193 newStatesWithLabel.set(*resultData.targetState, true);
194 }
195 if (!oldStatesWithLabel.isDisjointFrom(sinkStates) && resultData.sinkState.is_initialized()) {
196 newStatesWithLabel.set(*resultData.sinkState, true);
197 }
198 labeling.addLabel(label, std::move(newStatesWithLabel));
199 }
200
201 return labeling;
202}
203
204template<typename SparseModelType>
205std::unordered_map<std::string, typename SparseModelType::RewardModelType> GoalStateMerger<SparseModelType>::buildRewardModels(
206 storm::storage::BitVector const& maybeStates, ReturnType const& resultData, std::vector<std::string> const& selectedRewardModels) const {
207 typedef typename SparseModelType::RewardModelType::ValueType RewardValueType;
208
209 uint_fast64_t maybeStateCount = maybeStates.getNumberOfSetBits();
210 uint_fast64_t stateCount = maybeStateCount + (resultData.targetState ? 1 : 0) + (resultData.sinkState ? 1 : 0);
211 uint_fast64_t choiceCount = resultData.keptChoices.getNumberOfSetBits() + (resultData.targetState ? 1 : 0) + (resultData.sinkState ? 1 : 0);
212
213 std::unordered_map<std::string, typename SparseModelType::RewardModelType> rewardModels;
214 for (auto rewardModelName : selectedRewardModels) {
215 auto origRewardModel = originalModel.getRewardModel(rewardModelName);
216
217 std::optional<std::vector<RewardValueType>> stateRewards;
218 if (origRewardModel.hasStateRewards()) {
219 stateRewards = storm::utility::vector::filterVector(origRewardModel.getStateRewardVector(), maybeStates);
220 stateRewards->resize(stateCount, storm::utility::zero<RewardValueType>());
221 }
222
223 std::optional<std::vector<RewardValueType>> stateActionRewards;
224 if (origRewardModel.hasStateActionRewards()) {
225 stateActionRewards = storm::utility::vector::filterVector(origRewardModel.getStateActionRewardVector(), resultData.keptChoices);
226 stateActionRewards->resize(choiceCount, storm::utility::zero<RewardValueType>());
227 }
228
229 std::optional<storm::storage::SparseMatrix<RewardValueType>> transitionRewards;
230 if (origRewardModel.hasTransitionRewards()) {
231 storm::storage::SparseMatrixBuilder<RewardValueType> builder(choiceCount, stateCount, 0, true);
232 for (auto row : resultData.keptChoices) {
233 boost::optional<typename SparseModelType::ValueType> targetValue, sinkValue;
234 for (auto const& entry : origRewardModel.getTransitionRewardMatrix().getRow(row)) {
235 uint_fast64_t const& newColumn = resultData.oldToNewStateIndexMapping[entry.getColumn()];
236 if (newColumn < maybeStateCount) {
237 builder.addNextValue(row, newColumn, entry.getValue());
238 } else if (resultData.targetState && newColumn == resultData.targetState.get()) {
239 targetValue = targetValue.is_initialized() ? *targetValue + entry.getValue() : entry.getValue();
240 } else if (resultData.sinkState && newColumn == resultData.sinkState.get()) {
241 sinkValue = sinkValue.is_initialized() ? *sinkValue + entry.getValue() : entry.getValue();
242 } else {
243 STORM_LOG_THROW(false, storm::exceptions::UnexpectedException,
244 "There is a transition reward originating from a maybestate that does not lead to a maybe-, target-, or sinkstate.");
245 }
246 }
247 if (targetValue) {
248 builder.addNextValue(row, *resultData.targetState, storm::utility::simplify(*targetValue));
249 }
250 if (sinkValue) {
251 builder.addNextValue(row, *resultData.sinkState, storm::utility::simplify(*sinkValue));
252 }
253 }
254 transitionRewards = builder.build();
255 }
256
257 rewardModels.insert(std::make_pair(
258 rewardModelName, typename SparseModelType::RewardModelType(std::move(stateRewards), std::move(stateActionRewards), std::move(transitionRewards))));
259 }
260 return rewardModels;
261}
262
263template<>
264std::shared_ptr<storm::models::sparse::MarkovAutomaton<double>> GoalStateMerger<storm::models::sparse::MarkovAutomaton<double>>::buildOutputModel(
265 storm::storage::BitVector const& maybeStates, ReturnType const& resultData, storm::storage::SparseMatrix<double>&& transitionMatrix,
267 std::unordered_map<std::string, typename storm::models::sparse::MarkovAutomaton<double>::RewardModelType>&& rewardModels) const {
268 storm::storage::sparse::ModelComponents<double> modelComponents(std::move(transitionMatrix), std::move(labeling), std::move(rewardModels));
269 uint_fast64_t stateCount = maybeStates.getNumberOfSetBits() + (resultData.targetState ? 1 : 0) + (resultData.sinkState ? 1 : 0);
270
271 modelComponents.markovianStates = originalModel.getMarkovianStates() % maybeStates;
272 modelComponents.markovianStates->resize(stateCount, true);
273
274 modelComponents.exitRates = storm::utility::vector::filterVector(originalModel.getExitRates(), maybeStates);
275 modelComponents.exitRates->resize(stateCount, storm::utility::one<double>());
276
277 return std::make_shared<storm::models::sparse::MarkovAutomaton<double>>(std::move(modelComponents));
278}
279
280template<>
281std::shared_ptr<storm::models::sparse::MarkovAutomaton<storm::RationalNumber>>
282GoalStateMerger<storm::models::sparse::MarkovAutomaton<storm::RationalNumber>>::buildOutputModel(
283 storm::storage::BitVector const& maybeStates, ReturnType const& resultData, storm::storage::SparseMatrix<storm::RationalNumber>&& transitionMatrix,
285 std::unordered_map<std::string, typename storm::models::sparse::MarkovAutomaton<storm::RationalNumber>::RewardModelType>&& rewardModels) const {
286 storm::storage::sparse::ModelComponents<storm::RationalNumber> modelComponents(std::move(transitionMatrix), std::move(labeling), std::move(rewardModels));
287 uint_fast64_t stateCount = maybeStates.getNumberOfSetBits() + (resultData.targetState ? 1 : 0) + (resultData.sinkState ? 1 : 0);
288
289 modelComponents.markovianStates = originalModel.getMarkovianStates() % maybeStates;
290 modelComponents.markovianStates->resize(stateCount, true);
291
292 modelComponents.exitRates = storm::utility::vector::filterVector(originalModel.getExitRates(), maybeStates);
293 modelComponents.exitRates->resize(stateCount, storm::utility::one<storm::RationalNumber>());
294
295 return std::make_shared<storm::models::sparse::MarkovAutomaton<storm::RationalNumber>>(std::move(modelComponents));
296}
297
298template<typename SparseModelType>
299std::shared_ptr<SparseModelType> GoalStateMerger<SparseModelType>::buildOutputModel(
300 storm::storage::BitVector const&, GoalStateMerger::ReturnType const&, storm::storage::SparseMatrix<typename SparseModelType::ValueType>&& transitionMatrix,
301 storm::models::sparse::StateLabeling&& labeling, std::unordered_map<std::string, typename SparseModelType::RewardModelType>&& rewardModels) const {
302 return std::make_shared<SparseModelType>(std::move(transitionMatrix), std::move(labeling), std::move(rewardModels));
303}
304
305template class GoalStateMerger<storm::models::sparse::Dtmc<double>>;
306template class GoalStateMerger<storm::models::sparse::Mdp<double>>;
307template class GoalStateMerger<storm::models::sparse::MarkovAutomaton<double>>;
308template class GoalStateMerger<storm::models::sparse::Dtmc<storm::RationalNumber>>;
309template class GoalStateMerger<storm::models::sparse::Mdp<storm::RationalNumber>>;
310template class GoalStateMerger<storm::models::sparse::MarkovAutomaton<storm::RationalNumber>>;
311template class GoalStateMerger<storm::models::sparse::Dtmc<storm::RationalFunction>>;
312template class GoalStateMerger<storm::models::sparse::Mdp<storm::RationalFunction>>;
313
314} // namespace transformer
315} // namespace storm
This class manages the labeling of the state space with a number of (atomic) labels.
A bit vector that is internally represented as a vector of 64-bit values.
Definition BitVector.h:18
bool isDisjointFrom(BitVector const &other) const
Checks whether none of the bits that are set in the current bit vector are also set in the given bit ...
void resize(uint_fast64_t newLength, bool init=false)
Resizes the bit vector to hold the given new number of bits.
void set(uint_fast64_t index, bool value=true)
Sets the given truth value at the given index.
size_t size() const
Retrieves the number of bits this bit vector can store.
uint_fast64_t getNumberOfSetBits() const
Returns the number of bits that are set to true in this bit vector.
bool get(uint_fast64_t index) const
Retrieves the truth value of the bit at the given index and performs a bound check.
A class that can be used to build a sparse matrix by adding value by value.
void newRowGroup(index_type startingRow)
Starts a new row group in the matrix.
A class that holds a possibly non-square matrix in the compressed row storage format.
bool hasTrivialRowGrouping() const
Retrieves whether the matrix has a trivial row grouping.
std::vector< index_type > const & getRowGroupIndices() const
Returns the grouping of rows of this matrix.
index_type getRowCount() const
Returns the number of rows of the matrix.
ReturnType mergeTargetAndSinkStates(storm::storage::BitVector const &maybeStates, storm::storage::BitVector const &targetStates, storm::storage::BitVector const &sinkStates, std::vector< std::string > const &selectedRewardModels=std::vector< std::string >(), boost::optional< storm::storage::BitVector > const &choiceFilter=boost::none) const
GoalStateMerger(SparseModelType const &model)
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:30
void setVectorValues(std::vector< T > &vector, storm::storage::BitVector const &positions, std::vector< T > const &values)
Sets the provided values at the provided positions in the given vector.
Definition vector.h:83
std::vector< Type > filterVector(std::vector< Type > const &in, storm::storage::BitVector const &filter)
Definition vector.h:1213
ValueType simplify(ValueType value)
LabParser.cpp.
Definition cli.cpp:18