20namespace modelchecker {
24template<
typename ValueType,
typename ComponentType, LraViTransitionsType TransitionsType>
28 std::vector<ValueType>
const* exitRates)
29 : _transitionMatrix(transitionMatrix),
30 _timedStates(timedStates),
32 _Tsx1IsCurrent(false) {
33 setComponent(component);
38 std::map<uint64_t, uint64_t> toSubModelStateMapping;
40 uint64_t numTsSubModelStates(0), numTsSubModelChoices(0);
41 uint64_t numIsSubModelStates(0), numIsSubModelChoices(0);
45 _uniformizationRate = exitRates ==
nullptr ? storm::utility::one<ValueType>() : storm::utility::zero<ValueType>();
47 for (
auto const& element : _component) {
48 uint64_t componentState = element.first;
49 if (isTimedState(componentState)) {
50 toSubModelStateMapping.emplace(componentState, numTsSubModelStates);
51 ++numTsSubModelStates;
52 numTsSubModelChoices += element.second.size();
53 STORM_LOG_ASSERT(nondetTs() || element.second.size() == 1,
"Timed state has multiple choices but only a single choice was expected.");
55 _uniformizationRate = std::max(_uniformizationRate, (*exitRates)[componentState]);
58 toSubModelStateMapping.emplace(componentState, numIsSubModelStates);
59 ++numIsSubModelStates;
60 numIsSubModelChoices += element.second.size();
61 STORM_LOG_ASSERT(nondetIs() || element.second.size() == 1,
"Instant state has multiple choices but only a single choice was expected.");
64 assert(numIsSubModelStates + numTsSubModelStates == _component.size());
65 assert(_hasInstantStates || numIsSubModelStates == 0);
66 STORM_LOG_ASSERT(nondetTs() || numTsSubModelStates == numTsSubModelChoices,
"Unexpected choice count of deterministic timed submodel.");
67 STORM_LOG_ASSERT(nondetIs() || numIsSubModelStates == numIsSubModelChoices,
"Unexpected choice count of deterministic instant submodel.");
68 _hasInstantStates = _hasInstantStates && numIsSubModelStates > 0;
70 numTsSubModelStates > 0, storm::exceptions::InvalidOperationException,
71 "Bottom Component has no timed states. Computation of Long Run Average values not supported. Is this a Markov Automaton with Zeno behavior?");
74 _uniformizationRate *= storm::utility::one<ValueType>() + aperiodicFactor;
79 nondetTs() ? numTsSubModelStates : 0);
81 if (_hasInstantStates) {
83 nondetTs() ? numTsSubModelStates : 0);
85 nondetIs() ? numIsSubModelStates : 0);
87 nondetIs() ? numIsSubModelStates : 0);
88 _IsChoiceValues.reserve(numIsSubModelChoices);
90 ValueType uniformizationFactor = storm::utility::one<ValueType>() / _uniformizationRate;
91 uint64_t currTsRow = 0;
92 uint64_t currIsRow = 0;
93 for (
auto const& element : _component) {
94 uint64_t componentState = element.first;
95 if (isTimedState(componentState)) {
99 if (_hasInstantStates) {
105 uniformizationFactor = (*exitRates)[componentState] / _uniformizationRate;
108 ValueType selfLoopProb = storm::utility::one<ValueType>() - uniformizationFactor;
109 for (
auto const& componentChoice : element.second) {
111 for (
auto const& entry : this->_transitionMatrix.getRow(componentChoice)) {
112 uint64_t subModelColumn = toSubModelStateMapping[entry.getColumn()];
113 if (isTimedState(entry.getColumn())) {
115 STORM_LOG_ASSERT(subModelColumn < numTsSubModelStates,
"Invalid state for timed submodel");
116 tsTransitionsBuilder.
addNextValue(currTsRow, subModelColumn, uniformizationFactor * entry.getValue());
119 STORM_LOG_ASSERT(subModelColumn < numIsSubModelStates,
"Invalid state for instant submodel");
120 tsToIsTransitionsBuilder.
addNextValue(currTsRow, subModelColumn, uniformizationFactor * entry.getValue());
131 for (
auto const& componentChoice : element.second) {
132 for (
auto const& entry : this->_transitionMatrix.getRow(componentChoice)) {
133 uint64_t subModelColumn = toSubModelStateMapping[entry.getColumn()];
134 if (isTimedState(entry.getColumn())) {
136 STORM_LOG_ASSERT(subModelColumn < numTsSubModelStates,
"Invalid state for timed submodel");
137 isToTsTransitionsBuilder.
addNextValue(currIsRow, subModelColumn, entry.getValue());
140 STORM_LOG_ASSERT(subModelColumn < numIsSubModelStates,
"Invalid state for instant submodel");
141 isTransitionsBuilder.
addNextValue(currIsRow, subModelColumn, entry.getValue());
148 _TsTransitions = tsTransitionsBuilder.
build();
149 if (_hasInstantStates) {
150 _TsToIsTransitions = tsToIsTransitionsBuilder.
build();
151 _IsTransitions = isTransitionsBuilder.
build();
152 _IsToTsTransitions = isToTsTransitionsBuilder.
build();
156template<
typename ValueType,
typename ComponentType, LraViTransitionsType TransitionsType>
159 for (
auto const& element : component) {
161 std::set<uint64_t> componentChoices;
163 ++componentChoiceIt) {
164 componentChoices.insert(*componentChoiceIt);
166 _component.emplace(std::move(componentState), std::move(componentChoices));
170template<
typename ValueType,
typename ComponentType, LraViTransitionsType TransitionsType>
173 std::vector<ValueType>
const* exitRates,
175 std::vector<uint64_t>* choices) {
176 initializeNewValues(stateValueGetter, actionValueGetter, exitRates);
179 boost::optional<uint64_t> maxIter;
185 ValueType result = storm::utility::zero<ValueType>();
187 while (!maxIter.is_initialized() || iter < maxIter.get()) {
189 performIterationStep(env, dir);
192 auto convergenceCheckResult = checkConvergence(relative, precision);
193 result = convergenceCheckResult.currentValue;
194 if (convergenceCheckResult.isPrecisionAchieved) {
201 prepareNextIteration(env);
203 if (maxIter.is_initialized() && iter == maxIter.get()) {
204 STORM_LOG_WARN(
"LRA computation did not converge within " << iter <<
" iterations.");
206 STORM_LOG_WARN(
"LRA computation aborted after " << iter <<
" iterations.");
208 STORM_LOG_TRACE(
"LRA computation converged after " << iter <<
" iterations.");
213 prepareNextIteration(env);
214 performIterationStep(env, dir, choices);
219template<
typename ValueType,
typename ComponentType, LraViTransitionsType TransitionsType>
221 std::vector<ValueType>
const* exitRates) {
223 _TsChoiceValues.clear();
224 _TsChoiceValues.reserve(_TsTransitions.getRowCount());
225 if (_hasInstantStates) {
226 _IsChoiceValues.clear();
227 _IsChoiceValues.reserve(_IsTransitions.getRowCount());
231 ValueType actionRewardScalingFactor = storm::utility::one<ValueType>() / _uniformizationRate;
232 for (
auto const& element : _component) {
233 uint64_t componentState = element.first;
234 if (isTimedState(componentState)) {
236 actionRewardScalingFactor = (*exitRates)[componentState] / _uniformizationRate;
238 for (
auto const& componentChoice : element.second) {
240 _TsChoiceValues.push_back(stateValueGetter(componentState) / _uniformizationRate +
241 actionValueGetter(componentChoice) * actionRewardScalingFactor);
244 for (
auto const& componentChoice : element.second) {
247 _IsChoiceValues.push_back(actionValueGetter(componentChoice));
253 _Tsx1.assign(_TsTransitions.getRowGroupCount(), storm::utility::zero<ValueType>());
256 if (_hasInstantStates) {
258 _Isx.resize(_IsTransitions.getRowGroupCount(), storm::utility::zero<ValueType>());
259 _Isb = _IsChoiceValues;
263template<
typename ValueType,
typename ComponentType, LraViTransitionsType TransitionsType>
264void LraViHelper<ValueType, ComponentType, TransitionsType>::prepareSolversAndMultipliers(
const Environment& env,
267 if (_hasInstantStates) {
268 if (_IsTransitions.getNonzeroEntryCount() > 0) {
270 _IsSolverEnv = std::make_unique<storm::Environment>(env);
271 if (env.solver().isForceSoundness()) {
274 _IsSolverEnv->solver().setForceExact(
true);
279 _IsSolverEnv->solver().minMax().setMethod(storm::solver::MinMaxMethod::Acyclic);
280 _IsSolverEnv->solver().setLinearEquationSolverType(storm::solver::EquationSolverType::Acyclic);
284 _NondetIsSolver = factory.
create(*_IsSolverEnv, _IsTransitions);
285 _NondetIsSolver->setHasUniqueSolution(
true);
286 _NondetIsSolver->setHasNoEndComponents(
true);
287 _NondetIsSolver->setCachingEnabled(
true);
288 auto req = _NondetIsSolver->getRequirements(*_IsSolverEnv, *dir);
289 req.clearUniqueSolution();
295 STORM_LOG_THROW(!req.hasEnabledCriticalRequirement(), storm::exceptions::UnmetRequirementException,
296 "The solver requirement " << req.getEnabledRequirementsAsString() <<
" has not been cleared.");
297 _NondetIsSolver->setRequirementsChecked(
true);
306 converted.convertToEquationSystem();
307 STORM_LOG_WARN(
"The selected equation solver requires to create a temporary " << converted.getDimensionsAsString());
309 _DetIsSolver = factory.
create(*_IsSolverEnv, std::move(converted));
311 _DetIsSolver = factory.
create(*_IsSolverEnv, _IsTransitions);
313 _DetIsSolver->setCachingEnabled(
true);
314 auto req = _DetIsSolver->getRequirements(*_IsSolverEnv);
319 STORM_LOG_THROW(!req.hasEnabledCriticalRequirement(), storm::exceptions::UnmetRequirementException,
320 "The solver requirement " << req.getEnabledRequirementsAsString() <<
" has not been cleared.");
330template<
typename ValueType,
typename ComponentType, LraViTransitionsType TransitionsType>
331void LraViHelper<ValueType, ComponentType, TransitionsType>::setInputModelChoices(std::vector<uint64_t>& choices, std::vector<uint64_t>
const& localMecChoices,
332 bool setChoiceZeroToTimedStates,
bool setChoiceZeroToInstantStates)
const {
334 uint64_t localState = 0;
335 for (
auto const& element : _component) {
336 uint64_t elementState = element.first;
337 if ((setChoiceZeroToTimedStates && isTimedState(elementState)) || (setChoiceZeroToInstantStates && !isTimedState(elementState))) {
338 choices[elementState] = 0;
340 uint64_t choice = localMecChoices[localState];
341 STORM_LOG_ASSERT(choice < element.second.size(),
"The selected choice does not seem to exist.");
342 auto globalChoiceIndexIt = element.second.begin();
343 for (uint64_t i = 0;
i < choice; ++
i) {
344 ++globalChoiceIndexIt;
346 uint64_t globalChoiceIndex = *(globalChoiceIndexIt);
347 choices[elementState] = globalChoiceIndex - _transitionMatrix.getRowGroupIndices()[elementState];
351 STORM_LOG_ASSERT(localState == localMecChoices.size(),
"Did not traverse all component states.");
354template<
typename ValueType,
typename ComponentType, LraViTransitionsType TransitionsType>
356 std::vector<uint64_t>* choices) {
357 STORM_LOG_ASSERT(!((nondetTs() || nondetIs()) && dir ==
nullptr),
"No optimization direction provided for model with nondeterminism");
359 if (!_TsMultiplier) {
360 prepareSolversAndMultipliers(env, dir);
365 _Tsx1IsCurrent = !_Tsx1IsCurrent;
371 if (choices ==
nullptr) {
372 _TsMultiplier->multiplyAndReduce(env, *dir, xOld(), &_TsChoiceValues, xNew());
375 std::vector<uint64_t> tsChoices(_TsTransitions.getRowGroupCount());
376 _TsMultiplier->multiplyAndReduce(env, *dir, xOld(), &_TsChoiceValues, xNew(), &tsChoices);
379 STORM_LOG_ASSERT(!_hasInstantStates,
"Nondeterministic timed states are only supported if there are no instant states.");
380 setInputModelChoices(*choices, tsChoices);
383 _TsMultiplier->multiply(env, xOld(), &_TsChoiceValues, xNew());
385 if (_hasInstantStates) {
388 if (_NondetIsSolver) {
390 if (choices ==
nullptr) {
391 _NondetIsSolver->solveEquations(*_IsSolverEnv, *dir, _Isx, _Isb);
393 _NondetIsSolver->setTrackScheduler();
394 _NondetIsSolver->solveEquations(*_IsSolverEnv, *dir, _Isx, _Isb);
395 setInputModelChoices(*choices, _NondetIsSolver->getSchedulerChoices(),
true);
397 }
else if (_DetIsSolver) {
398 _DetIsSolver->solveEquations(*_IsSolverEnv, _Isx, _Isb);
400 STORM_LOG_ASSERT(_IsTransitions.getNonzeroEntryCount() == 0,
"If no solver was initialized, an empty matrix would have been expected.");
402 if (choices ==
nullptr) {
405 std::vector<uint64_t> psChoices(_IsTransitions.getRowGroupCount());
407 setInputModelChoices(*choices, psChoices,
true);
416 setInputModelChoices(*choices, {},
true,
true);
421 _TsToIsMultiplier->multiply(env, _Isx, &xNew(), xNew());
425template<
typename ValueType,
typename ComponentType, LraViTransitionsType TransitionsType>
426typename LraViHelper<ValueType, ComponentType, TransitionsType>::ConvergenceCheckResult
427LraViHelper<ValueType, ComponentType, TransitionsType>::checkConvergence(
bool relative, ValueType precision)
const {
428 STORM_LOG_ASSERT(_TsMultiplier,
"tried to check for convergence without doing an iteration first.");
432 ValueType threshold = relative ? precision :
ValueType(precision / _uniformizationRate);
434 ConvergenceCheckResult res = {
true, storm::utility::one<ValueType>()};
436 STORM_LOG_ASSERT(threshold > storm::utility::zero<ValueType>(),
"Did not expect a non-positive threshold.");
437 auto x1It = xOld().begin();
438 auto x1Ite = xOld().end();
439 auto x2It = xNew().begin();
443 for (++x1It, ++x2It; x1It != x1Ite; ++x1It, ++x2It) {
446 bool skipCheck =
false;
447 if (maxDiff < diff) {
449 }
else if (minDiff > diff) {
455 if (!skipCheck && (maxDiff - minDiff) > (relative ? (threshold * minDiff) : threshold)) {
456 res.isPrecisionAchieved =
false;
462 ValueType avgDiff = (maxDiff + minDiff) / (storm::utility::convertNumber<ValueType>(2.0));
465 res.currentValue = avgDiff * _uniformizationRate;
469template<
typename ValueType,
typename ComponentType, LraViTransitionsType TransitionsType>
470void LraViHelper<ValueType, ComponentType, TransitionsType>::prepareNextIteration(Environment
const& env) {
472 ValueType referenceValue = xNew().front();
473 storm::utility::vector::applyPointwise<ValueType, ValueType>(xNew(), xNew(),
474 [&referenceValue](ValueType
const& x_i) -> ValueType {
return x_i - referenceValue; });
475 if (_hasInstantStates) {
477 STORM_LOG_ASSERT(!nondetTs(),
"Nondeterministic timed states not expected when there are also instant states.");
478 _IsToTsMultiplier->multiply(env, xNew(), &_IsChoiceValues, _Isb);
482template<
typename ValueType,
typename ComponentType, LraViTransitionsType TransitionsType>
483bool LraViHelper<ValueType, ComponentType, TransitionsType>::isTimedState(uint64_t
const& inputModelStateIndex)
const {
484 STORM_LOG_ASSERT(!_hasInstantStates || _timedStates !=
nullptr,
"Model has instant states but no partition into timed and instant states is given.");
485 STORM_LOG_ASSERT(!_hasInstantStates || inputModelStateIndex < _timedStates->size(),
486 "Unable to determine whether state " << inputModelStateIndex <<
" is timed.");
487 return !_hasInstantStates || _timedStates->get(inputModelStateIndex);
490template<
typename ValueType,
typename ComponentType, LraViTransitionsType TransitionsType>
491std::vector<ValueType>& LraViHelper<ValueType, ComponentType, TransitionsType>::xNew() {
492 return _Tsx1IsCurrent ? _Tsx1 : _Tsx2;
495template<
typename ValueType,
typename ComponentType, LraViTransitionsType TransitionsType>
496std::vector<ValueType>
const& LraViHelper<ValueType, ComponentType, TransitionsType>::xNew()
const {
497 return _Tsx1IsCurrent ? _Tsx1 : _Tsx2;
500template<
typename ValueType,
typename ComponentType, LraViTransitionsType TransitionsType>
501std::vector<ValueType>& LraViHelper<ValueType, ComponentType, TransitionsType>::xOld() {
502 return _Tsx1IsCurrent ? _Tsx2 : _Tsx1;
505template<
typename ValueType,
typename ComponentType, LraViTransitionsType TransitionsType>
506std::vector<ValueType>
const& LraViHelper<ValueType, ComponentType, TransitionsType>::xOld()
const {
507 return _Tsx1IsCurrent ? _Tsx2 : _Tsx1;
510template<
typename ValueType,
typename ComponentType, LraViTransitionsType TransitionsType>
511bool LraViHelper<ValueType, ComponentType, TransitionsType>::nondetTs()
const {
515template<
typename ValueType,
typename ComponentType, LraViTransitionsType TransitionsType>
516bool LraViHelper<ValueType, ComponentType, TransitionsType>::nondetIs()
const {
520template class LraViHelper<double, storm::storage::MaximalEndComponent, LraViTransitionsType::NondetTsNoIs>;
521template class LraViHelper<storm::RationalNumber, storm::storage::MaximalEndComponent, LraViTransitionsType::NondetTsNoIs>;
522template class LraViHelper<double, storm::storage::MaximalEndComponent, LraViTransitionsType::DetTsNondetIs>;
523template class LraViHelper<storm::RationalNumber, storm::storage::MaximalEndComponent, LraViTransitionsType::DetTsNondetIs>;
525template class LraViHelper<double, storm::storage::StronglyConnectedComponent, LraViTransitionsType::DetTsNoIs>;
526template class LraViHelper<storm::RationalNumber, storm::storage::StronglyConnectedComponent, LraViTransitionsType::DetTsNoIs>;
SolverEnvironment & solver()
bool isMaximalIterationCountSet() const
storm::RationalNumber const & getPrecision() const
bool const & getRelativeTerminationCriterion() const
uint64_t getMaximalIterationCount() const
LongRunAverageSolverEnvironment & lra()
Helper class that performs iterations of the value iteration method.
ValueType performValueIteration(Environment const &env, ValueGetter const &stateValueGetter, ValueGetter const &actionValueGetter, std::vector< ValueType > const *exitRates=nullptr, storm::solver::OptimizationDirection const *dir=nullptr, std::vector< uint64_t > *choices=nullptr)
Performs value iteration with the given state- and action values.
LraViHelper(ComponentType const &component, storm::storage::SparseMatrix< ValueType > const &transitionMatrix, ValueType const &aperiodicFactor, storm::storage::BitVector const *timedStates=nullptr, std::vector< ValueType > const *exitRates=nullptr)
Initializes a new VI helper for the provided MEC or BSCC.
std::function< ValueType(uint64_t)> ValueGetter
Function mapping from indices to values.
virtual std::unique_ptr< LinearEquationSolver< ValueType > > create(Environment const &env) const override
Creates an equation solver with the current settings, but without a matrix.
virtual std::unique_ptr< MinMaxLinearEquationSolver< ValueType, SolutionType > > create(Environment const &env) const override
virtual LinearEquationSolverProblemFormat getEquationProblemFormat(Environment const &env) const
Retrieves the problem format that the solver expects if it was created with the current settings.
std::unique_ptr< Multiplier< ValueType > > create(Environment const &env, storm::storage::SparseMatrix< ValueType > const &matrix)
A bit vector that is internally represented as a vector of 64-bit values.
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.
void addDiagonalEntry(index_type row, ValueType const &value)
Makes sure that a diagonal entry will be inserted at the given row.
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.
#define STORM_LOG_INFO(message)
#define STORM_LOG_WARN(message)
#define STORM_LOG_TRACE(message)
#define STORM_LOG_ASSERT(cond, message)
#define STORM_LOG_THROW(cond, exception, message)
SFTBDDChecker::ValueType ValueType
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)
LraViTransitionsType
Specifies differnt kinds of transition types with which this helper can be used Ts means timed states...
@ 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)
@ DetTsDetIs
deterministic choice at timed states, nondeterministic choice at instant states (as in Markov Automat...
uint64_t const * getComponentElementChoicesBegin(typename storm::storage::StronglyConnectedComponent::value_type const &element)
bool hasCycle(storm::storage::SparseMatrix< T > const &transitionMatrix, boost::optional< storm::storage::BitVector > const &subsystem)
Returns true if the graph represented by the given matrix has a cycle.
bool isTerminate()
Check whether the program should terminate (due to some abort signal).
void reduceVectorMinOrMax(storm::solver::OptimizationDirection dir, std::vector< T > const &source, std::vector< T > &target, std::vector< uint_fast64_t > const &rowGrouping, std::vector< uint_fast64_t > *choices=nullptr)
Reduces the given source vector by selecting either the smallest or the largest out of each row group...