Storm 1.11.1.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
SparseNondeterministicInfiniteHorizonHelper.cpp
Go to the documentation of this file.
2
17
18namespace storm {
19namespace modelchecker {
20namespace helper {
21
22template<typename ValueType>
28
29template<typename ValueType>
31 storm::storage::SparseMatrix<ValueType> const& transitionMatrix, storm::storage::BitVector const& markovianStates, std::vector<ValueType> const& exitRates)
32 : SparseInfiniteHorizonHelper<ValueType, true>(transitionMatrix, markovianStates, exitRates) {
33 // Intentionally left empty.
34}
35
36template<typename ValueType>
38 STORM_LOG_ASSERT(this->isProduceSchedulerSet(), "Trying to get the produced optimal choices although no scheduler was requested.");
39 STORM_LOG_ASSERT(this->_producedOptimalChoices.is_initialized(),
40 "Trying to get the produced optimal choices but none were available. Was there a computation call before?");
41 return this->_producedOptimalChoices.get();
42}
43
44template<typename ValueType>
46 STORM_LOG_ASSERT(this->isProduceSchedulerSet(), "Trying to get the produced optimal choices although no scheduler was requested.");
47 STORM_LOG_ASSERT(this->_producedOptimalChoices.is_initialized(),
48 "Trying to get the produced optimal choices but none were available. Was there a computation call before?");
49 return this->_producedOptimalChoices.get();
50}
51
52template<typename ValueType>
54 auto const& optimalChoices = getProducedOptimalChoices();
55 storm::storage::Scheduler<ValueType> scheduler(optimalChoices.size());
56 for (uint64_t state = 0; state < optimalChoices.size(); ++state) {
57 scheduler.setChoice(optimalChoices[state], state);
58 }
59 return scheduler;
60}
61
62template<typename ValueType>
64 if (this->_longRunComponentDecomposition == nullptr) {
65 // The decomposition has not been provided or computed, yet.
66 this->createBackwardTransitions();
67 this->_computedLongRunComponentDecomposition =
68 std::make_unique<storm::storage::MaximalEndComponentDecomposition<ValueType>>(this->_transitionMatrix, *this->_backwardTransitions);
69 this->_longRunComponentDecomposition = this->_computedLongRunComponentDecomposition.get();
70 }
71}
72
73template<typename ValueType>
75 ValueGetter const& actionRewardsGetter,
76 storm::storage::MaximalEndComponent const& component) {
77 // For models with potential nondeterminisim, we compute the LRA for a maximal end component (MEC)
78
79 // Allocate memory for the nondeterministic choices.
80 if (this->isProduceSchedulerSet()) {
81 if (!this->_producedOptimalChoices.is_initialized()) {
82 this->_producedOptimalChoices.emplace();
83 }
84 this->_producedOptimalChoices->resize(this->_transitionMatrix.getRowGroupCount());
85 }
86
87 auto trivialResult = this->computeLraForTrivialMec(env, stateRewardsGetter, actionRewardsGetter, component);
88 if (trivialResult.first) {
89 return trivialResult.second;
90 }
91
92 // Solve nontrivial MEC with the method specified in the settings
93 storm::solver::LraMethod method = env.solver().lra().getNondetLraMethod();
95 method != storm::solver::LraMethod::LinearProgramming) {
97 "Selecting 'LP' as the solution technique for long-run properties to guarantee exact results. If you want to override this, please explicitly "
98 "specify a different LRA method.");
99 method = storm::solver::LraMethod::LinearProgramming;
100 } else if (env.solver().isForceSoundness() && env.solver().lra().isNondetLraMethodSetFromDefault() && method != storm::solver::LraMethod::ValueIteration) {
102 "Selecting 'VI' as the solution technique for long-run properties to guarantee sound results. If you want to override this, please explicitly "
103 "specify a different LRA method.");
104 method = storm::solver::LraMethod::ValueIteration;
105 }
106 STORM_LOG_ERROR_COND(!this->isProduceSchedulerSet() || method == storm::solver::LraMethod::ValueIteration,
107 "Scheduler generation not supported for the chosen LRA method. Try value-iteration.");
108 if (method == storm::solver::LraMethod::LinearProgramming) {
109 return computeLraForMecLp(env, stateRewardsGetter, actionRewardsGetter, component);
110 } else if (method == storm::solver::LraMethod::ValueIteration) {
111 return computeLraForMecVi(env, stateRewardsGetter, actionRewardsGetter, component);
112 } else {
113 STORM_LOG_THROW(false, storm::exceptions::InvalidSettingsException, "Unsupported technique.");
114 }
115}
116
117template<typename ValueType>
119 Environment const& env, ValueGetter const& stateRewardsGetter, ValueGetter const& actionRewardsGetter,
120 storm::storage::MaximalEndComponent const& component) {
121 // If the component only consists of a single state, we compute the LRA value directly
122 if (component.size() == 1) {
123 auto const& element = *component.begin();
124 uint64_t state = internal::getComponentElementState(element);
125 auto choiceIt = internal::getComponentElementChoicesBegin(element);
126 if (!this->isContinuousTime()) {
127 // This is an MDP.
128 // Find the choice with the highest/lowest reward
129 ValueType bestValue = actionRewardsGetter(*choiceIt);
130 uint64_t bestChoice = *choiceIt;
131 for (++choiceIt; choiceIt != internal::getComponentElementChoicesEnd(element); ++choiceIt) {
132 ValueType currentValue = actionRewardsGetter(*choiceIt);
133 if ((this->minimize() && currentValue < bestValue) || (this->maximize() && currentValue > bestValue)) {
134 bestValue = std::move(currentValue);
135 bestChoice = *choiceIt;
136 }
137 }
138 if (this->isProduceSchedulerSet()) {
139 this->_producedOptimalChoices.get()[state] = bestChoice - this->_transitionMatrix.getRowGroupIndices()[state];
140 }
141 bestValue += stateRewardsGetter(state);
142 return {true, bestValue};
143 } else {
144 // In a Markov Automaton, singleton components have to consist of a Markovian state because of the non-Zenoness assumption. Then, there is just one
145 // possible choice.
146 STORM_LOG_ASSERT(this->_markovianStates != nullptr,
147 "Nondeterministic continuous time model without Markovian states... Is this a not a Markov Automaton?");
148 STORM_LOG_THROW(this->_markovianStates->get(state), storm::exceptions::InvalidOperationException,
149 "Markov Automaton has Zeno behavior. Computation of Long Run Average values not supported.");
150 STORM_LOG_ASSERT(internal::getComponentElementChoiceCount(element) == 1, "Markovian state has Nondeterministic behavior.");
151 if (this->isProduceSchedulerSet()) {
152 this->_producedOptimalChoices.get()[state] = 0;
153 }
154 ValueType result = stateRewardsGetter(state) +
155 (this->isContinuousTime() ? (*this->_exitRates)[state] * actionRewardsGetter(*choiceIt) : actionRewardsGetter(*choiceIt));
156 return {true, result};
157 }
158 }
159 return {false, storm::utility::zero<ValueType>()};
160}
161
162template<typename ValueType>
164 ValueGetter const& actionRewardsGetter,
166 // Collect some parameters of the computation
167 ValueType aperiodicFactor = storm::utility::convertNumber<ValueType>(env.solver().lra().getAperiodicFactor());
168 std::vector<uint64_t>* optimalChoices = nullptr;
169 if (this->isProduceSchedulerSet()) {
170 optimalChoices = &this->_producedOptimalChoices.get();
171 }
172
173 // Now create a helper and perform the algorithm
174 if (this->isContinuousTime()) {
175 // We assume a Markov Automaton (with deterministic timed states and nondeterministic instant states)
178 viHelper(mec, this->_transitionMatrix, aperiodicFactor, this->_markovianStates, this->_exitRates);
179 return viHelper.performValueIteration(env, stateRewardsGetter, actionRewardsGetter, this->_exitRates, &this->getOptimizationDirection(),
180 optimalChoices);
181 } else {
182 // We assume an MDP (with nondeterministic timed states and no instant states)
185 viHelper(mec, this->_transitionMatrix, aperiodicFactor);
186 return viHelper.performValueIteration(env, stateRewardsGetter, actionRewardsGetter, nullptr, &this->getOptimizationDirection(), optimalChoices);
187 }
188}
189
190template<typename ValueType>
192 ValueGetter const& actionRewardsGetter,
194 // Create an LP solver
195 auto solver = storm::utility::solver::getLpSolver<ValueType>("LRA for MEC");
196
197 // Now build the LP formulation as described in:
198 // Guck et al.: Modelling and Analysis of Markov Reward Automata (ATVA'14), https://doi.org/10.1007/978-3-319-11936-6_13
199 solver->setOptimizationDirection(invert(this->getOptimizationDirection()));
200
201 // Create variables
202 // TODO: Investigate whether we can easily make the variables bounded
203 std::map<uint_fast64_t, storm::expressions::Variable> stateToVariableMap;
204 for (auto const& stateChoicesPair : mec) {
205 std::string variableName = "x" + std::to_string(stateChoicesPair.first);
206 stateToVariableMap[stateChoicesPair.first] = solver->addUnboundedContinuousVariable(variableName);
207 }
208 storm::expressions::Variable k = solver->addUnboundedContinuousVariable("k", storm::utility::one<ValueType>());
209 solver->update();
210
211 // Add constraints.
212 for (auto const& stateChoicesPair : mec) {
213 uint_fast64_t state = stateChoicesPair.first;
214 bool stateIsMarkovian = this->_markovianStates && this->_markovianStates->get(state);
215
216 // Now create a suitable constraint for each choice
217 // x_s {≤, ≥} -k/rate(s) + sum_s' P(s,act,s') * x_s' + (value(s)/rate(s) + value(s,act))
218 for (auto choice : stateChoicesPair.second) {
219 std::vector<storm::expressions::Expression> summands;
220 auto matrixRow = this->_transitionMatrix.getRow(choice);
221 summands.reserve(matrixRow.getNumberOfEntries() + 2);
222 // add -k/rate(s) (only if s is either a Markovian state or we have an MDP)
223 if (stateIsMarkovian) {
224 summands.push_back(-(k / solver->getManager().rational((*this->_exitRates)[state])));
225 } else if (!this->isContinuousTime()) {
226 summands.push_back(-k);
227 }
228 // add sum_s' P(s,act,s') * x_s'
229 for (auto const& element : matrixRow) {
230 summands.push_back(stateToVariableMap.at(element.getColumn()) * solver->getConstant(element.getValue()));
231 }
232 // add value for state and choice
233 ValueType value;
234 if (stateIsMarkovian) {
235 // divide state reward with exit rate
236 value = stateRewardsGetter(state) / (*this->_exitRates)[state] + actionRewardsGetter(choice);
237 } else if (!this->isContinuousTime()) {
238 // in discrete time models no scaling is needed
239 value = stateRewardsGetter(state) + actionRewardsGetter(choice);
240 } else {
241 // state is a probabilistic state of a Markov automaton. The state reward is not collected
242 value = actionRewardsGetter(choice);
243 }
244 summands.push_back(solver->getConstant(value));
246 if (this->minimize()) {
247 constraint = stateToVariableMap.at(state) <= storm::expressions::sum(summands);
248 } else {
249 constraint = stateToVariableMap.at(state) >= storm::expressions::sum(summands);
250 }
251 solver->addConstraint("s" + std::to_string(state) + "," + std::to_string(choice), constraint);
252 }
253 }
254
255 solver->optimize();
256 STORM_LOG_THROW(!this->isProduceSchedulerSet(), storm::exceptions::NotImplementedException,
257 "Scheduler extraction is not yet implemented for LP based LRA method.");
258 return solver->getContinuousValue(k);
259}
260
266template<typename ValueType>
267void addSspMatrixChoice(uint64_t const& inputMatrixChoice, storm::storage::SparseMatrix<ValueType> const& inputTransitionMatrix,
268 std::vector<uint64_t> const& inputToSspStateMap, uint64_t const& numberOfNonComponentStates, uint64_t const& currentSspChoice,
270 // As there could be multiple transitions to the same MEC, we accumulate them in this map before adding them to the matrix builder.
271 std::map<uint64_t, ValueType> auxiliaryStateToProbabilityMap;
272
273 for (auto const& transition : inputTransitionMatrix.getRow(inputMatrixChoice)) {
274 if (!storm::utility::isZero(transition.getValue())) {
275 auto const& sspTransitionTarget = inputToSspStateMap[transition.getColumn()];
276 // Since the auxiliary Component states are appended at the end of the matrix, we can use this check to
277 // decide whether the transition leads to a component state or not
278 if (sspTransitionTarget < numberOfNonComponentStates) {
279 // If the target state is not contained in a component, we can copy over the entry.
280 sspMatrixBuilder.addNextValue(currentSspChoice, sspTransitionTarget, transition.getValue());
281 } else {
282 // If the target state is contained in component i, we need to add the probability to the corresponding field in the vector
283 // so that we are able to write the cumulative probability to the component into the matrix later.
284 auto insertionRes = auxiliaryStateToProbabilityMap.emplace(sspTransitionTarget, transition.getValue());
285 if (!insertionRes.second) {
286 // sspTransitionTarget already existed in the map, i.e., there already was a transition to that component.
287 // Hence, we add up the probabilities.
288 insertionRes.first->second += transition.getValue();
289 }
290 }
291 }
292 }
293
294 // Now insert all (cumulative) probability values that target a component.
295 for (auto const& componentToProbEntry : auxiliaryStateToProbabilityMap) {
296 sspMatrixBuilder.addNextValue(currentSspChoice, componentToProbEntry.first, componentToProbEntry.second);
297 }
298}
299
300template<typename ValueType>
301std::pair<storm::storage::SparseMatrix<ValueType>, std::vector<ValueType>> SparseNondeterministicInfiniteHorizonHelper<ValueType>::buildSspMatrixVector(
302 std::vector<ValueType> const& mecLraValues, std::vector<uint64_t> const& inputToSspStateMap, storm::storage::BitVector const& statesNotInComponent,
303 uint64_t numberOfNonComponentStates, std::vector<std::pair<uint64_t, uint64_t>>* sspComponentExitChoicesToOriginalMap) {
304 auto const& choiceIndices = this->_transitionMatrix.getRowGroupIndices();
305
306 std::vector<ValueType> rhs;
307 uint64_t numberOfSspStates = numberOfNonComponentStates + this->_longRunComponentDecomposition->size();
308 storm::storage::SparseMatrixBuilder<ValueType> sspMatrixBuilder(0, numberOfSspStates, 0, true, true, numberOfSspStates);
309 // If the source state of a transition is not contained in any component, we copy its choices (and perform the necessary modifications).
310 uint64_t currentSspChoice = 0;
311 for (auto nonComponentState : statesNotInComponent) {
312 sspMatrixBuilder.newRowGroup(currentSspChoice);
313 for (uint64_t choice = choiceIndices[nonComponentState]; choice < choiceIndices[nonComponentState + 1]; ++choice, ++currentSspChoice) {
314 rhs.push_back(storm::utility::zero<ValueType>());
315 addSspMatrixChoice(choice, this->_transitionMatrix, inputToSspStateMap, numberOfNonComponentStates, currentSspChoice, sspMatrixBuilder);
316 }
317 }
318 // Now we construct the choices for the auxiliary states which reflect former Component states.
319 for (uint64_t componentIndex = 0; componentIndex < this->_longRunComponentDecomposition->size(); ++componentIndex) {
320 auto const& component = (*this->_longRunComponentDecomposition)[componentIndex];
321 sspMatrixBuilder.newRowGroup(currentSspChoice);
322 // For nondeterministic models it might still be that we leave the component again. This needs to be reflected in the SSP
323 // by adding the "exiting" choices of the MEC to the axiliary states
324 for (auto const& element : component) {
325 uint64_t componentState = internal::getComponentElementState(element);
326 for (uint64_t choice = choiceIndices[componentState]; choice < choiceIndices[componentState + 1]; ++choice) {
327 // If the choice is not contained in the component itself, we have to add a similar distribution to the auxiliary state.
328 if (!internal::componentElementChoicesContains(element, choice)) {
329 rhs.push_back(storm::utility::zero<ValueType>());
330 addSspMatrixChoice(choice, this->_transitionMatrix, inputToSspStateMap, numberOfNonComponentStates, currentSspChoice, sspMatrixBuilder);
331 if (sspComponentExitChoicesToOriginalMap) {
332 // Later we need to be able to map this choice back to the original input model
333 sspComponentExitChoicesToOriginalMap->emplace_back(componentState, choice - choiceIndices[componentState]);
334 }
335 ++currentSspChoice;
336 }
337 }
338 }
339 // For each auxiliary state, there is the option to achieve the reward value of the LRA associated with the component.
340 rhs.push_back(mecLraValues[componentIndex]);
341 if (sspComponentExitChoicesToOriginalMap) {
342 // Insert some invalid values so we can later detect that this choice is not an exit choice
343 sspComponentExitChoicesToOriginalMap->emplace_back(std::numeric_limits<uint_fast64_t>::max(), std::numeric_limits<uint_fast64_t>::max());
344 }
345 ++currentSspChoice;
346 }
347 return std::make_pair(sspMatrixBuilder.build(currentSspChoice, numberOfSspStates, numberOfSspStates), std::move(rhs));
348}
349
350template<typename ValueType>
352 std::vector<uint64_t> const& sspChoices, storm::storage::SparseMatrix<ValueType> const& sspMatrix, std::vector<uint64_t> const& inputToSspStateMap,
353 storm::storage::BitVector const& statesNotInComponent, uint64_t numberOfNonComponentStates,
354 std::vector<std::pair<uint64_t, uint64_t>> const& sspComponentExitChoicesToOriginalMap) {
355 // We first take care of non-mec states
356 storm::utility::vector::setVectorValues(this->_producedOptimalChoices.get(), statesNotInComponent, sspChoices);
357 // Secondly, we consider MEC states. There are 3 cases for each MEC state:
358 // 1. The SSP choices encode that we want to stay in the MEC
359 // 2. The SSP choices encode that we want to leave the MEC and
360 // a) we take an exit (non-MEC) choice at the given state
361 // b) we have to take a MEC choice at the given state in a way that eventually an exit state of the MEC is reached
362 uint64_t exitChoiceOffset = sspMatrix.getRowGroupIndices()[numberOfNonComponentStates];
363 for (auto const& mec : *this->_longRunComponentDecomposition) {
364 // Get the sspState of this MEC (using one representative mec state)
365 auto const& sspState = inputToSspStateMap[mec.begin()->first];
366 uint64_t sspChoiceIndex = sspMatrix.getRowGroupIndices()[sspState] + sspChoices[sspState];
367 // Obtain the state and choice of the original model to which the selected choice corresponds.
368 auto const& originalStateChoice = sspComponentExitChoicesToOriginalMap[sspChoiceIndex - exitChoiceOffset];
369 // Check if we are in Case 1 or 2
370 if (originalStateChoice.first == std::numeric_limits<uint_fast64_t>::max()) {
371 // The optimal choice is to stay in this mec (Case 1)
372 // In this case, no further operations are necessary. The scheduler has already been set to the optimal choices during the call of computeLraForMec.
373 STORM_LOG_ASSERT(sspMatrix.getRow(sspState, sspChoices[sspState]).getNumberOfEntries() == 0, "Expected empty row at choice that stays in MEC.");
374 } else {
375 // The best choice is to leave this MEC via the selected state and choice. (Case 2)
376 // Set the exit choice (Case 2.a)
377 this->_producedOptimalChoices.get()[originalStateChoice.first] = originalStateChoice.second;
378 // The remaining states in this MEC need to reach the state with the exit choice with probability 1. (Case 2.b)
379 // Perform a backwards search from the exit state, only using MEC choices
380 // We start by setting an invalid choice to all remaining mec states (so that we can easily detect them as unprocessed)
381 for (auto const& stateActions : mec) {
382 if (stateActions.first != originalStateChoice.first) {
383 this->_producedOptimalChoices.get()[stateActions.first] = std::numeric_limits<uint64_t>::max();
384 }
385 }
386 // Ensure that backwards transitions are available
387 this->createBackwardTransitions();
388 // Now start a backwards DFS
389 std::vector<uint64_t> stack = {originalStateChoice.first};
390 while (!stack.empty()) {
391 uint64_t currentState = stack.back();
392 stack.pop_back();
393 for (auto const& backwardsTransition : this->_backwardTransitions->getRowGroup(currentState)) {
394 uint64_t predecessorState = backwardsTransition.getColumn();
395 if (mec.containsState(predecessorState)) {
396 auto& selectedPredChoice = this->_producedOptimalChoices.get()[predecessorState];
397 if (selectedPredChoice == std::numeric_limits<uint64_t>::max()) {
398 // We don't already have a choice for this predecessor.
399 // We now need to check whether there is a *MEC* choice leading to currentState
400 for (auto const& predChoice : mec.getChoicesForState(predecessorState)) {
401 for (auto const& forwardTransition : this->_transitionMatrix.getRow(predChoice)) {
402 if (forwardTransition.getColumn() == currentState && !storm::utility::isZero(forwardTransition.getValue())) {
403 // Playing this choice (infinitely often) will lead to current state (infinitely often)!
404 selectedPredChoice = predChoice - this->_transitionMatrix.getRowGroupIndices()[predecessorState];
405 stack.push_back(predecessorState);
406 break;
407 }
408 }
409 if (selectedPredChoice != std::numeric_limits<uint64_t>::max()) {
410 break;
411 }
412 }
413 }
414 }
415 }
416 }
417 }
418 }
419}
420
421template<typename ValueType>
423 std::vector<ValueType> const& componentLraValues) {
424 STORM_LOG_ASSERT(this->_longRunComponentDecomposition != nullptr, "Decomposition not computed, yet.");
425
426 // For fast transition rewriting, we build a mapping from the input state indices to the state indices of a new transition matrix
427 // which redirects all transitions leading to a former component state to a new auxiliary state.
428 // There will be one auxiliary state for each component. These states will be appended to the end of the matrix.
429
430 // First gather the states that are part of a component
431 // and create a mapping from states that lie in a component to the corresponding component index.
432 storm::storage::BitVector statesInComponents(this->_transitionMatrix.getRowGroupCount());
433 std::vector<uint64_t> inputToSspStateMap(this->_transitionMatrix.getRowGroupCount(), std::numeric_limits<uint64_t>::max());
434 for (uint64_t currentComponentIndex = 0; currentComponentIndex < this->_longRunComponentDecomposition->size(); ++currentComponentIndex) {
435 for (auto const& element : (*this->_longRunComponentDecomposition)[currentComponentIndex]) {
436 uint64_t state = internal::getComponentElementState(element);
437 statesInComponents.set(state);
438 inputToSspStateMap[state] = currentComponentIndex;
439 }
440 }
441 // Now take care of the non-component states. Note that the order of these states will be preserved.
442 uint64_t numberOfNonComponentStates = 0;
443 storm::storage::BitVector statesNotInComponent = ~statesInComponents;
444 for (auto nonComponentState : statesNotInComponent) {
445 inputToSspStateMap[nonComponentState] = numberOfNonComponentStates;
446 ++numberOfNonComponentStates;
447 }
448 // Finalize the mapping for the component states which now still assigns component states to to their component index.
449 // To make sure that they point to the auxiliary states (located at the end of the SspMatrix), we need to shift them by the
450 // number of states that are not in a component.
451 for (auto mecState : statesInComponents) {
452 inputToSspStateMap[mecState] += numberOfNonComponentStates;
453 }
454
455 // For scheduler extraction, we will need to create a mapping between choices at the auxiliary states and the
456 // corresponding choices in the original model.
457 std::vector<std::pair<uint_fast64_t, uint_fast64_t>> sspComponentExitChoicesToOriginalMap;
458
459 // The next step is to create the SSP matrix and the right-hand side of the SSP.
460 auto sspMatrixVector = buildSspMatrixVector(componentLraValues, inputToSspStateMap, statesNotInComponent, numberOfNonComponentStates,
461 this->isProduceSchedulerSet() ? &sspComponentExitChoicesToOriginalMap : nullptr);
462
463 // Set-up a solver
466 minMaxLinearEquationSolverFactory.getRequirements(env, true, true, this->getOptimizationDirection(), false, this->isProduceSchedulerSet());
467 requirements.clearBounds();
468 STORM_LOG_THROW(!requirements.hasEnabledCriticalRequirement(), storm::exceptions::UnmetRequirementException,
469 "Solver requirements " + requirements.getEnabledRequirementsAsString() + " not checked.");
470 std::unique_ptr<storm::solver::MinMaxLinearEquationSolver<ValueType>> solver = minMaxLinearEquationSolverFactory.create(env, sspMatrixVector.first);
471 solver->setHasUniqueSolution();
472 solver->setHasNoEndComponents();
473 solver->setTrackScheduler(this->isProduceSchedulerSet());
474 auto lowerUpperBounds = std::minmax_element(componentLraValues.begin(), componentLraValues.end());
475 solver->setLowerBound(*lowerUpperBounds.first);
476 solver->setUpperBound(*lowerUpperBounds.second);
477 solver->setRequirementsChecked();
478
479 // Solve the equation system
480 std::vector<ValueType> x(sspMatrixVector.first.getRowGroupCount());
481 solver->solveEquations(env, this->getOptimizationDirection(), x, sspMatrixVector.second);
482
483 // Prepare scheduler (if requested)
484 if (this->isProduceSchedulerSet() && solver->hasScheduler()) {
485 // Translate result for ssp matrix to original model
486 constructOptimalChoices(solver->getSchedulerChoices(), sspMatrixVector.first, inputToSspStateMap, statesNotInComponent, numberOfNonComponentStates,
487 sspComponentExitChoicesToOriginalMap);
488 } else {
489 STORM_LOG_ERROR_COND(!this->isProduceSchedulerSet(), "Requested to produce a scheduler, but no scheduler was generated.");
490 }
491
492 // Prepare result vector.
493 // For efficiency reasons, we re-use the memory of our rhs for this!
494 std::vector<ValueType> result = std::move(sspMatrixVector.second);
495 result.resize(this->_transitionMatrix.getRowGroupCount());
496 result.shrink_to_fit();
497 storm::utility::vector::selectVectorValues(result, inputToSspStateMap, x);
498 return result;
499}
500
503
504} // namespace helper
505} // namespace modelchecker
506} // namespace storm
SolverEnvironment & solver()
storm::RationalNumber const & getAperiodicFactor() const
storm::solver::LraMethod const & getNondetLraMethod() const
LongRunAverageSolverEnvironment & lra()
Expression rational(double value) const
Creates an expression that characterizes the given rational literal.
ExpressionManager const & getManager() const
Retrieves the manager responsible for this variable.
Definition Variable.cpp:54
Helper class for model checking queries that depend on the long run behavior of the (nondeterministic...
Helper class for model checking queries that depend on the long run behavior of the (nondeterministic...
virtual std::vector< ValueType > buildAndSolveSsp(Environment const &env, std::vector< ValueType > const &mecLraValues) override
std::pair< storm::storage::SparseMatrix< ValueType >, std::vector< ValueType > > buildSspMatrixVector(std::vector< ValueType > const &mecLraValues, std::vector< uint64_t > const &inputToSspStateMap, storm::storage::BitVector const &statesNotInComponent, uint64_t numberOfNonComponentStates, std::vector< std::pair< uint64_t, uint64_t > > *sspComponentExitChoicesToOriginalMap)
SparseInfiniteHorizonHelper< ValueType, true >::ValueGetter ValueGetter
Function mapping from indices to values.
virtual ValueType computeLraForComponent(Environment const &env, ValueGetter const &stateValuesGetter, ValueGetter const &actionValuesGetter, storm::storage::MaximalEndComponent const &component) override
void constructOptimalChoices(std::vector< uint64_t > const &sspChoices, storm::storage::SparseMatrix< ValueType > const &sspMatrix, std::vector< uint64_t > const &inputToSspStateMap, storm::storage::BitVector const &statesNotInComponent, uint64_t numberOfNonComponentStates, std::vector< std::pair< uint64_t, uint64_t > > const &sspComponentExitChoicesToOriginalMap)
std::pair< bool, ValueType > computeLraForTrivialMec(Environment const &env, ValueGetter const &stateValuesGetter, ValueGetter const &actionValuesGetter, storm::storage::MaximalEndComponent const &mec)
SparseNondeterministicInfiniteHorizonHelper(storm::storage::SparseMatrix< ValueType > const &transitionMatrix)
Initializes the helper for a discrete time model (i.e.
ValueType computeLraForMecLp(Environment const &env, ValueGetter const &stateValuesGetter, ValueGetter const &actionValuesGetter, storm::storage::MaximalEndComponent const &mec)
As computeLraForMec but uses linear programming as a solution method (independent of what is set in e...
ValueType computeLraForMecVi(Environment const &env, ValueGetter const &stateValuesGetter, ValueGetter const &actionValuesGetter, storm::storage::MaximalEndComponent const &mec)
As computeLraForMec but uses value iteration as a solution method (independent of what is set in env)
Helper class that performs iterations of the value iteration method.
Definition LraViHelper.h:42
std::string getEnabledRequirementsAsString() const
Returns a string that enumerates the enabled requirements.
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.
This class represents a maximal end-component of a nondeterministic model.
iterator begin()
Retrieves an iterator that points to the first state and its choices in the MEC.
This class defines which action is chosen in a particular state of a non-deterministic model.
Definition Scheduler.h:18
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
index_type getNumberOfEntries() const
Retrieves the number of entries in the rows.
A class that can be used to build a sparse matrix by adding value by value.
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)
A class that holds a possibly non-square matrix in the compressed row storage format.
const_rows getRow(index_type row) const
Returns an object representing the given row.
std::vector< index_type > const & getRowGroupIndices() const
Returns the grouping of rows of this matrix.
#define STORM_LOG_INFO(message)
Definition logging.h:24
#define STORM_LOG_ASSERT(cond, message)
Definition macros.h:11
#define STORM_LOG_ERROR_COND(cond, message)
Definition macros.h:52
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:30
Expression sum(std::vector< storm::expressions::Expression > const &expressions)
bool componentElementChoicesContains(typename storm::storage::StronglyConnectedComponent::value_type const &element, uint64_t choice)
uint64_t getComponentElementState(typename storm::storage::StronglyConnectedComponent::value_type const &element)
Auxiliary functions that deal with the different kinds of components (MECs on potentially nondetermin...
uint64_t const * getComponentElementChoicesEnd(typename storm::storage::StronglyConnectedComponent::value_type const &element)
constexpr uint64_t getComponentElementChoiceCount(typename storm::storage::StronglyConnectedComponent::value_type const &)
@ NondetTsNoIs
deterministic choice at timed states, deterministic choice at instant states (as in Markov Automata w...
@ DetTsNondetIs
deterministic choice at timed states, no instant states (as in DTMCs and CTMCs)
uint64_t const * getComponentElementChoicesBegin(typename storm::storage::StronglyConnectedComponent::value_type const &element)
void addSspMatrixChoice(uint64_t const &inputMatrixChoice, storm::storage::SparseMatrix< ValueType > const &inputTransitionMatrix, std::vector< uint64_t > const &inputToSspStateMap, uint64_t const &numberOfNonComponentStates, uint64_t const &currentSspChoice, storm::storage::SparseMatrixBuilder< ValueType > &sspMatrixBuilder)
Auxiliary function that adds the entries of the Ssp Matrix for a single choice (i....
void setVectorValues(std::vector< T > &vector, storm::storage::BitVector const &positions, std::vector< T > const &values)
Sets the provided values at the provided positions in the given vector.
Definition vector.h:78
void selectVectorValues(std::vector< T > &vector, storm::storage::BitVector const &positions, std::vector< T > const &values)
Selects the elements from a vector at the specified positions and writes them consecutively into anot...
Definition vector.h:184
bool isZero(ValueType const &a)
Definition constants.cpp:39