19namespace modelchecker {
22template<
typename ValueType>
29template<
typename ValueType>
36template<
typename ValueType>
38 STORM_LOG_ASSERT(this->isProduceSchedulerSet(),
"Trying to get the produced optimal choices although no scheduler was requested.");
40 "Trying to get the produced optimal choices but none were available. Was there a computation call before?");
41 return this->_producedOptimalChoices.get();
44template<
typename ValueType>
46 STORM_LOG_ASSERT(this->isProduceSchedulerSet(),
"Trying to get the produced optimal choices although no scheduler was requested.");
48 "Trying to get the produced optimal choices but none were available. Was there a computation call before?");
49 return this->_producedOptimalChoices.get();
52template<
typename ValueType>
54 auto const& optimalChoices = getProducedOptimalChoices();
56 for (uint64_t state = 0; state < optimalChoices.size(); ++state) {
57 scheduler.
setChoice(optimalChoices[state], state);
62template<
typename ValueType>
64 if (this->_longRunComponentDecomposition ==
nullptr) {
66 this->createBackwardTransitions();
67 this->_computedLongRunComponentDecomposition =
68 std::make_unique<storm::storage::MaximalEndComponentDecomposition<ValueType>>(this->_transitionMatrix, *this->_backwardTransitions);
69 this->_longRunComponentDecomposition = this->_computedLongRunComponentDecomposition.get();
73template<
typename ValueType>
80 if (this->isProduceSchedulerSet()) {
81 if (!this->_producedOptimalChoices.is_initialized()) {
82 this->_producedOptimalChoices.emplace();
84 this->_producedOptimalChoices->resize(this->_transitionMatrix.getRowGroupCount());
87 auto trivialResult = this->computeLraForTrivialMec(env, stateRewardsGetter, actionRewardsGetter, component);
88 if (trivialResult.first) {
89 return trivialResult.second;
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;
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;
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);
113 STORM_LOG_THROW(
false, storm::exceptions::InvalidSettingsException,
"Unsupported technique.");
117template<
typename ValueType>
122 if (component.
size() == 1) {
123 auto const& element = *component.
begin();
126 if (!this->isContinuousTime()) {
129 ValueType bestValue = actionRewardsGetter(*choiceIt);
130 uint64_t bestChoice = *choiceIt;
132 ValueType currentValue = actionRewardsGetter(*choiceIt);
133 if ((this->minimize() && currentValue < bestValue) || (this->maximize() && currentValue > bestValue)) {
134 bestValue = std::move(currentValue);
135 bestChoice = *choiceIt;
138 if (this->isProduceSchedulerSet()) {
139 this->_producedOptimalChoices.get()[state] = bestChoice - this->_transitionMatrix.getRowGroupIndices()[state];
141 bestValue += stateRewardsGetter(state);
142 return {
true, bestValue};
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.");
151 if (this->isProduceSchedulerSet()) {
152 this->_producedOptimalChoices.get()[state] = 0;
154 ValueType result = stateRewardsGetter(state) +
155 (this->isContinuousTime() ? (*this->_exitRates)[state] * actionRewardsGetter(*choiceIt) : actionRewardsGetter(*choiceIt));
156 return {
true, result};
159 return {
false, storm::utility::zero<ValueType>()};
162template<
typename ValueType>
168 std::vector<uint64_t>* optimalChoices =
nullptr;
169 if (this->isProduceSchedulerSet()) {
170 optimalChoices = &this->_producedOptimalChoices.get();
174 if (this->isContinuousTime()) {
178 viHelper(mec, this->_transitionMatrix, aperiodicFactor, this->_markovianStates, this->_exitRates);
179 return viHelper.performValueIteration(env, stateRewardsGetter, actionRewardsGetter, this->_exitRates, &this->getOptimizationDirection(),
185 viHelper(mec, this->_transitionMatrix, aperiodicFactor);
186 return viHelper.performValueIteration(env, stateRewardsGetter, actionRewardsGetter,
nullptr, &this->getOptimizationDirection(), optimalChoices);
190template<
typename ValueType>
195 auto solver = storm::utility::solver::getLpSolver<ValueType>(
"LRA for MEC");
199 solver->setOptimizationDirection(invert(this->getOptimizationDirection()));
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);
212 for (
auto const& stateChoicesPair : mec) {
213 uint_fast64_t state = stateChoicesPair.first;
214 bool stateIsMarkovian = this->_markovianStates && this->_markovianStates->get(state);
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);
223 if (stateIsMarkovian) {
225 }
else if (!this->isContinuousTime()) {
226 summands.push_back(-k);
229 for (
auto const& element : matrixRow) {
230 summands.push_back(stateToVariableMap.at(element.getColumn()) * solver->getConstant(element.getValue()));
234 if (stateIsMarkovian) {
236 value = stateRewardsGetter(state) / (*this->_exitRates)[state] + actionRewardsGetter(choice);
237 }
else if (!this->isContinuousTime()) {
239 value = stateRewardsGetter(state) + actionRewardsGetter(choice);
242 value = actionRewardsGetter(choice);
244 summands.push_back(solver->getConstant(value));
246 if (this->minimize()) {
251 solver->addConstraint(
"s" + std::to_string(state) +
"," + std::to_string(choice), constraint);
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);
266template<
typename ValueType>
268 std::vector<uint64_t>
const& inputToSspStateMap, uint64_t
const& numberOfNonComponentStates, uint64_t
const& currentSspChoice,
271 std::map<uint64_t, ValueType> auxiliaryStateToProbabilityMap;
273 for (
auto const& transition : inputTransitionMatrix.
getRow(inputMatrixChoice)) {
275 auto const& sspTransitionTarget = inputToSspStateMap[transition.getColumn()];
278 if (sspTransitionTarget < numberOfNonComponentStates) {
280 sspMatrixBuilder.
addNextValue(currentSspChoice, sspTransitionTarget, transition.getValue());
284 auto insertionRes = auxiliaryStateToProbabilityMap.emplace(sspTransitionTarget, transition.getValue());
285 if (!insertionRes.second) {
288 insertionRes.first->second += transition.getValue();
295 for (
auto const& componentToProbEntry : auxiliaryStateToProbabilityMap) {
296 sspMatrixBuilder.
addNextValue(currentSspChoice, componentToProbEntry.first, componentToProbEntry.second);
300template<
typename ValueType>
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();
306 std::vector<ValueType> rhs;
307 uint64_t numberOfSspStates = numberOfNonComponentStates + this->_longRunComponentDecomposition->size();
310 uint64_t currentSspChoice = 0;
311 for (
auto nonComponentState : statesNotInComponent) {
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);
319 for (uint64_t componentIndex = 0; componentIndex < this->_longRunComponentDecomposition->size(); ++componentIndex) {
320 auto const& component = (*this->_longRunComponentDecomposition)[componentIndex];
324 for (
auto const& element : component) {
326 for (uint64_t choice = choiceIndices[componentState]; choice < choiceIndices[componentState + 1]; ++choice) {
329 rhs.push_back(storm::utility::zero<ValueType>());
330 addSspMatrixChoice(choice, this->_transitionMatrix, inputToSspStateMap, numberOfNonComponentStates, currentSspChoice, sspMatrixBuilder);
331 if (sspComponentExitChoicesToOriginalMap) {
333 sspComponentExitChoicesToOriginalMap->emplace_back(componentState, choice - choiceIndices[componentState]);
340 rhs.push_back(mecLraValues[componentIndex]);
341 if (sspComponentExitChoicesToOriginalMap) {
343 sspComponentExitChoicesToOriginalMap->emplace_back(std::numeric_limits<uint_fast64_t>::max(), std::numeric_limits<uint_fast64_t>::max());
347 return std::make_pair(sspMatrixBuilder.
build(currentSspChoice, numberOfSspStates, numberOfSspStates), std::move(rhs));
350template<
typename ValueType>
354 std::vector<std::pair<uint64_t, uint64_t>>
const& sspComponentExitChoicesToOriginalMap) {
362 uint64_t exitChoiceOffset = sspMatrix.
getRowGroupIndices()[numberOfNonComponentStates];
363 for (
auto const& mec : *this->_longRunComponentDecomposition) {
365 auto const& sspState = inputToSspStateMap[mec.begin()->first];
366 uint64_t sspChoiceIndex = sspMatrix.
getRowGroupIndices()[sspState] + sspChoices[sspState];
368 auto const& originalStateChoice = sspComponentExitChoicesToOriginalMap[sspChoiceIndex - exitChoiceOffset];
370 if (originalStateChoice.first == std::numeric_limits<uint_fast64_t>::max()) {
377 this->_producedOptimalChoices.get()[originalStateChoice.first] = originalStateChoice.second;
381 for (
auto const& stateActions : mec) {
382 if (stateActions.first != originalStateChoice.first) {
383 this->_producedOptimalChoices.get()[stateActions.first] = std::numeric_limits<uint64_t>::max();
387 this->createBackwardTransitions();
389 std::vector<uint64_t> stack = {originalStateChoice.first};
390 while (!stack.empty()) {
391 uint64_t currentState = stack.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()) {
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())) {
404 selectedPredChoice = predChoice - this->_transitionMatrix.getRowGroupIndices()[predecessorState];
405 stack.push_back(predecessorState);
409 if (selectedPredChoice != std::numeric_limits<uint64_t>::max()) {
421template<
typename ValueType>
423 std::vector<ValueType>
const& componentLraValues) {
424 STORM_LOG_ASSERT(this->_longRunComponentDecomposition !=
nullptr,
"Decomposition not computed, yet.");
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]) {
437 statesInComponents.
set(state);
438 inputToSspStateMap[state] = currentComponentIndex;
442 uint64_t numberOfNonComponentStates = 0;
444 for (
auto nonComponentState : statesNotInComponent) {
445 inputToSspStateMap[nonComponentState] = numberOfNonComponentStates;
446 ++numberOfNonComponentStates;
451 for (
auto mecState : statesInComponents) {
452 inputToSspStateMap[mecState] += numberOfNonComponentStates;
457 std::vector<std::pair<uint_fast64_t, uint_fast64_t>> sspComponentExitChoicesToOriginalMap;
460 auto sspMatrixVector = buildSspMatrixVector(componentLraValues, inputToSspStateMap, statesNotInComponent, numberOfNonComponentStates,
461 this->isProduceSchedulerSet() ? &sspComponentExitChoicesToOriginalMap :
nullptr);
466 minMaxLinearEquationSolverFactory.getRequirements(env,
true,
true, this->getOptimizationDirection(),
false, this->isProduceSchedulerSet());
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();
480 std::vector<ValueType> x(sspMatrixVector.first.getRowGroupCount());
481 solver->solveEquations(env, this->getOptimizationDirection(), x, sspMatrixVector.second);
484 if (this->isProduceSchedulerSet() && solver->hasScheduler()) {
486 constructOptimalChoices(solver->getSchedulerChoices(), sspMatrixVector.first, inputToSspStateMap, statesNotInComponent, numberOfNonComponentStates,
487 sspComponentExitChoicesToOriginalMap);
489 STORM_LOG_ERROR_COND(!this->isProduceSchedulerSet(),
"Requested to produce a scheduler, but no scheduler was generated.");
494 std::vector<ValueType> result = std::move(sspMatrixVector.second);
495 result.resize(this->_transitionMatrix.getRowGroupCount());
496 result.shrink_to_fit();
SolverEnvironment & solver()
storm::RationalNumber const & getAperiodicFactor() const
storm::solver::LraMethod const & getNondetLraMethod() const
bool const & isNondetLraMethodSetFromDefault() const
bool isForceExact() const
LongRunAverageSolverEnvironment & lra()
bool isForceSoundness() const
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.
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...
storm::storage::Scheduler< ValueType > extractScheduler() const
virtual void createDecomposition() override
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.
std::vector< uint64_t > const & getProducedOptimalChoices() const
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.
bool hasEnabledCriticalRequirement() const
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.
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.
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.
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)
#define STORM_LOG_ASSERT(cond, message)
#define STORM_LOG_ERROR_COND(cond, message)
#define STORM_LOG_THROW(cond, exception, message)
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 ¤tSspChoice, 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.
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...
bool isZero(ValueType const &a)