23template<
typename ValueType>
30template<
typename ValueType>
37template<
typename ValueType>
41 "Trying to get the produced optimal choices but none were available. Was there a computation call before?");
45template<
typename ValueType>
49 "Trying to get the produced optimal choices but none were available. Was there a computation call before?");
53template<
typename ValueType>
57 for (uint64_t state = 0; state < optimalChoices.size(); ++state) {
58 scheduler.
setChoice(optimalChoices[state], state);
63template<
typename ValueType>
74template<
typename ValueType>
89 if (
auto const trivialResult = this->
computeLraForTrivialMec(env, stateRewardsGetter, actionRewardsGetter, component); trivialResult.has_value()) {
90 return trivialResult.value();
93 if (
auto const zeroResult = this->
computeLraForZeroMec(env, stateRewardsGetter, actionRewardsGetter, component); zeroResult.has_value()) {
94 return zeroResult.value();
100 method != storm::solver::LraMethod::LinearProgramming) {
102 "Selecting 'LP' as the solution technique for long-run properties to guarantee exact results. If you want to override this, please explicitly "
103 "specify a different LRA method.");
104 method = storm::solver::LraMethod::LinearProgramming;
107 "Selecting 'VI' as the solution technique for long-run properties to guarantee sound results. If you want to override this, please explicitly "
108 "specify a different LRA method.");
109 method = storm::solver::LraMethod::ValueIteration;
112 "Scheduler generation not supported for the chosen LRA method. Try value-iteration.");
113 if (method == storm::solver::LraMethod::LinearProgramming) {
115 }
else if (method == storm::solver::LraMethod::ValueIteration) {
118 STORM_LOG_THROW(
false, storm::exceptions::InvalidSettingsException,
"Unsupported technique.");
122template<
typename ValueType>
127 if (component.
size() != 1) {
131 auto const& element = *component.
begin();
137 ValueType bestValue = actionRewardsGetter(*choiceIt);
138 uint64_t bestChoice = *choiceIt;
140 ValueType currentValue = actionRewardsGetter(*choiceIt);
141 if ((this->
minimize() && currentValue < bestValue) || (this->
maximize() && currentValue > bestValue)) {
142 bestValue = std::move(currentValue);
143 bestChoice = *choiceIt;
149 bestValue += stateRewardsGetter(state);
155 "Nondeterministic continuous time model without Markovian states... Is this a not a Markov Automaton?");
157 "Markov Automaton has Zeno behavior. Computation of Long Run Average values not supported.");
162 ValueType result = stateRewardsGetter(state) +
168template<
typename ValueType>
174 bool hasNonZeroReward =
false;
175 std::map<uint64_t, std::set<uint64_t>> zeroLraStatesChoices;
176 for (
auto const& [state, choices] : component) {
177 auto const stateReward = stateRewardsGetter(state);
185 hasNonZeroReward =
true;
188 std::set<uint64_t> zeroLraChoices;
189 for (
auto const choice : choices) {
190 auto const actionReward = actionRewardsGetter(choice);
196 hasNonZeroReward =
true;
198 zeroLraChoices.insert(choice);
201 if (!zeroLraChoices.empty()) {
202 zeroLraStatesChoices.emplace(state, std::move(zeroLraChoices));
206 if (hasNonZeroReward) {
208 std::set<uint64_t> candidates;
209 for (
auto const& [state, _] : zeroLraStatesChoices) {
210 candidates.insert(state);
212 while (!candidates.empty()) {
213 uint64_t
const state = *candidates.begin();
214 candidates.erase(candidates.begin());
215 auto& choices = zeroLraStatesChoices.at(state);
217 for (
auto choiceIt = choices.begin(); choiceIt != choices.end();) {
219 if (std::all_of(row.begin(), row.end(),
220 [&zeroLraStatesChoices](
auto const& transition) { return zeroLraStatesChoices.contains(transition.getColumn()); })) {
224 choiceIt = choices.erase(choiceIt);
227 if (choices.empty()) {
229 zeroLraStatesChoices.erase(state);
231 auto const predecessor = backwardTransition.getColumn();
232 if (zeroLraStatesChoices.contains(predecessor)) {
233 candidates.insert(predecessor);
239 if (zeroLraStatesChoices.empty()) {
246 if (zeroLraStatesChoices.size() < component.
size()) {
248 std::set<uint64_t> candidates;
250 auto insertPredecessors = [&](uint64_t
const state) {
252 auto const predecessor = backwardTransition.getColumn();
253 if (!zeroLraStatesChoices.contains(predecessor) && component.
containsState(predecessor)) {
254 candidates.insert(predecessor);
258 for (
auto const& [state, _] : zeroLraStatesChoices) {
259 insertPredecessors(state);
261 while (!candidates.empty()) {
262 uint64_t
const state = *candidates.begin();
263 candidates.erase(candidates.begin());
266 if (std::any_of(row.begin(), row.end(),
267 [&zeroLraStatesChoices](
auto const& transition) { return zeroLraStatesChoices.contains(transition.getColumn()); })) {
268 zeroLraStatesChoices.emplace(state, std::set<uint64_t>{choice});
271 STORM_LOG_ASSERT(zeroLraStatesChoices.contains(state),
"No suitable choice found for state " << state);
272 insertPredecessors(state);
277 "Did not find a choice for all states: " << zeroLraStatesChoices.size() <<
" / " << component.
size() <<
".");
279 for (
auto const& [state, choices] : zeroLraStatesChoices) {
287template<
typename ValueType>
293 std::vector<uint64_t>* optimalChoices =
nullptr;
311 return viHelper.performValueIteration(env, stateRewardsGetter, actionRewardsGetter,
nullptr, &this->
getOptimizationDirection(), optimalChoices);
315template<
typename ValueType>
328 std::map<uint_fast64_t, storm::expressions::Variable> stateToVariableMap;
329 for (
auto const& stateChoicesPair : mec) {
330 std::string variableName =
"x" + std::to_string(stateChoicesPair.first);
331 stateToVariableMap[stateChoicesPair.first] =
solver->addUnboundedContinuousVariable(variableName);
337 for (
auto const& stateChoicesPair : mec) {
338 uint_fast64_t state = stateChoicesPair.first;
343 for (
auto choice : stateChoicesPair.second) {
344 std::vector<storm::expressions::Expression> summands;
346 summands.reserve(matrixRow.getNumberOfEntries() + 2);
348 if (stateIsMarkovian) {
349 summands.push_back(-(k /
solver->getManager().rational((*this->_exitRates)[state])));
351 summands.push_back(-k);
354 for (
auto const& element : matrixRow) {
355 summands.push_back(stateToVariableMap.at(element.getColumn()) *
solver->getConstant(element.getValue()));
359 if (stateIsMarkovian) {
361 value = stateRewardsGetter(state) / (*this->
_exitRates)[state] + actionRewardsGetter(choice);
364 value = stateRewardsGetter(state) + actionRewardsGetter(choice);
367 value = actionRewardsGetter(choice);
369 summands.push_back(
solver->getConstant(value));
376 solver->addConstraint(
"s" + std::to_string(state) +
"," + std::to_string(choice), constraint);
382 "Scheduler extraction is not yet implemented for LP based LRA method.");
383 return solver->getContinuousValue(k);
391template<
typename ValueType>
393 std::vector<uint64_t>
const& inputToSspStateMap, uint64_t
const& numberOfNonComponentStates, uint64_t
const& currentSspChoice,
396 std::map<uint64_t, ValueType> auxiliaryStateToProbabilityMap;
398 for (
auto const& transition : inputTransitionMatrix.
getRow(inputMatrixChoice)) {
400 auto const& sspTransitionTarget = inputToSspStateMap[transition.getColumn()];
403 if (sspTransitionTarget < numberOfNonComponentStates) {
405 sspMatrixBuilder.
addNextValue(currentSspChoice, sspTransitionTarget, transition.getValue());
409 auto insertionRes = auxiliaryStateToProbabilityMap.emplace(sspTransitionTarget, transition.getValue());
410 if (!insertionRes.second) {
413 insertionRes.first->second += transition.getValue();
420 for (
auto const& componentToProbEntry : auxiliaryStateToProbabilityMap) {
421 sspMatrixBuilder.
addNextValue(currentSspChoice, componentToProbEntry.first, componentToProbEntry.second);
425template<
typename ValueType>
427 std::vector<ValueType>
const& mecLraValues, std::vector<uint64_t>
const& inputToSspStateMap,
storm::storage::BitVector const& statesNotInComponent,
428 uint64_t numberOfNonComponentStates, std::vector<std::pair<uint64_t, uint64_t>>* sspComponentExitChoicesToOriginalMap) {
431 std::vector<ValueType> rhs;
435 uint64_t currentSspChoice = 0;
436 for (
auto nonComponentState : statesNotInComponent) {
438 for (uint64_t choice = choiceIndices[nonComponentState]; choice < choiceIndices[nonComponentState + 1]; ++choice, ++currentSspChoice) {
449 for (
auto const& element : component) {
451 for (uint64_t choice = choiceIndices[componentState]; choice < choiceIndices[componentState + 1]; ++choice) {
456 if (sspComponentExitChoicesToOriginalMap) {
458 sspComponentExitChoicesToOriginalMap->emplace_back(componentState, choice - choiceIndices[componentState]);
465 rhs.push_back(mecLraValues[componentIndex]);
466 if (sspComponentExitChoicesToOriginalMap) {
468 sspComponentExitChoicesToOriginalMap->emplace_back(std::numeric_limits<uint_fast64_t>::max(), std::numeric_limits<uint_fast64_t>::max());
472 return std::make_pair(sspMatrixBuilder.
build(currentSspChoice, numberOfSspStates, numberOfSspStates), std::move(rhs));
475template<
typename ValueType>
479 std::vector<std::pair<uint64_t, uint64_t>>
const& sspComponentExitChoicesToOriginalMap) {
487 uint64_t exitChoiceOffset = sspMatrix.
getRowGroupIndices()[numberOfNonComponentStates];
490 auto const& sspState = inputToSspStateMap[mec.begin()->first];
491 uint64_t sspChoiceIndex = sspMatrix.
getRowGroupIndices()[sspState] + sspChoices[sspState];
493 auto const& originalStateChoice = sspComponentExitChoicesToOriginalMap[sspChoiceIndex - exitChoiceOffset];
495 if (originalStateChoice.first == std::numeric_limits<uint_fast64_t>::max()) {
507 for (
auto const& stateActions : mec) {
508 if (stateActions.first != originalStateChoice.first) {
515 std::vector<uint64_t> stack = {originalStateChoice.first};
516 while (!stack.empty()) {
517 uint64_t currentState = stack.back();
520 uint64_t predecessorState = backwardsTransition.getColumn();
521 if (mec.containsState(predecessorState)) {
523 if (selectedPredChoice == std::numeric_limits<uint64_t>::max()) {
526 for (
auto const& predChoice : mec.getChoicesForState(predecessorState)) {
527 for (
auto const& forwardTransition : this->
_transitionMatrix.getRow(predChoice)) {
528 if (forwardTransition.getColumn() == currentState && !
storm::utility::isZero(forwardTransition.getValue())) {
530 selectedPredChoice = predChoice - this->
_transitionMatrix.getRowGroupIndices()[predecessorState];
531 stack.push_back(predecessorState);
535 if (selectedPredChoice != std::numeric_limits<uint64_t>::max()) {
547template<
typename ValueType>
549 std::vector<ValueType>
const& componentLraValues) {
559 std::vector<uint64_t> inputToSspStateMap(this->
_transitionMatrix.getRowGroupCount(), std::numeric_limits<uint64_t>::max());
563 statesInComponents.
set(state);
564 inputToSspStateMap[state] = currentComponentIndex;
568 uint64_t numberOfNonComponentStates = 0;
570 for (
auto nonComponentState : statesNotInComponent) {
571 inputToSspStateMap[nonComponentState] = numberOfNonComponentStates;
572 ++numberOfNonComponentStates;
577 for (
auto mecState : statesInComponents) {
578 inputToSspStateMap[mecState] += numberOfNonComponentStates;
583 std::vector<std::pair<uint_fast64_t, uint_fast64_t>> sspComponentExitChoicesToOriginalMap;
586 auto sspMatrixVector =
buildSspMatrixVector(componentLraValues, inputToSspStateMap, statesNotInComponent, numberOfNonComponentStates,
596 std::unique_ptr<storm::solver::MinMaxLinearEquationSolver<ValueType>>
solver = minMaxLinearEquationSolverFactory.
create(env, sspMatrixVector.first);
597 solver->setHasUniqueSolution();
598 solver->setHasNoEndComponents();
600 auto lowerUpperBounds = std::minmax_element(componentLraValues.begin(), componentLraValues.end());
601 solver->setLowerBound(*lowerUpperBounds.first);
602 solver->setUpperBound(*lowerUpperBounds.second);
603 solver->setRequirementsChecked();
606 std::vector<ValueType> x(sspMatrixVector.first.getRowGroupCount());
612 constructOptimalChoices(
solver->getSchedulerChoices(), sspMatrixVector.first, inputToSspStateMap, statesNotInComponent, numberOfNonComponentStates,
613 sspComponentExitChoicesToOriginalMap);
620 std::vector<ValueType> result = std::move(sspMatrixVector.second);
622 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
bool isProduceSchedulerSet() const
storm::solver::OptimizationDirection const & getOptimizationDirection() const
bool isContinuousTime() const
SparseInfiniteHorizonHelper(storm::storage::SparseMatrix< ValueType > const &transitionMatrix)
storm::storage::SparseMatrix< ValueType > const * _backwardTransitions
storm::storage::SparseMatrix< ValueType > const & _transitionMatrix
storm::storage::Decomposition< LongRunComponentType > const * _longRunComponentDecomposition
void createBackwardTransitions()
std::vector< ValueType > const * _exitRates
std::unique_ptr< storm::storage::Decomposition< LongRunComponentType > > _computedLongRunComponentDecomposition
boost::optional< std::vector< uint64_t > > _producedOptimalChoices
storm::storage::BitVector const * _markovianStates
Helper class for model checking queries that depend on the long run behavior of the (nondeterministic...
std::optional< ValueType > computeLraForTrivialMec(Environment const &env, ValueGetter const &stateValuesGetter, ValueGetter const &actionValuesGetter, storm::storage::MaximalEndComponent const &mec)
Checks if the component consists of a single state and if so, returns the value of that state.
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)
SparseNondeterministicInfiniteHorizonHelper(storm::storage::SparseMatrix< ValueType > const &transitionMatrix)
Initializes the helper for a discrete time model (i.e.
std::optional< ValueType > computeLraForZeroMec(Environment const &env, ValueGetter const &stateValuesGetter, ValueGetter const &actionValuesGetter, storm::storage::MaximalEndComponent const &mec)
Checks if we can determine via graph analysis, that the LRA value of the component is exactly zero.
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.
virtual std::unique_ptr< MinMaxLinearEquationSolver< ValueType, SolutionType > > create(Environment const &env) const override
MinMaxLinearEquationSolverRequirements getRequirements(Environment const &env, bool hasUniqueSolution=false, bool hasNoEndComponents=false, boost::optional< storm::solver::OptimizationDirection > const &direction=boost::none, bool hasInitialScheduler=false, bool trackScheduler=false) const
Retrieves the requirements of the solver that would be created when calling create() right now.
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.
bool containsState(uint_fast64_t state) const
Retrieves whether the given state is contained in this MEC.
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....
std::unique_ptr< storm::solver::LpSolver< ValueType > > getLpSolver(std::string const &name, storm::solver::LpSolverTypeSelection solvType)
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)
TargetType convertNumber(SourceType const &number)
static const bool IsExact