Storm 1.14.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>
23
24namespace storm {
25namespace generator {
26
27template<typename ValueType>
29 const std::map<std::pair<uint32_t, bool>, uint32_t>& observationMap,
30 std::map<uint32_t, std::string> observationDefaultAction)
31 : product(storm::models::sparse::Pomdp<ValueType>(product)), observationMap(observationMap), observationDefaultAction(observationDefaultAction) {}
32
33template<typename ValueType>
34const std::map<std::pair<uint32_t, bool>, uint32_t>& MonitorVerifier<ValueType>::getObservationMap() {
35 return observationMap;
36}
37
38template<typename ValueType>
42
43template<typename ValueType>
44const std::map<uint32_t, std::string>& MonitorVerifier<ValueType>::getObservationDefaultAction() {
45 return observationDefaultAction;
46}
47
48template<typename ValueType>
50 std::shared_ptr<storm::expressions::ExpressionManager>& exprManager, Options const& options)
51 : mc(mc), monitor(monitor), risk(), exprManager(exprManager), options(options) {
52 monvar = exprManager->declareFreshIntegerVariable(false, "_mon");
53 mcvar = exprManager->declareFreshIntegerVariable(false, "_mc");
54}
55
56template<typename ValueType>
57std::shared_ptr<MonitorVerifier<ValueType>> GenerateMonitorVerifier<ValueType>::createProduct() {
58 typedef storm::storage::sparse::state_type state_type;
59 typedef std::pair<state_type, state_type> product_state_type;
60
61 STORM_LOG_THROW(monitor.hasChoiceLabeling(), storm::exceptions::InvalidArgumentException, "The monitor should contain choice labeling.");
62
63 const std::set<std::string>& actions = monitor.getChoiceLabeling().getLabels();
64
65 // Build choice label map of monitor choices
66 std::vector<std::string> monitorChoiceLabels;
67 for (typename storm::storage::SparseMatrix<ValueType>::index_type i = 0; i < monitor.getTransitionMatrix().getRowCount(); i++) {
68 auto const& monitorLabels = monitor.getChoiceLabeling().getLabelsOfChoice(i);
69 STORM_LOG_THROW(monitorLabels.size() == 1, storm::exceptions::InvalidArgumentException, "Monitor choice has not exactly one choice label.");
70 monitorChoiceLabels.push_back(*monitorLabels.begin());
71 }
72
73 uint32_t nextObservation = 0;
74 std::map<std::pair<uint32_t, bool>, uint32_t> observationMap;
75 std::vector<uint32_t> observations;
76
77 std::map<std::pair<const std::string, uint32_t>, storage::BitVector> rowActionObservationMap;
78 std::vector<std::set<std::string>> observationUsedActions;
79
81 std::size_t currentRow = 0;
82 state_type nextStateId = 0;
83
84 state_type goalIndex = nextStateId++;
85 builder.newRowGroup(currentRow);
86 rowActionObservationMap[std::make_pair("end", nextObservation)].grow(currentRow + 1);
87 rowActionObservationMap[std::make_pair("end", nextObservation)].set(currentRow);
88 observationUsedActions.push_back({"end"});
89 builder.addDiagonalEntry(currentRow++, utility::one<ValueType>());
90 observations.push_back(nextObservation++);
91
92 state_type stopIndex = nextStateId++;
93 builder.newRowGroup(currentRow);
94 rowActionObservationMap[std::make_pair("end", nextObservation)].grow(currentRow + 1);
95 rowActionObservationMap[std::make_pair("end", nextObservation)].set(currentRow);
96 observationUsedActions.push_back({"end"});
97 builder.addDiagonalEntry(currentRow++, utility::one<ValueType>());
98 observations.push_back(nextObservation++);
99
100 std::map<product_state_type, state_type> prodToIndexMap;
101 std::vector<state_type> rejectToStates;
102
103 state_type rejectionIndex;
104 if (!options.useRestartSemantics) {
105 // Add sink state where all invalid transitions go
106 rejectionIndex = nextStateId++;
107 builder.newRowGroup(currentRow);
108 rowActionObservationMap[std::make_pair("end", nextObservation)].grow(currentRow + 1);
109 rowActionObservationMap[std::make_pair("end", nextObservation)].set(currentRow);
110 observationUsedActions.push_back({"end"});
111 builder.addDiagonalEntry(currentRow++, utility::one<ValueType>());
112 observations.push_back(nextObservation++);
113 rejectToStates.push_back(rejectionIndex);
114 }
115
116 std::vector<state_type> initialStates;
117
118 std::deque<product_state_type> todo;
119 for (state_type mc_s_0 : mc.getInitialStates()) {
120 for (state_type mon_s_0 : monitor.getInitialStates()) {
121 product_state_type prod_s(mc_s_0, mon_s_0);
122 state_type index = nextStateId++;
123 prodToIndexMap[prod_s] = index;
124 initialStates.push_back(index);
125 if (options.useRestartSemantics) {
126 rejectToStates.push_back(index);
127 }
128 todo.push_back(prod_s);
129 }
130 }
131
132 while (!todo.empty()) {
133 auto const [mc_from, mon_from] = std::move(todo.front());
134 todo.pop_front();
135
136 // Set observations for from
137 bool accepting = monitor.getStateLabeling().getStateHasLabel(options.acceptingLabel, mon_from);
138 uint32_t step;
139 for (auto& label : monitor.getStateLabeling().getLabelsOfState(mon_from)) {
140 if (label.starts_with(options.stepPrefix)) {
141 step = std::stoi(label.substr(options.stepPrefix.length()));
142 }
143 }
144 std::pair obsPair(step, accepting);
145 if (!observationMap.contains(obsPair)) {
146 observationMap[obsPair] = nextObservation++;
147 observationUsedActions.push_back(std::set<std::string>());
148 }
149 uint32_t currentObservation = observationMap.at(obsPair);
150 observations.push_back(currentObservation);
151
152 // Set transitions for from and add new states to todo
153 builder.newRowGroup(currentRow);
154 if (monitor.getStateLabeling().getLabelsOfState(mon_from).contains(options.horizonLabel)) {
155 const auto& action = *actions.begin();
156 for (state_type initState : rejectToStates) {
157 builder.addNextValue(currentRow, initState, storm::utility::one<ValueType>() / rejectToStates.size());
158 }
159 rowActionObservationMap[std::make_pair(action, currentObservation)].grow(currentRow + 1);
160 rowActionObservationMap[std::make_pair(action, currentObservation)].set(currentRow);
161 observationUsedActions[currentObservation].emplace(action);
162 currentRow++;
163 } else {
164 std::size_t numMonRows = monitor.getTransitionMatrix().getRowGroupSize(mon_from);
165 std::size_t monGroupStart = monitor.getTransitionMatrix().getRowGroupIndices()[mon_from];
166 std::set<std::string> actionsNotTaken(actions);
167 for (std::size_t i = 0; i < numMonRows; i++) {
168 // Remove labels of monitor choice from the labels we still have to take
169
170 const auto action = monitorChoiceLabels[monGroupStart + i];
171 actionsNotTaken.erase(action);
172
173 const auto& monitorRow = monitor.getTransitionMatrix().getRow(mon_from, i);
174 STORM_LOG_ASSERT(monitorRow.getNumberOfEntries() == 1, "Monitor is not fully deterministic.");
175 const auto& monitorEntry = monitorRow.begin();
176
177 const auto& mcRow = mc.getTransitionMatrix().getRow(mc_from);
178
179 // Find total probability of the transitions to a state with label action
180 auto totalProbability = utility::zero<ValueType>();
181 for (const auto& mcEntry : mcRow) {
182 if (mc.getStateLabeling().getStateHasLabel(action, mcEntry.getColumn())) {
183 totalProbability += mcEntry.getValue();
184 }
185 }
186
187 // Add new entries to an unsorted vector containing possible duplicate indexes
188 std::map<state_type, ValueType> newRow;
189
190 // Direct probability not used towards the initial states
191 if (totalProbability < storm::utility::one<ValueType>()) {
192 for (state_type initState : rejectToStates) {
193 if (newRow.contains(initState)) {
194 newRow[initState] = newRow[initState] + (1 - totalProbability) / rejectToStates.size();
195 } else {
196 newRow[initState] = (1 - totalProbability) / rejectToStates.size();
197 }
198 }
199 }
200
201 // Add transitions to the successors, if the successor has not yet been added, add it to the todo list
202 if (totalProbability > storm::utility::zero<ValueType>()) {
203 for (const auto& mcEntry : mcRow) {
204 if (mc.getStateLabeling().getStateHasLabel(action, mcEntry.getColumn())) {
205 const product_state_type to_pair(mcEntry.getColumn(), monitorEntry->getColumn());
206 state_type indexTo;
207 if (auto it = prodToIndexMap.find(to_pair); it != prodToIndexMap.end()) {
208 indexTo = it->second;
209 } else {
210 indexTo = nextStateId++;
211 todo.push_back(to_pair);
212 prodToIndexMap[to_pair] = indexTo;
213 }
214 if (newRow.contains(indexTo)) {
215 newRow[indexTo] = newRow[indexTo] + mcEntry.getValue();
216 } else {
217 newRow[indexTo] = mcEntry.getValue();
218 }
219 }
220 }
221
222 // Set action to used for this observation
223 observationUsedActions[currentObservation].emplace(action);
224 }
225
226 // Insert new entries
227 for (const auto& entry : newRow) {
228 builder.addNextValue(currentRow, entry.first, entry.second);
229 }
230 auto& rowBitVec = rowActionObservationMap[std::make_pair(action, currentObservation)];
231 rowBitVec.grow(currentRow + 1);
232 rowBitVec.set(currentRow);
233 currentRow++;
234 }
235
236 for (const auto& action : actionsNotTaken) {
237 for (state_type initState : rejectToStates) {
238 builder.addNextValue(currentRow, initState, storm::utility::one<ValueType>() / rejectToStates.size());
239 }
240 auto& rowBitVec = rowActionObservationMap[std::make_pair(action, currentObservation)];
241 rowBitVec.grow(currentRow + 1);
242 rowBitVec.set(currentRow);
243 currentRow++;
244 }
245 }
246
247 if (monitor.getStateLabeling().getStateHasLabel(options.acceptingLabel, mon_from)) {
248 STORM_LOG_THROW(risk[mc_from] >= -utility::convertNumber<ValueType>(1e-12) && risk[mc_from] <= utility::convertNumber<ValueType>(1.0 + 1e-12),
249 exceptions::IllegalArgumentException, "Risk for state " + std::to_string(mc_from) + " is not in [0, 1].");
250 if (utility::isAlmostZero(risk[mc_from])) {
251 builder.addNextValue(currentRow, stopIndex, utility::one<ValueType>());
252 } else if (utility::isAlmostOne(risk[mc_from])) {
253 builder.addNextValue(currentRow, goalIndex, utility::one<ValueType>());
254 } else {
255 builder.addNextValue(currentRow, goalIndex, risk[mc_from]);
256 builder.addNextValue(currentRow, stopIndex, utility::one<ValueType>() - risk[mc_from]);
257 }
258 observationUsedActions[currentObservation].emplace("end");
259 auto& rowBitVec = rowActionObservationMap[std::make_pair("end", currentObservation)];
260 rowBitVec.grow(currentRow + 1);
261 rowBitVec.set(currentRow);
262 currentRow++;
263 }
264 }
265
266 size_t numberOfRows = currentRow;
267
268 // Make all observation action bitvectors of size numberOfRows
269 for (auto& [labelObsPair, vec] : rowActionObservationMap) {
270 vec.resize(numberOfRows);
271 }
272
273 // Calculate which rows belong to action which don't all return for an observation and only keep these
275 storm::storage::BitVector rowsToKeep(transMatrix.getRowCount());
276 std::map<uint32_t, std::string> observationDefaultAction;
277 u_int32_t currentObservation = 0;
278 for (auto const& actionsInObs : observationUsedActions) {
279 if (actionsInObs.size() == 1) {
280 observationDefaultAction[currentObservation] = *actionsInObs.begin();
281 }
282
283 for (auto const& action : actionsInObs) {
284 rowsToKeep |= rowActionObservationMap[std::make_pair(action, currentObservation)];
285 }
286 currentObservation++;
287 }
288 // rowsToKeep.setMultiple(0, numberOfRows);
289 numberOfRows = rowsToKeep.getNumberOfSetBits();
290 storm::storage::SparseMatrix<ValueType> reducedTransitionMatrix = transMatrix.restrictRows(rowsToKeep);
291
292 // Create state labeling
293 const state_type numberOfStates = nextStateId;
294 storm::models::sparse::StateLabeling stateLabeling(numberOfStates);
295 stateLabeling.addLabel("init", storm::storage::BitVector(numberOfStates, initialStates.begin(), initialStates.end()));
296
297 stateLabeling.addLabel("goal", storm::storage::BitVector(numberOfStates));
298 stateLabeling.addLabelToState("goal", goalIndex);
299
300 stateLabeling.addLabel("stop", storm::storage::BitVector(numberOfStates));
301 stateLabeling.addLabelToState("stop", stopIndex);
302
303 stateLabeling.addLabel("condition", storm::storage::BitVector(numberOfStates));
304 stateLabeling.addLabelToState("condition", goalIndex);
305 stateLabeling.addLabelToState("condition", stopIndex);
306
307 if (!options.useRestartSemantics) {
308 stateLabeling.addLabel("sink", storm::storage::BitVector(numberOfStates));
309 stateLabeling.addLabelToState("sink", rejectionIndex);
310 }
311
312 storm::storage::sparse::ModelComponents<ValueType> components(reducedTransitionMatrix, std::move(stateLabeling));
313 components.observabilityClasses = std::move(observations);
314
315 // Add choice labeling
316 const std::vector<uint64_t> rowMapping = rowsToKeep.getNumberOfSetBitsBeforeIndices(); // Vector which maps old row id to new row id
317 storm::models::sparse::ChoiceLabeling choiceLabeling(numberOfRows);
318 for (const auto& [labelObsPair, bitvec] : rowActionObservationMap) {
319 // Rebuild bitvec with restricted rows
320 storm::storage::BitVector newBitVec(numberOfRows);
321 for (uint64_t setbit : bitvec) {
322 if (rowsToKeep[setbit]) {
323 newBitVec.set(rowMapping[setbit]);
324 }
325 }
326 // auto newBitVec = bitvec;
327
328 if (choiceLabeling.containsLabel(labelObsPair.first)) {
329 choiceLabeling.setChoices(labelObsPair.first, newBitVec | choiceLabeling.getChoices(labelObsPair.first));
330 } else {
331 choiceLabeling.addLabel(labelObsPair.first, newBitVec);
332 }
333 }
334
335 components.choiceLabeling = std::move(choiceLabeling);
336
337 if (mc.hasStateValuations()) {
338 // Add state valuations
339 auto const& oldValuations = mc.getStateValuations().getStorage();
340 storm::storage::sparse::ValuationsStorage stateValuations = [this, &oldValuations]() {
342 svBuilder.addIntegerVariable(monvar, -1, monitor.getNumberOfStates() - 1);
343 svBuilder.addIntegerVariable(mcvar, -1, mc.getNumberOfStates() - 1);
344 STORM_LOG_ASSERT(oldValuations.numClasses() == 1, "Only one class of valuations supported.");
345 svBuilder.addVariables(oldValuations.getClassDescription());
347 }();
348 stateValuations.resize(numberOfStates);
349
350 for (uint64_t i = 0; i < mc.getNumberOfStates(); i++) {
351 for (uint64_t j = 0; j < monitor.getNumberOfStates(); j++) {
352 product_state_type const s(i, j);
353 if (!prodToIndexMap.contains(s)) {
354 continue;
355 }
356 auto const productStateIndex = prodToIndexMap[s];
357 // Set the variable values for the product state.
358 // We copy the valuations from the original model and set the monvar and mcvar to the corresponding state indices.
359 stateValuations.writeCallback(productStateIndex, [this, &oldValuations, i, j](auto, auto const& var, auto& value) {
360 using VT = std::remove_cvref_t<decltype(value)>;
361 if (var == monvar || var == mcvar) {
362 if constexpr (std::is_same_v<VT, int64_t>) {
363 value = var == monvar ? j : i;
364 } else {
365 STORM_LOG_ASSERT(false, "Unexpected type.");
366 }
367 } else {
368 // This is a variable of the original model. Copy old valuation value.
369 value = oldValuations.template readValue<VT>(i, var);
370 }
371 });
372 }
373 }
374
375 stateValuations.writeValue<int64_t>(goalIndex, monvar, -1);
376 stateValuations.writeValue<int64_t>(goalIndex, mcvar, -1);
377 stateValuations.writeValue<int64_t>(stopIndex, monvar, -1);
378 stateValuations.writeValue<int64_t>(stopIndex, mcvar, -1);
379 components.stateValuations.emplace(std::move(stateValuations));
380 }
381
382 // Store model
383 storm::models::sparse::Pomdp<ValueType> product(std::move(components));
384 auto mv = std::make_shared<MonitorVerifier<ValueType>>(std::move(product), std::move(observationMap), std::move(observationDefaultAction));
385 return mv;
386}
387
388template<typename ValueType>
389void GenerateMonitorVerifier<ValueType>::setRisk(std::vector<ValueType> const& risk) {
390 this->risk = risk;
391}
392
393template class MonitorVerifier<double>;
397
398} // namespace generator
399} // 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
Helper to incrementally build a ValuationClassDescription, i.e.
ValuationClassDescription buildClassDescription()
Creates the finalized state valuations object.
void addIntegerVariable(storm::expressions::Variable const &variable, int64_t const lowerBound, int64_t const upperBound, bool optional=false)
Adds a new integer variable to the builder.
void addVariables(ValuationClassDescription const &description, bool addPadding=false)
Adds all variables from the given description.
Stores valuations of variables for a set of entities (e.g.
void resize(uint64_t newEntityCount, uint64_t classIndex=0)
Resizes the entity count to newEntityCount.
void writeValue(uint64_t entity, storm::expressions::Variable const &variable, ValueType const &value)
Directly writes value to the given variable of entity.
void writeCallback(uint64_t entity, Callback const &callback)
Writes all variables of the given entity by invoking callback for each one.
#define STORM_LOG_ASSERT(cond, message)
Definition macros.h:9
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:28
bool isAlmostZero(ValueType const &a)
Definition constants.cpp:96
bool isAlmostOne(ValueType const &a)
ValueType zero()
Definition constants.cpp:24
ValueType one()
Definition constants.cpp:19
TargetType convertNumber(SourceType const &number)
std::optional< storm::models::sparse::ChoiceLabeling > choiceLabeling
std::optional< storm::storage::sparse::Valuations > stateValuations
std::optional< std::vector< uint32_t > > observabilityClasses