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