Storm 1.13.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
GenerateMonitorVerifier.cpp
Go to the documentation of this file.
2#include <sys/types.h>
3
4#include <algorithm>
5#include <cstddef>
6#include <cstdint>
7#include <deque>
8#include <set>
9#include <string>
10#include <utility>
11#include <vector>
20
21namespace storm {
22namespace generator {
23
24template<typename ValueType>
26 const std::map<std::pair<uint32_t, bool>, uint32_t>& observationMap,
27 std::map<uint32_t, std::string> observationDefaultAction)
28 : product(storm::models::sparse::Pomdp<ValueType>(product)), observationMap(observationMap), observationDefaultAction(observationDefaultAction) {}
29
30template<typename ValueType>
31const std::map<std::pair<uint32_t, bool>, uint32_t>& MonitorVerifier<ValueType>::getObservationMap() {
32 return observationMap;
33}
34
35template<typename ValueType>
39
40template<typename ValueType>
41const std::map<uint32_t, std::string>& MonitorVerifier<ValueType>::getObservationDefaultAction() {
42 return observationDefaultAction;
43}
44
45template<typename ValueType>
47 std::shared_ptr<storm::expressions::ExpressionManager>& exprManager, Options const& options)
48 : mc(mc), monitor(monitor), risk(), exprManager(exprManager), options(options) {
49 monvar = exprManager->declareFreshIntegerVariable(false, "_mon");
50 mcvar = exprManager->declareFreshIntegerVariable(false, "_mc");
51}
52
53template<typename ValueType>
54std::shared_ptr<MonitorVerifier<ValueType>> GenerateMonitorVerifier<ValueType>::createProduct() {
55 typedef storm::storage::sparse::state_type state_type;
56 typedef std::pair<state_type, state_type> product_state_type;
57
58 STORM_LOG_THROW(monitor.hasChoiceLabeling(), storm::exceptions::InvalidArgumentException, "The monitor should contain choice labeling");
59
60 const std::set<std::string>& actions = monitor.getChoiceLabeling().getLabels();
61
62 // Build choice label map of monitor choices
63 std::vector<std::string> monitorChoiceLabels;
64 for (typename storm::storage::SparseMatrix<ValueType>::index_type i = 0; i < monitor.getTransitionMatrix().getRowCount(); i++) {
65 auto const& monitorLabels = monitor.getChoiceLabeling().getLabelsOfChoice(i);
66 STORM_LOG_THROW(monitorLabels.size() == 1, storm::exceptions::InvalidArgumentException, "Monitor choice has not exactly one choice label");
67 monitorChoiceLabels.push_back(*monitorLabels.begin());
68 }
69
70 uint32_t nextObservation = 0;
71 std::map<std::pair<uint32_t, bool>, uint32_t> observationMap;
72 std::vector<uint32_t> observations;
73
74 std::map<std::pair<const std::string, uint32_t>, storage::BitVector> rowActionObservationMap;
75 std::vector<std::set<std::string>> observationUsedActions;
76
78 std::size_t currentRow = 0;
79 state_type nextStateId = 0;
80
81 state_type goalIndex = nextStateId++;
82 builder.newRowGroup(currentRow);
83 rowActionObservationMap[std::make_pair("end", nextObservation)].grow(currentRow + 1);
84 rowActionObservationMap[std::make_pair("end", nextObservation)].set(currentRow);
85 observationUsedActions.push_back({"end"});
86 builder.addDiagonalEntry(currentRow++, utility::one<ValueType>());
87 observations.push_back(nextObservation++);
88
89 state_type stopIndex = nextStateId++;
90 builder.newRowGroup(currentRow);
91 rowActionObservationMap[std::make_pair("end", nextObservation)].grow(currentRow + 1);
92 rowActionObservationMap[std::make_pair("end", nextObservation)].set(currentRow);
93 observationUsedActions.push_back({"end"});
94 builder.addDiagonalEntry(currentRow++, utility::one<ValueType>());
95 observations.push_back(nextObservation++);
96
97 std::map<product_state_type, state_type> prodToIndexMap;
98 std::vector<state_type> rejectToStates;
99
100 state_type rejectionIndex;
101 if (!options.useRestartSemantics) {
102 // Add sink state where all invalid transitions go
103 rejectionIndex = nextStateId++;
104 builder.newRowGroup(currentRow);
105 rowActionObservationMap[std::make_pair("end", nextObservation)].grow(currentRow + 1);
106 rowActionObservationMap[std::make_pair("end", nextObservation)].set(currentRow);
107 observationUsedActions.push_back({"end"});
108 builder.addDiagonalEntry(currentRow++, utility::one<ValueType>());
109 observations.push_back(nextObservation++);
110 rejectToStates.push_back(rejectionIndex);
111 }
112
113 std::vector<state_type> initialStates;
114
115 std::deque<product_state_type> todo;
116 for (state_type mc_s_0 : mc.getInitialStates()) {
117 for (state_type mon_s_0 : monitor.getInitialStates()) {
118 product_state_type prod_s(mc_s_0, mon_s_0);
119 state_type index = nextStateId++;
120 prodToIndexMap[prod_s] = index;
121 initialStates.push_back(index);
122 if (options.useRestartSemantics)
123 rejectToStates.push_back(index);
124 todo.push_back(prod_s);
125 }
126 }
127
128 while (!todo.empty()) {
129 auto const [mc_from, mon_from] = std::move(todo.front());
130 todo.pop_front();
131
132 // Set observations for from
133 bool accepting = monitor.getStateLabeling().getStateHasLabel(options.acceptingLabel, mon_from);
134 uint32_t step;
135 for (auto& label : monitor.getStateLabeling().getLabelsOfState(mon_from)) {
136 if (label.starts_with(options.stepPrefix)) {
137 step = std::stoi(label.substr(options.stepPrefix.length()));
138 }
139 }
140 std::pair obsPair(step, accepting);
141 if (!observationMap.contains(obsPair)) {
142 observationMap[obsPair] = nextObservation++;
143 observationUsedActions.push_back(std::set<std::string>());
144 }
145 uint32_t currentObservation = observationMap.at(obsPair);
146 observations.push_back(currentObservation);
147
148 // Set transitions for from and add new states to todo
149 builder.newRowGroup(currentRow);
150 if (monitor.getStateLabeling().getLabelsOfState(mon_from).contains(options.horizonLabel)) {
151 const auto& action = *actions.begin();
152 for (state_type initState : rejectToStates) {
153 builder.addNextValue(currentRow, initState, storm::utility::one<ValueType>() / rejectToStates.size());
154 }
155 rowActionObservationMap[std::make_pair(action, currentObservation)].grow(currentRow + 1);
156 rowActionObservationMap[std::make_pair(action, currentObservation)].set(currentRow);
157 observationUsedActions[currentObservation].emplace(action);
158 currentRow++;
159 } else {
160 std::size_t numMonRows = monitor.getTransitionMatrix().getRowGroupSize(mon_from);
161 std::size_t monGroupStart = monitor.getTransitionMatrix().getRowGroupIndices()[mon_from];
162 std::set<std::string> actionsNotTaken(actions);
163 for (std::size_t i = 0; i < numMonRows; i++) {
164 // Remove labels of monitor choice from the labels we still have to take
165
166 const auto action = monitorChoiceLabels[monGroupStart + i];
167 actionsNotTaken.erase(action);
168
169 const auto& monitorRow = monitor.getTransitionMatrix().getRow(mon_from, i);
170 STORM_LOG_ASSERT(monitorRow.getNumberOfEntries() == 1, "Monitor is not fully deterministic");
171 const auto& monitorEntry = monitorRow.begin();
172
173 const auto& mcRow = mc.getTransitionMatrix().getRow(mc_from);
174
175 // Find total probability of the transitions to a state with label action
176 auto totalProbability = utility::zero<ValueType>();
177 for (const auto& mcEntry : mcRow) {
178 if (mc.getStateLabeling().getStateHasLabel(action, mcEntry.getColumn())) {
179 totalProbability += mcEntry.getValue();
180 }
181 }
182
183 // Add new entries to an unsorted vector containing possible duplicate indexes
184 std::map<state_type, ValueType> newRow;
185
186 // Direct probability not used towards the initial states
187 if (totalProbability < storm::utility::one<ValueType>()) {
188 for (state_type initState : rejectToStates) {
189 if (newRow.contains(initState))
190 newRow[initState] = newRow[initState] + (1 - totalProbability) / rejectToStates.size();
191 else
192 newRow[initState] = (1 - totalProbability) / rejectToStates.size();
193 }
194 }
195
196 // Add transitions to the successors, if the successor has not yet been added, add it to the todo list
197 if (totalProbability > storm::utility::zero<ValueType>()) {
198 for (const auto& mcEntry : mcRow) {
199 if (mc.getStateLabeling().getStateHasLabel(action, mcEntry.getColumn())) {
200 const product_state_type to_pair(mcEntry.getColumn(), monitorEntry->getColumn());
201 state_type indexTo;
202 if (auto it = prodToIndexMap.find(to_pair); it != prodToIndexMap.end()) {
203 indexTo = it->second;
204 } else {
205 indexTo = nextStateId++;
206 todo.push_back(to_pair);
207 prodToIndexMap[to_pair] = indexTo;
208 }
209 if (newRow.contains(indexTo))
210 newRow[indexTo] = newRow[indexTo] + mcEntry.getValue();
211 else
212 newRow[indexTo] = mcEntry.getValue();
213 }
214 }
215
216 // Set action to used for this observation
217 observationUsedActions[currentObservation].emplace(action);
218 }
219
220 // Insert new entries
221 for (const auto& entry : newRow) {
222 builder.addNextValue(currentRow, entry.first, entry.second);
223 }
224 auto& rowBitVec = rowActionObservationMap[std::make_pair(action, currentObservation)];
225 rowBitVec.grow(currentRow + 1);
226 rowBitVec.set(currentRow);
227 currentRow++;
228 }
229
230 for (const auto& action : actionsNotTaken) {
231 for (state_type initState : rejectToStates) {
232 builder.addNextValue(currentRow, initState, storm::utility::one<ValueType>() / rejectToStates.size());
233 }
234 auto& rowBitVec = rowActionObservationMap[std::make_pair(action, currentObservation)];
235 rowBitVec.grow(currentRow + 1);
236 rowBitVec.set(currentRow);
237 currentRow++;
238 }
239 }
240
241 if (monitor.getStateLabeling().getStateHasLabel(options.acceptingLabel, mon_from)) {
242 STORM_LOG_THROW(risk[mc_from] >= -utility::convertNumber<ValueType>(1e-12) && risk[mc_from] <= utility::convertNumber<ValueType>(1.0 + 1e-12),
243 exceptions::IllegalArgumentException, "Risk for state " + std::to_string(mc_from) + " is not in [0, 1]");
244 if (utility::isAlmostZero(risk[mc_from])) {
245 builder.addNextValue(currentRow, stopIndex, utility::one<ValueType>());
246 } else if (utility::isAlmostOne(risk[mc_from])) {
247 builder.addNextValue(currentRow, goalIndex, utility::one<ValueType>());
248 } else {
249 builder.addNextValue(currentRow, goalIndex, risk[mc_from]);
250 builder.addNextValue(currentRow, stopIndex, utility::one<ValueType>() - risk[mc_from]);
251 }
252 observationUsedActions[currentObservation].emplace("end");
253 auto& rowBitVec = rowActionObservationMap[std::make_pair("end", currentObservation)];
254 rowBitVec.grow(currentRow + 1);
255 rowBitVec.set(currentRow);
256 currentRow++;
257 }
258 }
259
260 size_t numberOfRows = currentRow;
261
262 // Make all observation action bitvectors of size numberOfRows
263 for (auto& [labelObsPair, vec] : rowActionObservationMap) {
264 vec.resize(numberOfRows);
265 }
266
267 // Calculate which rows belong to action which don't all return for an observation and only keep these
269 storm::storage::BitVector rowsToKeep(transMatrix.getRowCount());
270 std::map<uint32_t, std::string> observationDefaultAction;
271 u_int32_t currentObservation = 0;
272 for (auto const& actionsInObs : observationUsedActions) {
273 if (actionsInObs.size() == 1) {
274 observationDefaultAction[currentObservation] = *actionsInObs.begin();
275 }
276
277 for (auto const& action : actionsInObs) {
278 // std::cout << "Keeping action obs (" << action << ", " << currentObservation << ")" << std::endl;
279 rowsToKeep |= rowActionObservationMap[std::make_pair(action, currentObservation)];
280 }
281 currentObservation++;
282 }
283 // std::cout << "Kept " << rowsToKeep.getNumberOfSetBits() << " out of " << numberOfRows << " rows." << std::endl;
284 // rowsToKeep.setMultiple(0, numberOfRows);
285 numberOfRows = rowsToKeep.getNumberOfSetBits();
286 storm::storage::SparseMatrix<ValueType> reducedTransitionMatrix = transMatrix.restrictRows(rowsToKeep);
287
288 // Create state labeling
289 const state_type numberOfStates = nextStateId;
290 storm::models::sparse::StateLabeling stateLabeling(numberOfStates);
291 stateLabeling.addLabel("init", storm::storage::BitVector(numberOfStates, initialStates.begin(), initialStates.end()));
292
293 stateLabeling.addLabel("goal", storm::storage::BitVector(numberOfStates));
294 stateLabeling.addLabelToState("goal", goalIndex);
295
296 stateLabeling.addLabel("stop", storm::storage::BitVector(numberOfStates));
297 stateLabeling.addLabelToState("stop", stopIndex);
298
299 stateLabeling.addLabel("condition", storm::storage::BitVector(numberOfStates));
300 stateLabeling.addLabelToState("condition", goalIndex);
301 stateLabeling.addLabelToState("condition", stopIndex);
302
303 if (!options.useRestartSemantics) {
304 stateLabeling.addLabel("sink", storm::storage::BitVector(numberOfStates));
305 stateLabeling.addLabelToState("sink", rejectionIndex);
306 }
307
308 storm::storage::sparse::ModelComponents<ValueType> components(reducedTransitionMatrix, std::move(stateLabeling));
309 components.observabilityClasses = std::move(observations);
310
311 // Add choice labeling
312 const std::vector<uint64_t> rowMapping = rowsToKeep.getNumberOfSetBitsBeforeIndices(); // Vector which maps old row id to new row id
313 storm::models::sparse::ChoiceLabeling choiceLabeling(numberOfRows);
314 for (const auto& [labelObsPair, bitvec] : rowActionObservationMap) {
315 // Rebuild bitvec with restricted rows
316 storm::storage::BitVector newBitVec(numberOfRows);
317 for (const auto& setbit : bitvec) {
318 if (rowsToKeep[setbit])
319 newBitVec.set(rowMapping[setbit]);
320 }
321 // auto newBitVec = bitvec;
322
323 if (choiceLabeling.containsLabel(labelObsPair.first)) {
324 choiceLabeling.setChoices(labelObsPair.first, newBitVec | choiceLabeling.getChoices(labelObsPair.first));
325 } else {
326 choiceLabeling.addLabel(labelObsPair.first, newBitVec);
327 }
328 }
329
330 components.choiceLabeling = std::move(choiceLabeling);
331
332 if (mc.hasStateValuations()) {
333 // Add state valuations
335 svBuilder.addVariable(monvar);
336 svBuilder.addVariable(mcvar);
337 std::set<expressions::Variable> variables;
338 for (uint64_t i = 0; i < mc.getNumberOfStates(); i++) {
339 const auto& valAssignment = mc.getStateValuations().at(i);
340 for (auto val = valAssignment.begin(); val != valAssignment.end(); ++val) {
341 if (val.isVariableAssignment() && !variables.contains(val.getVariable())) {
342 variables.emplace(val.getVariable());
343 svBuilder.addVariable(val.getVariable());
344 }
345 }
346 }
347
348 for (uint64_t i = 0; i < mc.getNumberOfStates(); i++) {
349 for (uint64_t j = 0; j < monitor.getNumberOfStates(); j++) {
350 product_state_type s(i, j);
351 if (!prodToIndexMap.contains(s))
352 continue;
353
354 std::vector<bool> booleanValues;
355 std::vector<int64_t> integerValues;
356 std::vector<storm::RationalNumber> rationalValues;
357
358 integerValues.push_back(j); // Set monvar
359 integerValues.push_back(i); // Set mcvar
360
361 const auto& valAssignment = mc.getStateValuations().at(i);
362
363 for (auto& var : variables) {
364 for (auto val = valAssignment.begin(); val != valAssignment.end(); ++val) {
365 if (var == val.getVariable()) {
366 if (val.isBoolean()) {
367 booleanValues.push_back(val.getBooleanValue());
368 } else if (val.isInteger()) {
369 integerValues.push_back(val.getIntegerValue());
370 } else if (val.isRational()) {
371 rationalValues.push_back(val.getRationalValue());
372 }
373 break;
374 }
375 }
376 }
377 svBuilder.addState(prodToIndexMap[std::make_pair(i, j)], std::move(booleanValues), std::move(integerValues), std::move(rationalValues));
378 }
379 }
380
381 std::vector<bool> goalBooleanValues;
382 std::vector<int64_t> goalIntegerValues(2, -1);
383 std::vector<storm::RationalNumber> goalRationalValues;
384 for (auto& var : variables) {
385 if (var.hasBooleanType()) {
386 goalBooleanValues.push_back(false);
387 } else if (var.hasIntegerType()) {
388 goalIntegerValues.push_back(-1);
389 } else if (var.hasRationalType()) {
390 goalRationalValues.emplace_back(-1);
391 }
392 }
393 svBuilder.addState(goalIndex, std::move(goalBooleanValues), std::move(goalIntegerValues), std::move(goalRationalValues));
394
395 std::vector<bool> stopBooleanValues;
396 std::vector<int64_t> stopIntegerValues(2, -1);
397 std::vector<storm::RationalNumber> stopRationalValues;
398 for (auto& var : variables) {
399 if (var.hasBooleanType()) {
400 stopBooleanValues.push_back(false);
401 } else if (var.hasIntegerType()) {
402 stopIntegerValues.push_back(-1);
403 } else if (var.hasRationalType()) {
404 stopRationalValues.emplace_back(-1);
405 }
406 }
407 svBuilder.addState(stopIndex, std::move(stopBooleanValues), std::move(stopIntegerValues), std::move(stopRationalValues));
408
409 components.stateValuations = svBuilder.build();
410 }
411
412 // Store model
413 storm::models::sparse::Pomdp<ValueType> product(std::move(components));
414 auto mv = std::make_shared<MonitorVerifier<ValueType>>(std::move(product), std::move(observationMap), std::move(observationDefaultAction));
415 return mv;
416}
417
418template<typename ValueType>
419void GenerateMonitorVerifier<ValueType>::setRisk(std::vector<ValueType> const& risk) {
420 this->risk = risk;
421}
422
423template class MonitorVerifier<double>;
427
428} // namespace generator
429} // namespace storm
std::shared_ptr< MonitorVerifier< ValueType > > createProduct()
void setRisk(std::vector< ValueType > const &risk)
GenerateMonitorVerifier(storm::models::sparse::Dtmc< ValueType > const &mc, storm::models::sparse::Mdp< ValueType > const &monitor, std::shared_ptr< storm::expressions::ExpressionManager > &exprManager, Options const &options)
MonitorVerifier(const storm::models::sparse::Pomdp< ValueType > &product, const std::map< std::pair< uint32_t, bool >, uint32_t > &observationMap, std::map< uint32_t, std::string > observationDefaultAction)
std::map< std::pair< uint32_t, bool >, uint32_t > const & getObservationMap()
const storm::models::sparse::Pomdp< ValueType > & getProduct()
const std::map< uint32_t, std::string > & getObservationDefaultAction()
This class manages the labeling of the choice space with a number of (atomic) labels.
storm::storage::BitVector const & getChoices(std::string const &label) const
Returns the labeling of choices associated with the given label.
void setChoices(std::string const &label, storage::BitVector const &labeling)
Sets the labeling of choices associated with the given label.
This class represents a discrete-time Markov chain.
Definition Dtmc.h:13
void addLabel(std::string const &label)
Adds a new label to the labelings.
bool containsLabel(std::string const &label) const
Checks whether a label is registered within this labeling.
This class represents a (discrete-time) Markov decision process.
Definition Mdp.h:13
This class represents a partially observable Markov decision process.
Definition Pomdp.h:13
This class manages the labeling of the state space with a number of (atomic) labels.
void addLabelToState(std::string const &label, storm::storage::sparse::state_type state)
Adds a label to a given state.
A bit vector that is internally represented as a vector of 64-bit values.
Definition BitVector.h:16
std::vector< uint64_t > getNumberOfSetBitsBeforeIndices() const
Retrieves a vector that holds at position i the number of bits set before index i.
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.
A class that can be used to build a sparse matrix by adding value by value.
A class that holds a possibly non-square matrix in the compressed row storage format.
SparseMatrix restrictRows(storm::storage::BitVector const &rowsToKeep, bool allowEmptyRowGroups=false) const
Restrict rows in grouped rows matrix.
index_type getRowCount() const
Returns the number of rows of the matrix.
SparseMatrixIndexType index_type
void addState(storm::storage::sparse::state_type const &state, std::vector< bool > &&booleanValues={}, std::vector< int64_t > &&integerValues={})
Adds a new state.
StateValuations build()
Creates the finalized state valuations object.
void addVariable(storm::expressions::Variable const &variable)
Adds a new variable to keep track of for the state valuations.
#define STORM_LOG_ASSERT(cond, message)
Definition macros.h:11
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:30
bool isAlmostZero(ValueType const &a)
Definition constants.cpp:93
bool isAlmostOne(ValueType const &a)
Definition constants.cpp:98
ValueType zero()
Definition constants.cpp:24
ValueType one()
Definition constants.cpp:19
TargetType convertNumber(SourceType const &number)
std::optional< storm::storage::sparse::StateValuations > stateValuations
std::optional< storm::models::sparse::ChoiceLabeling > choiceLabeling
std::optional< std::vector< uint32_t > > observabilityClasses