Storm
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
SparseLTLSchedulerHelper.cpp
Go to the documentation of this file.
9
10namespace storm {
11namespace modelchecker {
12namespace helper {
13namespace internal {
14
15template<typename ValueType, bool Nondeterministic>
16const uint_fast64_t SparseLTLSchedulerHelper<ValueType, Nondeterministic>::DEFAULT_INFSET = 0;
17
18template<typename ValueType, bool Nondeterministic>
19uint_fast64_t SparseLTLSchedulerHelper<ValueType, Nondeterministic>::InfSetPool::getOrCreateIndex(storm::storage::BitVector&& infSet) {
20 auto it = std::find(_storage.begin(), _storage.end(), infSet);
21 if (it == _storage.end()) {
22 _storage.push_back(std::move(infSet));
23 return _storage.size() - 1;
24 } else {
25 return distance(_storage.begin(), it);
26 }
27}
28
29template<typename ValueType, bool Nondeterministic>
30storm::storage::BitVector const& SparseLTLSchedulerHelper<ValueType, Nondeterministic>::InfSetPool::get(uint_fast64_t index) const {
31 STORM_LOG_ASSERT(index < size(), "inf set index " << index << " is invalid.");
32 return _storage[index];
33}
34
35template<typename ValueType, bool Nondeterministic>
36uint_fast64_t SparseLTLSchedulerHelper<ValueType, Nondeterministic>::InfSetPool::size() const {
37 return _storage.size();
38}
39
40template<typename ValueType, bool Nondeterministic>
42 : _randomScheduler(false), _accInfSets(numProductStates, boost::none) {
43 // Intentionally left empty.
44}
45
46template<typename ValueType, bool Nondeterministic>
47uint_fast64_t SparseLTLSchedulerHelper<ValueType, Nondeterministic>::SparseLTLSchedulerHelper::getMemoryState(uint_fast64_t daState, uint_fast64_t infSet) {
48 return (daState * _infSets.size()) + infSet;
49}
50
51template<typename ValueType, bool Nondeterministic>
52void SparseLTLSchedulerHelper<ValueType, Nondeterministic>::SparseLTLSchedulerHelper::setRandom() {
53 this->_randomScheduler = true;
54}
55
56template<typename ValueType, bool Nondeterministic>
58 automata::AcceptanceCondition const& acceptance, storm::storage::MaximalEndComponent const& acceptingEc,
59 storm::storage::MaximalEndComponent const& containingMec, std::vector<automata::AcceptanceCondition::acceptance_expr::ptr> const& conjunction,
61 STORM_LOG_ASSERT(std::all_of(acceptingEc.begin(), acceptingEc.end(),
62 [&containingMec](auto const& accStateChoicesPair) { return containingMec.containsState(accStateChoicesPair.first); }),
63 "All states in the acceptingEC must be contained in the containing mec.");
64 storm::storage::BitVector acceptingEcStates(product->getProductModel().getNumberOfStates(), false);
65 for (auto const& [state, _] : acceptingEc) {
66 acceptingEcStates.set(state);
67 }
68
69 // Catch the case where there are states in the containing MEC that are not in the accepting EC
70 if (acceptingEc.size() < containingMec.size()) {
71 storm::storage::BitVector remainingMecStates(product->getProductModel().getNumberOfStates(), false);
72 for (auto const& [state, _] : containingMec) {
73 if (!acceptingEcStates.get(state)) {
74 remainingMecStates.set(state);
75 }
76 }
77 // Compute a scheduler that, with prob=1 reaches the overlap states
78 storm::storage::Scheduler<ValueType> mecScheduler(product->getProductModel().getNumberOfStates());
79 storm::utility::graph::computeSchedulerProb1E<ValueType>(remainingMecStates, product->getProductModel().getTransitionMatrix(),
80 product->getProductModel().getBackwardTransitions(), remainingMecStates, acceptingEcStates,
81 mecScheduler);
82
83 // Extract scheduler choices
84 for (auto pState : remainingMecStates) {
85 this->_producedChoices.insert(
86 {std::make_tuple(product->getModelState(pState), product->getAutomatonState(pState), DEFAULT_INFSET), mecScheduler.getChoice(pState)});
87 }
88 }
89
90 // Set choices for the accepting EC. We know the conjunction is satisfied: Save InfSets.
91 std::set<uint_fast64_t> infSetIds;
92 for (auto const& literal : conjunction) {
93 if (literal->isAtom()) {
94 const cpphoafparser::AtomAcceptance& atom = literal->getAtom();
95 if (atom.getType() == cpphoafparser::AtomAcceptance::TEMPORAL_INF) {
97 if (atom.isNegated()) {
98 infSet = ~acceptance.getAcceptanceSet(atom.getAcceptanceSet());
99 } else {
100 infSet = acceptance.getAcceptanceSet(atom.getAcceptanceSet());
101 }
102 // Save new InfSet
103 infSetIds.insert(_infSets.getOrCreateIndex(std::move(infSet)));
104 }
105 // A TEMPORAL_FIN atom can be ignored at this point since the mec is already known to only contain "allowed" states
106 }
107 // TRUE literals can be ignored since those will be satisfied anyway
108 // FALSE literals are not possible here since the MEC is known to be accepting.
109 }
110 if (infSetIds.empty()) {
111 // There might not be any infSet at this point (e.g. because all literals are TEMPORAL_FIN atoms). We need at least one infset, though
112 infSetIds.insert(_infSets.getOrCreateIndex(storm::storage::BitVector(product->getProductModel().getNumberOfStates(), true)));
113 }
114
115 // Save the InfSets into the _accInfSets for states in this MEC
116 for (auto const& mecState : acceptingEcStates) {
117 STORM_LOG_ASSERT(!_accInfSets[mecState].is_initialized(), "accepting inf sets were already defined for a MEC state which is not expected.");
118 _accInfSets[mecState].emplace(infSetIds);
119 }
120
121 // Define scheduler choices for the states in this MEC (that are not in any other MEC)
122 // The resulting scheduler will visit each InfSet inf often
123 for (uint_fast64_t id : infSetIds) {
124 // Scheduler that satisfies the MEC acceptance condition
125 storm::storage::Scheduler<ValueType> mecScheduler(product->getProductModel().getNumberOfStates());
126
127 storm::storage::BitVector infStatesWithinMec = _infSets.get(id) & acceptingEcStates;
128 // States not in InfSet: Compute a scheduler that, with prob=1, reaches the infSet via mecStates
129 storm::utility::graph::computeSchedulerProb1E<ValueType>(acceptingEcStates, product->getProductModel().getTransitionMatrix(),
130 product->getProductModel().getBackwardTransitions(), acceptingEcStates, infStatesWithinMec,
131 mecScheduler);
132
133 // States that already reached the InfSet
134 for (auto pState : infStatesWithinMec) {
135 // Prob1E sets an arbitrary choice for the psi states, but we want to stay in this accepting MEC.
136 mecScheduler.setChoice(
137 *acceptingEc.getChoicesForState(pState).begin() - product->getProductModel().getTransitionMatrix().getRowGroupIndices()[pState], pState);
138 }
139
140 // Extract scheduler choices
141 for (auto pState : acceptingEcStates) {
142 // We want to reach the InfSet, save choice: <s, q, InfSetID> ---> choice
143 this->_producedChoices.insert(
144 {std::make_tuple(product->getModelState(pState), product->getAutomatonState(pState), id), mecScheduler.getChoice(pState)});
145 }
146 }
147}
148
149template<typename ValueType, bool Nondeterministic>
151 std::unique_ptr<storm::storage::Scheduler<ValueType>> reachScheduler,
152 transformer::DAProductBuilder const& productBuilder,
154 storm::storage::BitVector const& modelStatesOfInterest,
155 storm::storage::SparseMatrix<ValueType> const& transitionMatrix) {
156 STORM_LOG_ASSERT(_infSets.size() > 0, "There is no inf set. Were the accepting ECs processed before?");
157
158 // Compute size of the resulting memory structure: A state <q, infSet> is encoded as (q* (|infSets|))+ |infSet|
159 uint64_t numMemoryStates = (numDaStates) * (_infSets.size());
160 _dontCareStates = std::vector<storm::storage::BitVector>(numMemoryStates, storm::storage::BitVector(transitionMatrix.getRowGroupCount(), false));
161
162 // Set choices for states or consider them "dontCare"
163 for (storm::storage::sparse::state_type automatonState = 0; automatonState < numDaStates; ++automatonState) {
164 for (storm::storage::sparse::state_type modelState = 0; modelState < transitionMatrix.getRowGroupCount(); ++modelState) {
165 if (!product->isValidProductState(modelState, automatonState)) {
166 // If the state <s,q> does not occur in the product model, all the infSet combinations are irrelevant for the scheduler.
167 for (uint_fast64_t infSet = 0; infSet < _infSets.size(); ++infSet) {
168 _dontCareStates[getMemoryState(automatonState, infSet)].set(modelState, true);
169 }
170 } else {
171 auto pState = product->getProductStateIndex(modelState, automatonState);
172 if (acceptingProductStates.get(pState)) {
173 // For states in accepting ECs set the MEC-scheduler. Missing combinations are "dontCare", they are not reachable using the scheduler
174 // choices.
175 for (uint_fast64_t infSet = 0; infSet < _infSets.size(); ++infSet) {
176 if (_producedChoices.count(std::make_tuple(product->getModelState(pState), product->getAutomatonState(pState), infSet)) == 0) {
177 _dontCareStates[getMemoryState(product->getAutomatonState(pState), infSet)].set(product->getModelState(pState), true);
178 }
179 }
180
181 } else {
182 // Extract the choices of the REACH-scheduler (choices to reach an acc. MEC) for the MDP-DA product: <s,q> -> choice. The memory structure
183 // corresponds to the "0th" copy of the DA (DEFAULT_INFSET).
184 this->_accInfSets[pState] = std::set<uint_fast64_t>({DEFAULT_INFSET});
185 if (reachScheduler->isDontCare(pState)) {
186 // Mark the maybe States of the untilProbability scheduler as "dontCare"
187 _dontCareStates[getMemoryState(product->getAutomatonState(pState), DEFAULT_INFSET)].set(product->getModelState(pState), true);
188 } else {
189 // Set choice For non-accepting states that are not in any accepting EC
190 this->_producedChoices.insert({std::make_tuple(product->getModelState(pState), product->getAutomatonState(pState), DEFAULT_INFSET),
191 reachScheduler->getChoice(pState)});
192 };
193 // All other InfSet combinations are unreachable (dontCare)
194 static_assert(DEFAULT_INFSET == 0, "This code assumes that the default infset is 0");
195 for (uint_fast64_t infSet = 1; infSet < _infSets.size(); ++infSet) {
196 _dontCareStates[getMemoryState(product->getAutomatonState(pState), infSet)].set(product->getModelState(pState), true);
197 }
198 }
199 }
200 }
201 }
202
203 // Prepare the memory structure. For that, we need: transitions, initialMemoryStates (and memoryStateLabeling)
204
205 // The next move function of the memory, will be build based on the transitions of the DA and jumps between InfSets.
206 _memoryTransitions = std::vector<std::vector<storm::storage::BitVector>>(
207 numMemoryStates, std::vector<storm::storage::BitVector>(numMemoryStates, storm::storage::BitVector(transitionMatrix.getRowGroupCount(), false)));
208 for (storm::storage::sparse::state_type automatonFrom = 0; automatonFrom < numDaStates; ++automatonFrom) {
209 for (storm::storage::sparse::state_type modelState = 0; modelState < transitionMatrix.getRowGroupCount(); ++modelState) {
210 uint_fast64_t automatonTo = productBuilder.getSuccessor(automatonFrom, modelState);
211
212 if (product->isValidProductState(modelState, automatonTo)) {
213 uint_fast64_t daProductState = product->getProductStateIndex(modelState, automatonTo);
214 STORM_LOG_ASSERT(_accInfSets[daProductState] != boost::none,
215 "The list of InfSets for the product state <" << modelState << ", " << automatonTo << "> is undefined.");
216 std::set<uint_fast64_t> const& daProductStateInfSets = _accInfSets[daProductState].get();
217 // Add the modelState to one outgoing transition of all states of the form <automatonFrom, InfSet>
218 // For non-accepting states that are not in any accepting EC and for overlapping accepting ECs we always use the '0th' copy of the DA.
219 // For the states in accepting ECs that do not overlap we cycle through copies 0,1,2, ... k of the DA (skipping copies whose inf-sets are not
220 // needed for the accepting EC).
221 for (uint_fast64_t currentInfSet = 0; currentInfSet < _infSets.size(); ++currentInfSet) {
222 uint_fast64_t newInfSet;
223 // Check if we need to switch the inf set (i.e. the DA copy)
224 if (daProductStateInfSets.count(currentInfSet) == 0) {
225 // This infSet is not relevant for the daProductState. We need to switch to a copy representing a relevant infset.
226 newInfSet = *daProductStateInfSets.begin();
227 } else if (daProductStateInfSets.size() > 1 && (_infSets.get(currentInfSet).get(daProductState))) {
228 // We have reached a state from the current infSet and thus need to move on to the next infSet in the list.
229 // Note that if the list contains just a single item, the switch would have no effect.
230 // In particular, this is the case for states that are not in an accepting MEC as those only have DEFAULT_INFSET in their list
231 auto nextInfSetIt = daProductStateInfSets.find(currentInfSet);
232 STORM_LOG_ASSERT(nextInfSetIt != daProductStateInfSets.end(), "The list of InfSets for the product state <"
233 << modelState << ", " << automatonTo
234 << "> does not contain the infSet " << currentInfSet);
235 nextInfSetIt++;
236 if (nextInfSetIt == daProductStateInfSets.end()) {
237 // Start again.
238 nextInfSetIt = daProductStateInfSets.begin();
239 }
240 newInfSet = *nextInfSetIt;
241 } else {
242 // In all other cases we can keep the current inf set (i.e. the DA copy)
243 newInfSet = currentInfSet;
244 }
245 _memoryTransitions[getMemoryState(automatonFrom, currentInfSet)][getMemoryState(automatonTo, newInfSet)].set(modelState);
246 }
247 }
248 }
249 }
250 // Finished creation of transitions.
251
252 // Find initial memory states
253 this->_memoryInitialStates = std::vector<uint_fast64_t>(transitionMatrix.getRowGroupCount());
254 // Save for each relevant model state its initial memory state (get the s-successor q of q0)
255 for (storm::storage::sparse::state_type modelState : modelStatesOfInterest) {
256 storm::storage::sparse::state_type automatonState = productBuilder.getInitialState(modelState);
257 STORM_LOG_ASSERT(product->isValidProductState(modelState, automatonState),
258 "The memory successor state for the model state " << modelState << "does not exist in the DA-Model Product.");
259 STORM_LOG_ASSERT(_accInfSets[product->getProductStateIndex(modelState, automatonState)] != boost::none,
260 "The list of InfSets for the product state <" << modelState << ", " << automatonState << "> is undefined.");
261 // Start in the first InfSet of <s, q>
262 auto infSet = _accInfSets[product->getProductStateIndex(modelState, automatonState)].get().begin();
263 _memoryInitialStates[modelState] = getMemoryState(automatonState, *infSet);
264 }
265}
266
267template<typename ValueType, bool Nondeterministic>
269 storm::models::sparse::Model<ValueType> const& model, bool onlyInitialStatesRelevant) {
270 if (_randomScheduler) {
272 for (storm::storage::sparse::state_type state = 0; state < model.getNumberOfStates(); ++state) {
273 scheduler.setChoice(0, state);
274 }
275 return scheduler;
276 }
277
278 // Otherwise, we compute a scheduler with memory.
279
280 // Create a memory structure for the MDP scheduler with memory. If hasRelevantStates is set, we only consider initial model states relevant.
281 auto memoryBuilder = storm::storage::MemoryStructureBuilder<ValueType>(this->_memoryTransitions.size(), model, onlyInitialStatesRelevant);
282
283 // Build the transitions between the memory states: startState to goalState using modelStates (transitionVector).
284 for (storm::storage::sparse::state_type startState = 0; startState < this->_memoryTransitions.size(); ++startState) {
285 for (storm::storage::sparse::state_type goalState = 0; goalState < this->_memoryTransitions.size(); ++goalState) {
286 // Bitvector that represents modelStates the model states that trigger this transition.
287 memoryBuilder.setTransition(startState, goalState, this->_memoryTransitions[startState][goalState]);
288 }
289 }
290
291 // InitialMemoryStates: Assign an initial memory state model states
292 if (onlyInitialStatesRelevant) {
293 // Only consider initial model states
294 for (uint_fast64_t modelState : model.getInitialStates()) {
295 memoryBuilder.setInitialMemoryState(modelState, this->_memoryInitialStates[modelState]);
296 }
297 } else {
298 // All model states are relevant
299 for (uint_fast64_t modelState = 0; modelState < model.getNumberOfStates(); ++modelState) {
300 memoryBuilder.setInitialMemoryState(modelState, this->_memoryInitialStates[modelState]);
301 }
302 }
303
304 // Build the memoryStructure.
305 storm::storage::MemoryStructure memoryStructure = memoryBuilder.build();
306
307 // Create a scheduler (with memory) for the model from the REACH and MEC scheduler of the MDP-DA-product model.
308 storm::storage::Scheduler<ValueType> scheduler(model.getNumberOfStates(), memoryStructure);
309
310 // Use choices in the product model to create a choice based on model state and memory state
311 for (const auto& choice : this->_producedChoices) {
312 // <s, q, InfSet> -> choice
313 storm::storage::sparse::state_type modelState = std::get<0>(choice.first);
314 storm::storage::sparse::state_type daState = std::get<1>(choice.first);
315 uint_fast64_t infSet = std::get<2>(choice.first);
316 STORM_LOG_ASSERT(!this->_dontCareStates[getMemoryState(daState, infSet)].get(modelState), "Tried to set choice for dontCare state.");
317 scheduler.setChoice(choice.second, modelState, getMemoryState(daState, infSet));
318 }
319
320 // Set "dontCare" states
321 for (uint_fast64_t memoryState = 0; memoryState < this->_dontCareStates.size(); ++memoryState) {
322 for (auto state : this->_dontCareStates[memoryState]) {
323 scheduler.setDontCare(state, memoryState);
324 }
325 }
326
327 // Sanity check for created scheduler.
328 STORM_LOG_ASSERT(scheduler.isDeterministicScheduler(), "Expected a deterministic scheduler");
329 STORM_LOG_ASSERT(!scheduler.isPartialScheduler(), "Expected a fully defined scheduler");
330
331 return scheduler;
332}
333
334template class SparseLTLSchedulerHelper<double, false>;
335template class SparseLTLSchedulerHelper<double, true>;
336
337#ifdef STORM_HAVE_CARL
338template class SparseLTLSchedulerHelper<storm::RationalNumber, false>;
339template class SparseLTLSchedulerHelper<storm::RationalNumber, true>;
340template class SparseLTLSchedulerHelper<storm::RationalFunction, false>;
341
342#endif
343
344} // namespace internal
345} // namespace helper
346} // namespace modelchecker
347} // namespace storm
storm::storage::BitVector & getAcceptanceSet(unsigned int index)
Helper class for scheduler construction in LTL model checking.
void saveProductEcChoices(automata::AcceptanceCondition const &acceptance, storm::storage::MaximalEndComponent const &acceptingEC, storm::storage::MaximalEndComponent const &containingMEC, std::vector< automata::AcceptanceCondition::acceptance_expr::ptr > const &conjunction, typename transformer::DAProduct< productModelType >::ptr product)
Save choices for states in the accepting end component of the DA-Model product and for the states in ...
void prepareScheduler(uint_fast64_t numDaStates, storm::storage::BitVector const &acceptingProductStates, std::unique_ptr< storm::storage::Scheduler< ValueType > > reachScheduler, transformer::DAProductBuilder const &productBuilder, typename transformer::DAProduct< productModelType >::ptr product, storm::storage::BitVector const &modelStatesOfInterest, storm::storage::SparseMatrix< ValueType > const &transitionMatrix)
Extracts scheduler choices and creates the memory structure for the LTL-Scheduler.
SparseLTLSchedulerHelper(uint_fast64_t numProductStates)
Initializes the helper.
Base class for all sparse models.
Definition Model.h:33
virtual uint_fast64_t getNumberOfStates() const override
Returns the number of states of the model.
Definition Model.cpp:162
A bit vector that is internally represented as a vector of 64-bit values.
Definition BitVector.h:18
void set(uint_fast64_t index, bool value=true)
Sets the given truth value at the given index.
bool get(uint_fast64_t index) const
Retrieves the truth value of the bit at the given index and performs a bound check.
This class represents a maximal end-component of a nondeterministic model.
iterator end()
Retrieves an iterator that points past the last state and its choices in the MEC.
set_type const & getChoicesForState(uint_fast64_t state) const
Retrieves the choices for the given state that are contained in this MEC under the assumption that th...
iterator begin()
Retrieves an iterator that points to the first state and its choices in the MEC.
This class represents a (deterministic) memory structure that can be used to encode certain events (s...
This class defines which action is chosen in a particular state of a non-deterministic model.
Definition Scheduler.h:18
SchedulerChoice< ValueType > const & getChoice(uint_fast64_t modelState, uint_fast64_t memoryState=0) const
Gets the choice defined by the scheduler for the given model and memory state.
Definition Scheduler.cpp:86
void setChoice(SchedulerChoice< ValueType > const &choice, uint_fast64_t modelState, uint_fast64_t memoryState=0)
Sets the choice defined by the scheduler for the given state.
Definition Scheduler.cpp:37
A class that holds a possibly non-square matrix in the compressed row storage format.
index_type getRowGroupCount() const
Returns the number of row groups in the matrix.
storm::storage::sparse::state_type getSuccessor(storm::storage::sparse::state_type automatonFrom, storm::storage::sparse::state_type modelTo) const
storm::storage::sparse::state_type getInitialState(storm::storage::sparse::state_type modelState) const
std::shared_ptr< DAProduct< Model > > ptr
Definition DAProduct.h:13
state_type getModelState(state_type productStateIndex) const
Definition Product.h:31
state_type getAutomatonState(state_type productStateIndex) const
Definition Product.h:35
state_type getProductStateIndex(state_type modelState, state_type automatonState) const
Definition Product.h:39
bool isValidProductState(state_type modelState, state_type automatonState) const
Definition Product.h:43
#define STORM_LOG_ASSERT(cond, message)
Definition macros.h:11
LabParser.cpp.
Definition cli.cpp:18