Storm 1.14.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
ObservationTraceUnfolder.cpp
Go to the documentation of this file.
2#include <algorithm>
3
13
14#undef _VERBOSE_OBSERVATION_UNFOLDING
15
16namespace storm {
17namespace pomdp {
18template<typename ValueType>
20 std::shared_ptr<storm::expressions::ExpressionManager> exprManager,
22 : model(model), risk(risk), exprManager(std::move(exprManager)), options(options) {
23 svvar = this->exprManager->declareFreshIntegerVariable(false, "_s");
24 tsvar = this->exprManager->declareFreshIntegerVariable(false, "_t");
25}
26
27template<typename ValueType>
28std::shared_ptr<storm::models::sparse::Mdp<ValueType>> ObservationTraceUnfolder<ValueType>::transform(const std::vector<uint32_t>& observations) {
29 std::vector<uint32_t> modifiedObservations = observations;
30 // First observation should be special.
31 // This just makes the algorithm simpler because we do not treat the first step as a special case later.
32 // We overwrite the observation with a non-existing obs z*
33 modifiedObservations[0] = model.getNrObservations();
34
35 storm::storage::BitVector initialStates = model.getInitialStates();
36 storm::storage::BitVector actualInitialStates = initialStates;
37 for (uint64_t state : initialStates) {
38 if (model.getObservation(state) != observations[0]) {
39 actualInitialStates.set(state, false);
40 }
41 }
42 STORM_LOG_THROW(actualInitialStates.getNumberOfSetBits() == 1, storm::exceptions::InvalidArgumentException,
43 "Must have unique initial state matching the observation.");
44
45#ifdef _VERBOSE_OBSERVATION_UNFOLDING
46 std::cout << "build valution builder..\n";
47#endif
48 // Initialize state valuations
49 storm::storage::sparse::ValuationsStorage stateValuations = [this, &observations]() {
51 svBuilder.addIntegerVariable(svvar, -1, static_cast<int64_t>(model.getNumberOfStates()) - 1);
52 svBuilder.addIntegerVariable(tsvar, -1, observations.size() - 1);
54 }();
55
56 // Shorthand for adding the next state's valuations with input model state index s and trace step t
57 auto addStateValuation = [this, &stateValuations](int64_t s, int64_t t) {
58 stateValuations.emplaceBack<false, int64_t>([this, s, t](auto, auto const& var, auto& value) { value = var == svvar ? s : t; });
59 };
60
61 std::unordered_map<uint64_t, uint64_t> unfoldedToOld;
62 std::unordered_map<uint64_t, uint64_t> unfoldedToOldNextStep;
63 std::unordered_map<uint64_t, uint64_t> oldToUnfolded;
64
65#ifdef _VERBOSE_OBSERVATION_UNFOLDING
66 std::cout << "start buildiing matrix...\n";
67#endif
68
69 uint64_t newStateIndex = 0;
70 uint64_t const violatedState = newStateIndex;
71 if (!options.useRestartSemantics) {
72 // The violated state is only used if we do no use the rejection semantics.
73 ++newStateIndex;
74 }
75 // Add this initial state:
76 uint64_t const initialState = newStateIndex;
77 ++newStateIndex;
78
79 unfoldedToOldNextStep[initialState] = actualInitialStates.getNextSetIndex(0);
80
81 uint64_t const resetDestination = options.useRestartSemantics ? initialState : violatedState; // Should be initial state for the standard semantics.
82 storm::storage::SparseMatrixBuilder<ValueType> transitionMatrixBuilder(0, 0, 0, true, true);
83
84 if (!options.useRestartSemantics) {
85 // the violated state (only used when no rejection sampling) is a sink state
86 transitionMatrixBuilder.newRowGroup(violatedState);
87 transitionMatrixBuilder.addNextValue(violatedState, violatedState, storm::utility::one<ValueType>());
88 addStateValuation(-1, -1);
89 }
90
91 // Now we are starting to build the MDP from the initial state onwards.
92 uint64_t newRowGroupStart = initialState;
93 uint64_t newRowCount = initialState;
94
95 // Notice that we are going to use a special last step
96 for (uint64_t step = 0; step < observations.size() - 1; ++step) {
97 oldToUnfolded.clear();
98 unfoldedToOld = unfoldedToOldNextStep;
99 unfoldedToOldNextStep.clear();
100
101 for (auto const& unfoldedToOldEntry : unfoldedToOld) {
102 transitionMatrixBuilder.newRowGroup(newRowGroupStart);
103#ifdef _VERBOSE_OBSERVATION_UNFOLDING
104 std::cout << "\tconsider new state " << unfoldedToOldEntry.first << '\n';
105#endif
106 STORM_LOG_ASSERT(step == 0 || newRowCount == transitionMatrixBuilder.getLastRow() + 1,
107 "Step " << step << " newRowCount " << newRowCount << " lastRow " << transitionMatrixBuilder.getLastRow());
108 addStateValuation(unfoldedToOldEntry.second, step);
109 uint64_t oldRowIndexStart = model.getNondeterministicChoiceIndices()[unfoldedToOldEntry.second];
110 uint64_t oldRowIndexEnd = model.getNondeterministicChoiceIndices()[unfoldedToOldEntry.second + 1];
111
112 for (uint64_t oldRowIndex = oldRowIndexStart; oldRowIndex != oldRowIndexEnd; oldRowIndex++) {
113#ifdef _VERBOSE_OBSERVATION_UNFOLDING
114 std::cout << "\t\tconsider old action " << oldRowIndex << '\n';
115 std::cout << "\t\tconsider new row nr " << newRowCount << '\n';
116#endif
117
118 ValueType resetProb = storm::utility::zero<ValueType>();
119 // We first find the reset probability
120 for (auto const& oldRowEntry : model.getTransitionMatrix().getRow(oldRowIndex)) {
121 if (model.getObservation(oldRowEntry.getColumn()) != observations[step + 1]) {
122 resetProb += oldRowEntry.getValue();
123 if constexpr (std::is_same_v<ValueType, storm::Interval>) {
124 resetProb.setUpper(std::min(resetProb.upper(), 1.0));
125 resetProb.setLower(std::max(resetProb.lower(), 0.0));
126 } else if constexpr (std::is_same_v<ValueType, storm::RationalInterval>) {
127 resetProb.setUpper(std::min(resetProb.upper(), utility::one<storm::RationalNumber>()));
128 resetProb.setLower(std::max(resetProb.lower(), utility::zero<storm::RationalNumber>()));
129 }
130 }
131 }
132#ifdef _VERBOSE_OBSERVATION_UNFOLDING
133 std::cout << "\t\t\t add reset with probability " << resetProb << '\n';
134#endif
135
136 // Add the resets
137 if (resetProb != storm::utility::zero<ValueType>()) {
138 transitionMatrixBuilder.addNextValue(newRowCount, resetDestination, resetProb);
139 }
140#ifdef _VERBOSE_OBSERVATION_UNFOLDING
141 std::cout << "\t\t\t add other transitions...\n";
142#endif
143
144 // Now, we build the outgoing transitions.
145 for (auto const& oldRowEntry : model.getTransitionMatrix().getRow(oldRowIndex)) {
146 if (model.getObservation(oldRowEntry.getColumn()) != observations[step + 1]) {
147 continue; // already handled.
148 }
149 uint64_t column = 0;
150
151 auto entryIt = oldToUnfolded.find(oldRowEntry.getColumn());
152 if (entryIt == oldToUnfolded.end()) {
153 column = newStateIndex;
154 oldToUnfolded[oldRowEntry.getColumn()] = column;
155 unfoldedToOldNextStep[column] = oldRowEntry.getColumn();
156 newStateIndex++;
157 } else {
158 column = entryIt->second;
159 }
160#ifdef _VERBOSE_OBSERVATION_UNFOLDING
161 std::cout << "\t\t\t\t transition to " << column << "with probability " << oldRowEntry.getValue() << '\n';
162#endif
163 transitionMatrixBuilder.addNextValue(newRowCount, column, oldRowEntry.getValue());
164 }
165 newRowCount++;
166 }
167 newRowGroupStart = transitionMatrixBuilder.getLastRow() + 1;
168 }
169 }
170 // Now, take care of the last step.
171 uint64_t sinkState = newStateIndex;
172 uint64_t targetState = newStateIndex + 1;
173 for (auto const& unfoldedToOldEntry : unfoldedToOldNextStep) {
174 addStateValuation(unfoldedToOldEntry.second, observations.size() - 1);
175 transitionMatrixBuilder.newRowGroup(newRowGroupStart);
176 STORM_LOG_ASSERT(risk.size() > unfoldedToOldEntry.second, "Must be a state.");
178 "Risk must be a probability");
179 // std::cout << "risk is" << risk[unfoldedToOldEntry.second] << '\n';
180 if (!storm::utility::isOne(risk[unfoldedToOldEntry.second])) {
181 transitionMatrixBuilder.addNextValue(newRowGroupStart, sinkState, storm::utility::one<ValueType>() - risk[unfoldedToOldEntry.second]);
182 }
183 if (!storm::utility::isZero(risk[unfoldedToOldEntry.second])) {
184 transitionMatrixBuilder.addNextValue(newRowGroupStart, targetState, risk[unfoldedToOldEntry.second]);
185 }
186 newRowGroupStart++;
187 }
188 // sink state
189 transitionMatrixBuilder.newRowGroup(newRowGroupStart);
190 transitionMatrixBuilder.addNextValue(newRowGroupStart, sinkState, storm::utility::one<ValueType>());
191 addStateValuation(-1, -1);
192
193 newRowGroupStart++;
194 transitionMatrixBuilder.newRowGroup(newRowGroupStart);
195 // target state
196 transitionMatrixBuilder.addNextValue(newRowGroupStart, targetState, storm::utility::one<ValueType>());
197 addStateValuation(-1, -1);
198
199#ifdef _VERBOSE_OBSERVATION_UNFOLDING
200 std::cout << "build matrix...\n";
201#endif
202
204 components.transitionMatrix = transitionMatrixBuilder.build();
205#ifdef _VERBOSE_OBSERVATION_UNFOLDING
206 // std::cout << components.transitionMatrix << '\n';
207#endif
208
209 storm::models::sparse::StateLabeling labeling(components.transitionMatrix.getRowGroupCount());
210 labeling.addLabel("_goal");
211 labeling.addLabelToState("_goal", targetState);
212 if (!options.useRestartSemantics) {
213 labeling.addLabel("_violated");
214 labeling.addLabelToState("_violated", violatedState);
215 }
216 labeling.addLabel("_end");
217 labeling.addLabelToState("_end", sinkState);
218 labeling.addLabelToState("_end", targetState);
219 labeling.addLabel("init");
220 labeling.addLabelToState("init", initialState);
221 components.stateLabeling = labeling;
222 components.stateValuations = std::move(stateValuations);
223 return std::make_shared<storm::models::sparse::Mdp<ValueType>>(std::move(components));
224}
225
226template<typename ValueType>
227std::shared_ptr<storm::models::sparse::Mdp<ValueType>> ObservationTraceUnfolder<ValueType>::extend(std::vector<uint32_t> const& observations) {
228 traceSoFar.insert(traceSoFar.end(), observations.begin(), observations.end());
229 return transform(traceSoFar);
230}
231
232template<typename ValueType>
234 traceSoFar = {observation};
235}
236
237template<typename ValueType>
239 return options.useRestartSemantics;
240}
241
247} // namespace pomdp
248} // namespace storm
void addLabel(std::string const &label)
Adds a new label to the labelings.
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.
Observation-trace unrolling to allow model checking for monitoring.
std::shared_ptr< storm::models::sparse::Mdp< ValueType > > extend(std::vector< uint32_t > const &observations)
Transform incrementaly.
void reset(uint32_t observation)
When using the incremental approach, reset the observations made so far.
ObservationTraceUnfolder(storm::models::sparse::Pomdp< ValueType > const &model, std::vector< ValueType > const &risk, std::shared_ptr< storm::expressions::ExpressionManager > exprManager, ObservationTraceUnfolderOptions const &options)
Initialize.
std::shared_ptr< storm::models::sparse::Mdp< ValueType > > transform(std::vector< uint32_t > const &observations)
Transform in one shot.
A bit vector that is internally represented as a vector of 64-bit values.
Definition BitVector.h:16
uint64_t getNextSetIndex(uint64_t startingIndex) const
Retrieves the index of the bit that is the next bit set to true in the bit vector.
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.
index_type getLastRow() const
Retrieves the most recently used row.
void addNextValue(index_type row, index_type column, value_type const &value)
Sets the matrix entry at the given row and column to the given value.
void newRowGroup(index_type startingRow)
Starts a new row group in the matrix.
SparseMatrix< value_type > build(index_type overriddenRowCount=0, index_type overriddenColumnCount=0, index_type overriddenRowGroupCount=0)
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.
Stores valuations of variables for a set of entities (e.g.
void emplaceBack(uint64_t classIndex, Callback const &callback)
Appends a new entity of the given class and populates its variables via callback.
#define STORM_LOG_ASSERT(cond, message)
Definition macros.h:9
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:28
bool isOne(ValueType const &a)
Definition constants.cpp:37
bool isBetween(ValueType const &a, ValueType const &b, ValueType const &c, bool strict)
Compare whether a <= b <= c or a < b < c, based on the strictness parameter.
Definition constants.cpp:85
bool isZero(ValueType const &a)
Definition constants.cpp:42
ValueType zero()
Definition constants.cpp:24
ValueType one()
Definition constants.cpp:19
storm::storage::SparseMatrix< ValueType > transitionMatrix
storm::models::sparse::StateLabeling stateLabeling
std::optional< storm::storage::sparse::Valuations > stateValuations