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