Browse Source
First version of sparse infinite horizon helpers for deterministic and nondeterministic models.
tempestpy_adaptions
First version of sparse infinite horizon helpers for deterministic and nondeterministic models.
tempestpy_adaptions
Tim Quatmann
5 years ago
6 changed files with 814 additions and 441 deletions
-
209src/storm/modelchecker/helper/infinitehorizon/SparseDeterministicInfiniteHorizonHelper.cpp
-
65src/storm/modelchecker/helper/infinitehorizon/SparseDeterministicInfiniteHorizonHelper.h
-
161src/storm/modelchecker/helper/infinitehorizon/SparseInfiniteHorizonHelper.cpp
-
140src/storm/modelchecker/helper/infinitehorizon/SparseInfiniteHorizonHelper.h
-
564src/storm/modelchecker/helper/infinitehorizon/SparseNondeterministicInfiniteHorizonHelper.cpp
-
116src/storm/modelchecker/helper/infinitehorizon/SparseNondeterministicInfiniteHorizonHelper.h
@ -0,0 +1,209 @@ |
|||
#include "SparseDeterministicInfiniteHorizonHelper.h"
|
|||
|
|||
#include "storm/modelchecker/helper/infinitehorizon/internal/ComponentUtility.h"
|
|||
#include "storm/modelchecker/helper/infinitehorizon/internal/LraViHelper.h"
|
|||
|
|||
|
|||
#include "storm/storage/SparseMatrix.h"
|
|||
#include "storm/storage/StronglyConnectedComponentDecomposition.h"
|
|||
#include "storm/storage/Scheduler.h"
|
|||
|
|||
#include "storm/solver/LinearEquationSolver.h"
|
|||
#include "storm/solver/Multiplier.h"
|
|||
#include "storm/solver/LpSolver.h"
|
|||
|
|||
#include "storm/utility/SignalHandler.h"
|
|||
#include "storm/utility/solver.h"
|
|||
#include "storm/utility/vector.h"
|
|||
|
|||
#include "storm/environment/solver/LongRunAverageSolverEnvironment.h"
|
|||
#include "storm/environment/solver/MinMaxSolverEnvironment.h"
|
|||
|
|||
#include "storm/exceptions/UnmetRequirementException.h"
|
|||
|
|||
namespace storm { |
|||
namespace modelchecker { |
|||
namespace helper { |
|||
|
|||
template <typename ValueType> |
|||
SparseDeterministicInfiniteHorizonHelper<ValueType>::SparseDeterministicInfiniteHorizonHelper(storm::storage::SparseMatrix<ValueType> const& transitionMatrix) : SparseInfiniteHorizonHelper<ValueType, false>(transitionMatrix) { |
|||
// Intentionally left empty.
|
|||
} |
|||
|
|||
template <typename ValueType> |
|||
SparseDeterministicInfiniteHorizonHelper<ValueType>::SparseDeterministicInfiniteHorizonHelper(storm::storage::SparseMatrix<ValueType> const& transitionMatrix, std::vector<ValueType> const& exitRates) : SparseInfiniteHorizonHelper<ValueType, false>(transitionMatrix, exitRates) { |
|||
// For the CTMC case we assert that the caller actually provided the probabilistic transitions
|
|||
STORM_LOG_ASSERT(this->_transitionMatrix.isProbabilistic(), "Non-probabilistic transitions"); |
|||
} |
|||
|
|||
template <typename ValueType> |
|||
void SparseDeterministicInfiniteHorizonHelper<ValueType>::createDecomposition() { |
|||
if (this->_longRunComponentDecomposition == nullptr) { |
|||
// The decomposition has not been provided or computed, yet.
|
|||
this->_computedLongRunComponentDecomposition = std::make_unique<storm::storage::StronglyConnectedComponentDecomposition<ValueType>>(this->_transitionMatrix, storm::storage::StronglyConnectedComponentDecompositionOptions().onlyBottomSccs()); |
|||
this->_longRunComponentDecomposition = this->_computedLongRunComponentDecomposition.get(); |
|||
} |
|||
} |
|||
|
|||
template <typename ValueType> |
|||
ValueType SparseDeterministicInfiniteHorizonHelper<ValueType>::computeLraForComponent(Environment const& env, ValueGetter const& stateRewardsGetter, ValueGetter const& actionRewardsGetter, storm::storage::StronglyConnectedComponent const& component) { |
|||
// For deterministic models, we compute the LRA for a BSCC
|
|||
|
|||
STORM_LOG_ASSERT(!this->isProduceSchedulerSet(), "Scheduler production enabled for deterministic model."); |
|||
|
|||
auto trivialResult = computeLraForTrivialBscc(env, stateRewardsGetter, actionRewardsGetter, component); |
|||
if (trivialResult.first) { |
|||
return trivialResult.second; |
|||
} |
|||
|
|||
// Solve nontrivial BSCC with the method specified in the settings
|
|||
storm::solver::LraMethod method = env.solver().lra().getDetLraMethod(); |
|||
if ((storm::NumberTraits<ValueType>::IsExact || env.solver().isForceExact()) && env.solver().lra().isDetLraMethodSetFromDefault() && method == storm::solver::LraMethod::ValueIteration) { |
|||
method = storm::solver::LraMethod::GainBiasEquations; |
|||
STORM_LOG_INFO("Selecting " << storm::solver::toString(method) << " as the solution technique for long-run properties to guarantee exact results. If you want to override this, please explicitly specify a different LRA method."); |
|||
} else if (env.solver().isForceSoundness() && env.solver().lra().isDetLraMethodSetFromDefault() && method != storm::solver::LraMethod::ValueIteration) { |
|||
method = storm::solver::LraMethod::ValueIteration; |
|||
STORM_LOG_INFO("Selecting " << storm::solver::toString(method) << " as the solution technique for long-run properties to guarantee sound results. If you want to override this, please explicitly specify a different LRA method."); |
|||
} |
|||
STORM_LOG_TRACE("Computing LRA for BSCC of size " << component.size() << " using '" << storm::solver::toString(method) << "'."); |
|||
if (method == storm::solver::LraMethod::ValueIteration) { |
|||
return computeLraForBsccVi(env, stateRewardsGetter, actionRewardsGetter, component); |
|||
}/* else if (method == storm::solver::LraMethod::LraDistributionEquations) {
|
|||
// We only need the first element of the pair as the lra distribution is not relevant at this point.
|
|||
return computeLongRunAveragesForBsccLraDistr<ValueType>(env, bscc, rateMatrix, valueGetter, exitRateVector).first; |
|||
} |
|||
STORM_LOG_WARN_COND(method == storm::solver::LraMethod::GainBiasEquations, "Unsupported lra method selected. Defaulting to " << storm::solver::toString(storm::solver::LraMethod::GainBiasEquations) << "."); |
|||
// We don't need the bias values
|
|||
return computeLongRunAveragesForBsccGainBias<ValueType>(env, bscc, rateMatrix, valueGetter, exitRateVector).first;*/ |
|||
} |
|||
|
|||
template <typename ValueType> |
|||
std::pair<bool, ValueType> SparseDeterministicInfiniteHorizonHelper<ValueType>::computeLraForTrivialBscc(Environment const& env, ValueGetter const& stateRewardsGetter, ValueGetter const& actionRewardsGetter, storm::storage::StronglyConnectedComponent const& component) { |
|||
|
|||
// For deterministic models, we can catch the case where all values are the same. This includes the special case where the BSCC consist only of just one state.
|
|||
bool first = true; |
|||
ValueType val = storm::utility::zero<ValueType>(); |
|||
for (auto const& element : component) { |
|||
auto state = internal::getComponentElementState(element); |
|||
STORM_LOG_ASSERT(state == *internal::getComponentElementChoicesBegin(element), "Unexpected choice index at state " << state << " of deterministic model."); |
|||
ValueType curr = stateRewardsGetter(state) + (this->isContinuousTime() ? (*this->_exitRates)[state] * actionRewardsGetter(state) : actionRewardsGetter(state)); |
|||
if (first) { |
|||
first = false; |
|||
} else if (val != curr) { |
|||
return {false, storm::utility::zero<ValueType>()}; |
|||
} |
|||
} |
|||
// All values are the same
|
|||
return {true, val}; |
|||
} |
|||
|
|||
|
|||
template <typename ValueType> |
|||
ValueType SparseDeterministicInfiniteHorizonHelper<ValueType>::computeLraForBsccVi(Environment const& env, ValueGetter const& stateRewardsGetter, ValueGetter const& actionRewardsGetter, storm::storage::StronglyConnectedComponent const& bscc) { |
|||
|
|||
// Collect parameters of the computation
|
|||
ValueType aperiodicFactor = storm::utility::convertNumber<ValueType>(env.solver().lra().getAperiodicFactor()); |
|||
|
|||
// Now create a helper and perform the algorithm
|
|||
if (this->isContinuousTime()) { |
|||
// We assume a CTMC (with deterministic timed states and no instant states)
|
|||
storm::modelchecker::helper::internal::LraViHelper<ValueType, storm::storage::StronglyConnectedComponent, storm::modelchecker::helper::internal::LraViTransitionsType::DetTsNoIs> viHelper(bscc, this->_transitionMatrix, aperiodicFactor, this->_markovianStates, this->_exitRates); |
|||
return viHelper.performValueIteration(env, stateRewardsGetter, actionRewardsGetter, this->_exitRates); |
|||
} else { |
|||
// We assume a DTMC (with deterministic timed states and no instant states)
|
|||
storm::modelchecker::helper::internal::LraViHelper<ValueType, storm::storage::StronglyConnectedComponent, storm::modelchecker::helper::internal::LraViTransitionsType::DetTsNoIs> viHelper(bscc, this->_transitionMatrix, aperiodicFactor); |
|||
return viHelper.performValueIteration(env, stateRewardsGetter, actionRewardsGetter); |
|||
} |
|||
} |
|||
|
|||
template <typename ValueType> |
|||
std::pair<storm::storage::SparseMatrix<ValueType>, std::vector<ValueType>> SparseDeterministicInfiniteHorizonHelper<ValueType>::buildSspMatrixVector(std::vector<ValueType> const& bsccLraValues, std::vector<uint64_t> const& inputStateToBsccIndexMap, storm::storage::BitVector const& statesNotInComponent, bool asEquationSystem) { |
|||
|
|||
// Create SSP Matrix.
|
|||
// In contrast to the version for nondeterministic models, we eliminate the auxiliary states representing each BSCC on the fly
|
|||
|
|||
// Probability mass that would lead to a BSCC will be considered in the rhs of the equation system
|
|||
auto sspMatrix = this->_transitionMatrix.getSubmatrix(false, statesNotInComponent, statesNotInComponent, asEquationSystem); |
|||
if (asEquationSystem) { |
|||
sspMatrix.convertToEquationSystem(); |
|||
} |
|||
|
|||
// Create the SSP right-hand-side
|
|||
std::vector<ValueType> rhs; |
|||
rhs.reserve(sspMatrix.getRowCount()); |
|||
for (auto const& state : statesNotInComponent) { |
|||
ValueType stateValue = storm::utility::zero<ValueType>(); |
|||
for (auto const& transition : this->_transitionMatrix.getRow(state)) { |
|||
if (!statesNotInComponent.get(transition.getColumn())) { |
|||
// This transition leads to a BSCC!
|
|||
stateValue += transition.getValue() * bsccLraValues[inputStateToBsccIndexMap[transition.getColumn()]]; |
|||
} |
|||
} |
|||
rhs.push_back(std::move(stateValue)); |
|||
} |
|||
|
|||
return std::make_pair(std::move(sspMatrix), std::move(rhs)); |
|||
} |
|||
|
|||
template <typename ValueType> |
|||
std::vector<ValueType> SparseDeterministicInfiniteHorizonHelper<ValueType>::buildAndSolveSsp(Environment const& env, std::vector<ValueType> const& componentLraValues) { |
|||
STORM_LOG_ASSERT(this->_longRunComponentDecomposition != nullptr, "Decomposition not computed, yet."); |
|||
|
|||
// For fast transition rewriting, we build a mapping from the input state indices to the state indices of a new transition matrix
|
|||
// which redirects all transitions leading to a former BSCC state to a new (imaginary) auxiliary state.
|
|||
// Each auxiliary state gets assigned the value of that BSCC and we compute expected rewards (aka stochastic shortest path, SSP) on that new system.
|
|||
// For efficiency reasons, we actually build the system where the auxiliary states are already eliminated.
|
|||
|
|||
// First gather the states that are part of a component
|
|||
// and create a mapping from states that lie in a component to the corresponding component index.
|
|||
storm::storage::BitVector statesInComponents(this->_transitionMatrix.getRowGroupCount()); |
|||
std::vector<uint64_t> stateIndexMap(this->_transitionMatrix.getRowGroupCount(), std::numeric_limits<uint64_t>::max()); |
|||
for (uint64_t currentComponentIndex = 0; currentComponentIndex < this->_longRunComponentDecomposition->size(); ++currentComponentIndex) { |
|||
for (auto const& element : (*this->_longRunComponentDecomposition)[currentComponentIndex]) { |
|||
uint64_t state = internal::getComponentElementState(element); |
|||
statesInComponents.set(state); |
|||
stateIndexMap[state] = currentComponentIndex; |
|||
} |
|||
} |
|||
// Map the non-component states to their index in the SSP. Note that the order of these states will be preserved.
|
|||
uint64_t numberOfNonComponentStates = 0; |
|||
storm::storage::BitVector statesNotInComponent = ~statesInComponents; |
|||
for (auto const& nonComponentState : statesNotInComponent) { |
|||
stateIndexMap[nonComponentState] = numberOfNonComponentStates; |
|||
++numberOfNonComponentStates; |
|||
} |
|||
|
|||
// The next step is to create the equation system solving the SSP (unless the whole system consists of BSCCs)
|
|||
std::vector<ValueType> sspValues; |
|||
if (numberOfNonComponentStates > 0) { |
|||
storm::solver::GeneralLinearEquationSolverFactory<ValueType> linearEquationSolverFactory; |
|||
bool isEqSysFormat = linearEquationSolverFactory.getEquationProblemFormat(env) == storm::solver::LinearEquationSolverProblemFormat::EquationSystem; |
|||
auto sspMatrixVector = buildSspMatrixVector(componentLraValues, stateIndexMap, statesNotInComponent, isEqSysFormat); |
|||
std::unique_ptr<storm::solver::LinearEquationSolver<ValueType>> solver = linearEquationSolverFactory.create(env, sspMatrixVector.first); |
|||
auto lowerUpperBounds = std::minmax_element(componentLraValues.begin(), componentLraValues.end()); |
|||
solver->setBounds(*lowerUpperBounds.first, *lowerUpperBounds.second); |
|||
// Check solver requirements
|
|||
auto requirements = solver->getRequirements(env); |
|||
STORM_LOG_THROW(!requirements.hasEnabledCriticalRequirement(), storm::exceptions::UnmetRequirementException, "Solver requirements " + requirements.getEnabledRequirementsAsString() + " not checked."); |
|||
sspValues.assign(sspMatrixVector.first.getRowCount(), (*lowerUpperBounds.first + *lowerUpperBounds.second) / storm::utility::convertNumber<ValueType,uint64_t>(2)); |
|||
solver->solveEquations(env, sspValues, sspMatrixVector.second); |
|||
} |
|||
|
|||
// Prepare result vector.
|
|||
std::vector<ValueType> result(this->_transitionMatrix.getRowGroupCount()); |
|||
for (uint64_t state = 0; state < stateIndexMap.size(); ++state) { |
|||
if (statesNotInComponent.get(state)) { |
|||
result[state] = sspValues[stateIndexMap[state]]; |
|||
} else { |
|||
result[state] = componentLraValues[stateIndexMap[state]]; |
|||
} |
|||
} |
|||
return result; |
|||
} |
|||
|
|||
template class SparseDeterministicInfiniteHorizonHelper<double>; |
|||
template class SparseDeterministicInfiniteHorizonHelper<storm::RationalNumber>; |
|||
|
|||
} |
|||
} |
|||
} |
@ -0,0 +1,65 @@ |
|||
#pragma once |
|||
#include "storm/modelchecker/helper/infinitehorizon/SparseInfiniteHorizonHelper.h" |
|||
|
|||
|
|||
namespace storm { |
|||
|
|||
namespace modelchecker { |
|||
namespace helper { |
|||
|
|||
/*! |
|||
* Helper class for model checking queries that depend on the long run behavior of the (nondeterministic) system. |
|||
* @tparam ValueType the type a value can have |
|||
*/ |
|||
template <typename ValueType> |
|||
class SparseDeterministicInfiniteHorizonHelper : public SparseInfiniteHorizonHelper<ValueType, false> { |
|||
|
|||
public: |
|||
/*! |
|||
* Function mapping from indices to values |
|||
*/ |
|||
typedef typename SparseInfiniteHorizonHelper<ValueType, true>::ValueGetter ValueGetter; |
|||
|
|||
/*! |
|||
* Initializes the helper for a discrete time model (i.e. DTMC) |
|||
*/ |
|||
SparseDeterministicInfiniteHorizonHelper(storm::storage::SparseMatrix<ValueType> const& transitionMatrix); |
|||
|
|||
/*! |
|||
* Initializes the helper for a continuous time model (i.e. CTMC) |
|||
* @note The transition matrix shall be probabilistic (i.e. the rows sum up to one) |
|||
*/ |
|||
SparseDeterministicInfiniteHorizonHelper(storm::storage::SparseMatrix<ValueType> const& transitionMatrix, std::vector<ValueType> const& exitRates); |
|||
|
|||
/*! |
|||
* @param stateValuesGetter a function returning a value for a given state index |
|||
* @param actionValuesGetter a function returning a value for a given (global) choice index |
|||
* @return the (unique) optimal LRA value for the given component. |
|||
* @post if scheduler production is enabled and Nondeterministic is true, getProducedOptimalChoices() contains choices for the states of the given component which yield the returned LRA value. Choices for states outside of the component are not affected. |
|||
*/ |
|||
virtual ValueType computeLraForComponent(Environment const& env, ValueGetter const& stateValuesGetter, ValueGetter const& actionValuesGetter, storm::storage::StronglyConnectedComponent const& component) override; |
|||
|
|||
protected: |
|||
|
|||
virtual void createDecomposition() override; |
|||
|
|||
std::pair<bool, ValueType> computeLraForTrivialBscc(Environment const& env, ValueGetter const& stateValuesGetter, ValueGetter const& actionValuesGetter, storm::storage::StronglyConnectedComponent const& bscc); |
|||
|
|||
/*! |
|||
* As computeLraForMec but uses value iteration as a solution method (independent of what is set in env) |
|||
*/ |
|||
ValueType computeLraForBsccVi(Environment const& env, ValueGetter const& stateValuesGetter, ValueGetter const& actionValuesGetter, storm::storage::StronglyConnectedComponent const& bscc); |
|||
|
|||
std::pair<storm::storage::SparseMatrix<ValueType>, std::vector<ValueType>> buildSspMatrixVector(std::vector<ValueType> const& bsccLraValues, std::vector<uint64_t> const& inputStateToBsccIndexMap, storm::storage::BitVector const& statesNotInComponent, bool asEquationSystem); |
|||
|
|||
/*! |
|||
* @return Lra values for each state |
|||
*/ |
|||
virtual std::vector<ValueType> buildAndSolveSsp(Environment const& env, std::vector<ValueType> const& mecLraValues) override; |
|||
|
|||
}; |
|||
|
|||
|
|||
} |
|||
} |
|||
} |
@ -0,0 +1,161 @@ |
|||
#include "SparseInfiniteHorizonHelper.h"
|
|||
|
|||
#include "storm/modelchecker/helper/infinitehorizon/internal/ComponentUtility.h"
|
|||
#include "storm/modelchecker/helper/infinitehorizon/internal/LraViHelper.h"
|
|||
|
|||
#include "storm/models/sparse/StandardRewardModel.h"
|
|||
|
|||
#include "storm/storage/SparseMatrix.h"
|
|||
#include "storm/storage/MaximalEndComponentDecomposition.h"
|
|||
#include "storm/storage/StronglyConnectedComponentDecomposition.h"
|
|||
|
|||
#include "storm/solver/MinMaxLinearEquationSolver.h"
|
|||
#include "storm/solver/LinearEquationSolver.h"
|
|||
#include "storm/solver/Multiplier.h"
|
|||
#include "storm/solver/LpSolver.h"
|
|||
|
|||
#include "storm/utility/SignalHandler.h"
|
|||
#include "storm/utility/solver.h"
|
|||
#include "storm/utility/vector.h"
|
|||
|
|||
#include "storm/environment/solver/LongRunAverageSolverEnvironment.h"
|
|||
#include "storm/environment/solver/MinMaxSolverEnvironment.h"
|
|||
|
|||
#include "storm/exceptions/UnmetRequirementException.h"
|
|||
|
|||
namespace storm { |
|||
namespace modelchecker { |
|||
namespace helper { |
|||
|
|||
template <typename ValueType, bool Nondeterministic> |
|||
SparseInfiniteHorizonHelper<ValueType, Nondeterministic>::SparseInfiniteHorizonHelper(storm::storage::SparseMatrix<ValueType> const& transitionMatrix) : _transitionMatrix(transitionMatrix), _markovianStates(nullptr), _exitRates(nullptr), _backwardTransitions(nullptr), _longRunComponentDecomposition(nullptr) { |
|||
// Intentionally left empty.
|
|||
} |
|||
|
|||
template <typename ValueType, bool Nondeterministic> |
|||
SparseInfiniteHorizonHelper<ValueType, Nondeterministic>::SparseInfiniteHorizonHelper(storm::storage::SparseMatrix<ValueType> const& transitionMatrix, storm::storage::BitVector const& markovianStates, std::vector<ValueType> const& exitRates) : _transitionMatrix(transitionMatrix), _markovianStates(&markovianStates), _exitRates(&exitRates), _backwardTransitions(nullptr), _longRunComponentDecomposition(nullptr) { |
|||
// Intentionally left empty.
|
|||
} |
|||
|
|||
template <typename ValueType, bool Nondeterministic> |
|||
SparseInfiniteHorizonHelper<ValueType, Nondeterministic>::SparseInfiniteHorizonHelper(storm::storage::SparseMatrix<ValueType> const& transitionMatrix, std::vector<ValueType> const& exitRates) : _transitionMatrix(transitionMatrix), _markovianStates(nullptr), _exitRates(&exitRates), _backwardTransitions(nullptr), _longRunComponentDecomposition(nullptr) { |
|||
// Intentionally left empty.
|
|||
} |
|||
|
|||
template <typename ValueType, bool Nondeterministic> |
|||
void SparseInfiniteHorizonHelper<ValueType, Nondeterministic>::provideBackwardTransitions(storm::storage::SparseMatrix<ValueType> const& backwardTransitions) { |
|||
STORM_LOG_WARN_COND(_backwardTransitions == nullptr, "Backwards transitions were provided but they were already computed or provided before."); |
|||
_backwardTransitions = &backwardTransitions; |
|||
} |
|||
|
|||
template <typename ValueType, bool Nondeterministic> |
|||
void SparseInfiniteHorizonHelper<ValueType, Nondeterministic>::provideLongRunComponentDecomposition(storm::storage::Decomposition<LongRunComponentType> const& decomposition) { |
|||
STORM_LOG_WARN_COND(_longRunComponentDecomposition == nullptr, "Long Run Component Decomposition was provided but it was already computed or provided before."); |
|||
_longRunComponentDecomposition = &decomposition; |
|||
} |
|||
|
|||
template <typename ValueType, bool Nondeterministic> |
|||
std::vector<ValueType> SparseInfiniteHorizonHelper<ValueType, Nondeterministic>::computeLongRunAverageProbabilities(Environment const& env, storm::storage::BitVector const& psiStates) { |
|||
return computeLongRunAverageValues(env, |
|||
[&psiStates] (uint64_t stateIndex) { return psiStates.get(stateIndex) ? storm::utility::one<ValueType>() : storm::utility::zero<ValueType>(); }, |
|||
[] (uint64_t) { return storm::utility::zero<ValueType>(); } |
|||
); |
|||
} |
|||
|
|||
template <typename ValueType, bool Nondeterministic> |
|||
std::vector<ValueType> SparseInfiniteHorizonHelper<ValueType, Nondeterministic>::computeLongRunAverageRewards(Environment const& env, storm::models::sparse::StandardRewardModel<ValueType> const& rewardModel) { |
|||
ValueGetter stateRewardsGetter; |
|||
if (rewardModel.hasStateRewards()) { |
|||
stateRewardsGetter = [&rewardModel] (uint64_t stateIndex) { return rewardModel.getStateReward(stateIndex); }; |
|||
} else { |
|||
stateRewardsGetter = [] (uint64_t) { return storm::utility::zero<ValueType>(); }; |
|||
} |
|||
ValueGetter actionRewardsGetter; |
|||
if (rewardModel.hasStateActionRewards() || rewardModel.hasTransitionRewards()) { |
|||
if (rewardModel.hasTransitionRewards()) { |
|||
actionRewardsGetter = [&] (uint64_t globalChoiceIndex) { return rewardModel.getStateActionAndTransitionReward(globalChoiceIndex, this->_transitionMatrix); }; |
|||
} else { |
|||
actionRewardsGetter = [&] (uint64_t globalChoiceIndex) { return rewardModel.getStateActionReward(globalChoiceIndex); }; |
|||
} |
|||
} else { |
|||
stateRewardsGetter = [] (uint64_t) { return storm::utility::zero<ValueType>(); }; |
|||
} |
|||
|
|||
return computeLongRunAverageValues(env, stateRewardsGetter, actionRewardsGetter); |
|||
} |
|||
|
|||
template <typename ValueType, bool Nondeterministic> |
|||
std::vector<ValueType> SparseInfiniteHorizonHelper<ValueType, Nondeterministic>::computeLongRunAverageValues(Environment const& env, std::vector<ValueType> const* stateValues, std::vector<ValueType> const* actionValues) { |
|||
ValueGetter stateValuesGetter; |
|||
if (stateValues) { |
|||
stateValuesGetter = [&stateValues] (uint64_t stateIndex) { return (*stateValues)[stateIndex]; }; |
|||
} else { |
|||
stateValuesGetter = [] (uint64_t) { return storm::utility::zero<ValueType>(); }; |
|||
} |
|||
ValueGetter actionValuesGetter; |
|||
if (actionValues) { |
|||
actionValuesGetter = [&actionValues] (uint64_t globalChoiceIndex) { return (*actionValues)[globalChoiceIndex]; }; |
|||
} else { |
|||
actionValuesGetter = [] (uint64_t) { return storm::utility::zero<ValueType>(); }; |
|||
} |
|||
|
|||
return computeLongRunAverageValues(env, stateValuesGetter, actionValuesGetter); |
|||
|
|||
} |
|||
|
|||
|
|||
template <typename ValueType, bool Nondeterministic> |
|||
std::vector<ValueType> SparseInfiniteHorizonHelper<ValueType, Nondeterministic>::computeLongRunAverageValues(Environment const& env, ValueGetter const& stateRewardsGetter, ValueGetter const& actionRewardsGetter) { |
|||
// We will compute the long run average value for each MEC individually and then set-up an Equation system to compute the value also at non-mec states.
|
|||
// For a description of this approach see, e.g., Guck et al.: Modelling and Analysis of Markov Reward Automata (ATVA'14), https://doi.org/10.1007/978-3-319-11936-6_13
|
|||
|
|||
// Prepare an environment for the underlying solvers
|
|||
auto underlyingSolverEnvironment = env; |
|||
if (env.solver().isForceSoundness()) { |
|||
// For sound computations, the error in the MECS plus the error in the remaining system should not exceed the user defined precsion.
|
|||
storm::RationalNumber newPrecision = env.solver().lra().getPrecision() / storm::utility::convertNumber<storm::RationalNumber>(2); |
|||
underlyingSolverEnvironment.solver().minMax().setPrecision(newPrecision); |
|||
underlyingSolverEnvironment.solver().minMax().setRelativeTerminationCriterion(env.solver().lra().getRelativeTerminationCriterion()); |
|||
underlyingSolverEnvironment.solver().setLinearEquationSolverPrecision(newPrecision, env.solver().lra().getRelativeTerminationCriterion()); |
|||
underlyingSolverEnvironment.solver().lra().setPrecision(newPrecision); |
|||
} |
|||
|
|||
// If requested, allocate memory for the choices made
|
|||
if (Nondeterministic && this->isProduceSchedulerSet()) { |
|||
if (!_producedOptimalChoices.is_initialized()) { |
|||
_producedOptimalChoices.emplace(); |
|||
} |
|||
_producedOptimalChoices->resize(_transitionMatrix.getRowGroupCount()); |
|||
} |
|||
STORM_LOG_ASSERT(Nondeterministic || !this->isProduceSchedulerSet(), "Scheduler production enabled for deterministic model."); |
|||
|
|||
// Decompose the model to their bottom components (MECS or BSCCS)
|
|||
createDecomposition(); |
|||
|
|||
// Compute the long-run average for all components in isolation.
|
|||
std::vector<ValueType> componentLraValues; |
|||
componentLraValues.reserve(_longRunComponentDecomposition->size()); |
|||
for (auto const& c : *_longRunComponentDecomposition) { |
|||
componentLraValues.push_back(computeLraForComponent(underlyingSolverEnvironment, stateRewardsGetter, actionRewardsGetter, c)); |
|||
} |
|||
|
|||
// Solve the resulting SSP where end components are collapsed into single auxiliary states
|
|||
return buildAndSolveSsp(underlyingSolverEnvironment, componentLraValues); |
|||
} |
|||
|
|||
template <typename ValueType, bool Nondeterministic> |
|||
bool SparseInfiniteHorizonHelper<ValueType, Nondeterministic>::isContinuousTime() const { |
|||
STORM_LOG_ASSERT((_markovianStates == nullptr) || (_exitRates != nullptr), "Inconsistent information given: Have Markovian states but no exit rates." ); |
|||
return _exitRates != nullptr; |
|||
} |
|||
|
|||
template class SparseInfiniteHorizonHelper<double, true>; |
|||
template class SparseInfiniteHorizonHelper<storm::RationalNumber, true>; |
|||
template class SparseInfiniteHorizonHelper<storm::RationalFunction, true>; |
|||
|
|||
template class SparseInfiniteHorizonHelper<double, false>; |
|||
template class SparseInfiniteHorizonHelper<storm::RationalNumber, false>; |
|||
|
|||
} |
|||
} |
|||
} |
@ -0,0 +1,140 @@ |
|||
#pragma once |
|||
#include "storm/modelchecker/helper/SingleValueModelCheckerHelper.h" |
|||
|
|||
#include "storm/storage/MaximalEndComponent.h" |
|||
#include "storm/storage/StronglyConnectedComponent.h" |
|||
#include "storm/storage/Decomposition.h" |
|||
#include "storm/storage/SparseMatrix.h" |
|||
|
|||
namespace storm { |
|||
class Environment; |
|||
|
|||
namespace models { |
|||
namespace sparse { |
|||
template <typename VT> class StandardRewardModel; |
|||
} |
|||
} |
|||
|
|||
namespace modelchecker { |
|||
namespace helper { |
|||
|
|||
/*! |
|||
* Helper class for model checking queries that depend on the long run behavior of the (nondeterministic) system. |
|||
* @tparam ValueType the type a value can have |
|||
* @tparam Nondeterministic true if there is nondeterminism in the Model (MDP or MA) |
|||
*/ |
|||
template <typename ValueType, bool Nondeterministic> |
|||
class SparseInfiniteHorizonHelper : public SingleValueModelCheckerHelper<ValueType> { |
|||
|
|||
public: |
|||
|
|||
/*! |
|||
* The type of a component in which the system resides in the long run (BSCC for deterministic models, MEC for nondeterministic models) |
|||
*/ |
|||
using LongRunComponentType = typename std::conditional<Nondeterministic, storm::storage::MaximalEndComponent, storm::storage::StronglyConnectedComponent>::type; |
|||
|
|||
/*! |
|||
* Function mapping from indices to values |
|||
*/ |
|||
typedef std::function<ValueType(uint64_t)> ValueGetter; |
|||
|
|||
/*! |
|||
* Initializes the helper for a discrete time (i.e. DTMC, MDP) |
|||
*/ |
|||
SparseInfiniteHorizonHelper(storm::storage::SparseMatrix<ValueType> const& transitionMatrix); |
|||
|
|||
/*! |
|||
* Initializes the helper for continuous time (i.e. MA) |
|||
*/ |
|||
SparseInfiniteHorizonHelper(storm::storage::SparseMatrix<ValueType> const& transitionMatrix, storm::storage::BitVector const& markovianStates, std::vector<ValueType> const& exitRates); |
|||
|
|||
/*! |
|||
* Initializes the helper for continuous time (i.e. CTMC) |
|||
*/ |
|||
SparseInfiniteHorizonHelper(storm::storage::SparseMatrix<ValueType> const& transitionMatrix, std::vector<ValueType> const& exitRates); |
|||
|
|||
/*! |
|||
* Provides backward transitions that can be used during the computation. |
|||
* Providing them is optional. If they are not provided, they will be computed internally |
|||
* Be aware that this class does not take ownership, i.e. the caller has to make sure that the reference to the backwardstransitions remains valid. |
|||
*/ |
|||
void provideBackwardTransitions(storm::storage::SparseMatrix<ValueType> const& backwardsTransitions); |
|||
|
|||
/*! |
|||
* Provides the decomposition into long run components (BSCCs/MECs) that can be used during the computation. |
|||
* Providing the decomposition is optional. If it is not provided, they will be computed internally. |
|||
* Be aware that this class does not take ownership, i.e. the caller has to make sure that the reference to the decomposition remains valid. |
|||
*/ |
|||
void provideLongRunComponentDecomposition(storm::storage::Decomposition<LongRunComponentType> const& decomposition); |
|||
|
|||
/*! |
|||
* Computes the long run average probabilities, i.e., the fraction of the time we are in a psiState |
|||
* @return a value for each state |
|||
*/ |
|||
std::vector<ValueType> computeLongRunAverageProbabilities(Environment const& env, storm::storage::BitVector const& psiStates); |
|||
|
|||
/*! |
|||
* Computes the long run average rewards, i.e., the average reward collected per time unit |
|||
* @return a value for each state |
|||
*/ |
|||
std::vector<ValueType> computeLongRunAverageRewards(Environment const& env, storm::models::sparse::StandardRewardModel<ValueType> const& rewardModel); |
|||
|
|||
/*! |
|||
* Computes the long run average value given the provided state and action-based rewards. |
|||
* @param stateValues a vector containing a value for every state |
|||
* @param actionValues a vector containing a value for every choice |
|||
* @return a value for each state |
|||
*/ |
|||
std::vector<ValueType> computeLongRunAverageValues(Environment const& env, std::vector<ValueType> const* stateValues = nullptr, std::vector<ValueType> const* actionValues = nullptr); |
|||
|
|||
/*! |
|||
* Computes the long run average value given the provided state and action based rewards |
|||
* @param stateValuesGetter a function returning a value for a given state index |
|||
* @param actionValuesGetter a function returning a value for a given (global) choice index |
|||
* @return a value for each state |
|||
*/ |
|||
std::vector<ValueType> computeLongRunAverageValues(Environment const& env, ValueGetter const& stateValuesGetter, ValueGetter const& actionValuesGetter); |
|||
|
|||
/*! |
|||
* @param stateValuesGetter a function returning a value for a given state index |
|||
* @param actionValuesGetter a function returning a value for a given (global) choice index |
|||
* @return the (unique) optimal LRA value for the given component. |
|||
* @post if scheduler production is enabled and Nondeterministic is true, getProducedOptimalChoices() contains choices for the states of the given component which yield the returned LRA value. Choices for states outside of the component are not affected. |
|||
*/ |
|||
virtual ValueType computeLraForComponent(Environment const& env, ValueGetter const& stateValuesGetter, ValueGetter const& actionValuesGetter, LongRunComponentType const& component) = 0; |
|||
|
|||
protected: |
|||
|
|||
/*! |
|||
* @return true iff this is a computation on a continuous time model (i.e. CTMC, MA) |
|||
*/ |
|||
bool isContinuousTime() const; |
|||
|
|||
/*! |
|||
* @post _longRunComponentDecomposition points to a decomposition of the long run components (MECs, BSCCs) |
|||
*/ |
|||
virtual void createDecomposition() = 0; |
|||
|
|||
/*! |
|||
* @pre if scheduler production is enabled and Nondeterministic is true, a choice for each state within a component must be set such that the choices yield optimal values w.r.t. the individual components. |
|||
* @return Lra values for each state |
|||
* @post if scheduler production is enabled and Nondeterministic is true, getProducedOptimalChoices() contains choices for all input model states which yield the returned LRA values. |
|||
*/ |
|||
virtual std::vector<ValueType> buildAndSolveSsp(Environment const& env, std::vector<ValueType> const& mecLraValues) = 0; |
|||
|
|||
storm::storage::SparseMatrix<ValueType> const& _transitionMatrix; |
|||
storm::storage::BitVector const* _markovianStates; |
|||
std::vector<ValueType> const* _exitRates; |
|||
|
|||
storm::storage::SparseMatrix<ValueType> const* _backwardTransitions; |
|||
storm::storage::Decomposition<LongRunComponentType> const* _longRunComponentDecomposition; |
|||
std::unique_ptr<storm::storage::SparseMatrix<ValueType>> _computedBackwardTransitions; |
|||
std::unique_ptr<storm::storage::Decomposition<LongRunComponentType>> _computedLongRunComponentDecomposition; |
|||
|
|||
boost::optional<std::vector<uint64_t>> _producedOptimalChoices; |
|||
}; |
|||
|
|||
|
|||
} |
|||
} |
|||
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue