23namespace modelchecker {
25template<
typename ValueType>
27 : transitionMatrix(transitionMatrix),
31 nonBsccStates(transitionMatrix.getRowCount(), false) {
35template<
typename ValueType>
37 std::vector<ValueType>
const& exitRates)
38 : transitionMatrix(transitionMatrix),
42 nonBsccStates(transitionMatrix.getRowCount(), false) {
47template<
typename ValueType>
49 STORM_LOG_WARN_COND(!backwardTransitions,
"Backward transition matrix was provided but it was already computed or provided before.");
50 backwardTransitions.reset(providedBackwardTransitions);
53template<
typename ValueType>
56 STORM_LOG_WARN_COND(!sccDecomposition,
"SCC Decomposition was provided but it was already computed or provided before.");
57 sccDecomposition.reset(decomposition);
60template<
typename ValueType>
65 ValueType
const p = storm::utility::one<ValueType>() / storm::utility::convertNumber<ValueType, uint64_t>(initialStates.
getNumberOfSetBits());
66 std::vector<ValueType> result(transitionMatrix.getRowCount(), storm::utility::zero<ValueType>());
68 computeExpectedVisitingTimes(env, result);
72template<
typename ValueType>
74 STORM_LOG_ASSERT(initialState < transitionMatrix.getRowCount(),
"Invalid initial state index.");
75 std::vector<ValueType> result(transitionMatrix.getRowCount(), storm::utility::zero<ValueType>());
76 result[initialState] = storm::utility::one<ValueType>();
77 computeExpectedVisitingTimes(env, result);
81template<
typename ValueType>
84 std::vector<ValueType> result;
85 result.reserve(transitionMatrix.getRowCount());
86 for (uint64_t s = 0; s != transitionMatrix.getRowCount(); ++s) {
87 result.push_back(initialStateValueGetter(s));
89 computeExpectedVisitingTimes(env, result);
93template<
typename ValueType>
95 STORM_LOG_ASSERT(stateValues.size() == transitionMatrix.getRowCount(),
"Dimension missmatch.");
96 createBackwardTransitions();
97 createDecomposition(env);
98 createNonBsccStateVector();
102 auto isLeavingTransition = [&sccAsBitVector](
auto const& e) {
return !sccAsBitVector.
get(e.getColumn()); };
103 auto isLeavingTransitionWithNonZeroValue = [&isLeavingTransition, &stateValues](
auto const& e) {
106 auto isReachableInState = [
this, &isLeavingTransitionWithNonZeroValue, &stateValues](uint64_t state) {
110 auto row = this->backwardTransitions->getRow(state);
111 return std::any_of(row.begin(), row.end(), isLeavingTransitionWithNonZeroValue);
117 auto sccEnv = getEnvironmentForSolver(env,
true);
123 uint64_t sccIndex = 0;
124 auto sccItEnd = std::make_reverse_iterator(sccDecomposition->begin());
125 for (
auto sccIt = std::make_reverse_iterator(sccDecomposition->end()); sccIt != sccItEnd; ++sccIt) {
126 auto const& scc = *sccIt;
127 if (scc.size() == 1) {
128 processSingletonScc(*scc.begin(), stateValues);
130 sccAsBitVector.
set(scc.begin(), scc.end(),
true);
131 if (sccAsBitVector.
isSubsetOf(nonBsccStates)) {
133 auto sccResult = computeValueForStateSet(sccEnv, sccAsBitVector, stateValues);
137 if (std::any_of(sccAsBitVector.
begin(), sccAsBitVector.
end(), isReachableInState)) {
143 sccAsBitVector.
clear();
148 STORM_LOG_WARN(
"Visiting times computation aborted after analyzing " << sccIndex <<
"/" << this->computedSccDecomposition->size() <<
" SCCs.");
154 if (!nonBsccStates.empty()) {
156 Environment adjustedEnv = getEnvironmentForSolver(env,
false);
157 auto result = computeValueForStateSet(adjustedEnv, nonBsccStates, stateValues);
162 auto sccItEnd = std::make_reverse_iterator(sccDecomposition->begin());
163 for (
auto sccIt = std::make_reverse_iterator(sccDecomposition->end()); sccIt != sccItEnd; ++sccIt) {
164 auto const& scc = *sccIt;
165 sccAsBitVector.
set(scc.begin(), scc.end(),
true);
166 if (sccAsBitVector.
isSubsetOf(~nonBsccStates)) {
168 if (std::any_of(sccAsBitVector.
begin(), sccAsBitVector.
end(), isReachableInState)) {
176 sccAsBitVector.
clear();
180 if (isContinuousTime()) {
189template<
typename ValueType>
194template<
typename ValueType>
195void SparseDeterministicVisitingTimesHelper<ValueType>::createBackwardTransitions() {
196 if (!this->backwardTransitions) {
197 this->computedBackwardTransitions =
198 std::make_unique<storm::storage::SparseMatrix<ValueType>>(transitionMatrix.transpose(
true,
false));
199 this->backwardTransitions.reset(*this->computedBackwardTransitions);
203template<
typename ValueType>
204void SparseDeterministicVisitingTimesHelper<ValueType>::createDecomposition(Environment
const& env) {
205 if (this->sccDecomposition && !this->sccDecomposition->hasSccDepth() && env.solver().isForceSoundness()) {
207 STORM_LOG_WARN(
"Recomputing SCC Decomposition because the currently available decomposition is computed without SCCDepths.");
208 this->computedSccDecomposition.reset();
209 this->sccDecomposition.reset();
212 if (!this->sccDecomposition) {
216 this->computedSccDecomposition = std::make_unique<storm::storage::StronglyConnectedComponentDecomposition<ValueType>>(this->transitionMatrix, options);
217 this->sccDecomposition.reset(*this->computedSccDecomposition);
221template<
typename ValueType>
222void SparseDeterministicVisitingTimesHelper<ValueType>::createNonBsccStateVector() {
225 auto isLeavingTransition = [&sccAsBitVector](
auto const& e) {
return !sccAsBitVector.get(e.getColumn()); };
226 auto isExitState = [
this, &isLeavingTransition](uint64_t state) {
227 auto row = this->transitionMatrix.getRow(state);
228 return std::any_of(row.begin(), row.end(), isLeavingTransition);
231 auto sccItEnd = std::make_reverse_iterator(sccDecomposition->begin());
232 for (
auto sccIt = std::make_reverse_iterator(sccDecomposition->end()); sccIt != sccItEnd; ++sccIt) {
233 auto const& scc = *sccIt;
234 sccAsBitVector.set(scc.begin(), scc.end(),
true);
235 if (std::any_of(sccAsBitVector.begin(), sccAsBitVector.end(), isExitState)) {
237 nonBsccStates = nonBsccStates | sccAsBitVector;
239 sccAsBitVector.clear();
244std::vector<storm::RationalFunction> SparseDeterministicVisitingTimesHelper<storm::RationalFunction>::computeUpperBounds(
247 "Computing upper bounds for expected visiting times over rational functions is not supported.");
250template<
typename ValueType>
251std::vector<ValueType> SparseDeterministicVisitingTimesHelper<ValueType>::computeUpperBounds(
storm::storage::BitVector const& stateSetAsBitvector)
const {
253 std::vector<ValueType> leavingTransitions = transitionMatrix.getConstrainedRowGroupSumVector(stateSetAsBitvector, ~stateSetAsBitvector);
260 subTransitionMatrix, leavingTransitions);
264template<
typename ValueType>
270 newEnv = getEnvironmentForTopologicalSolver(env);
274 if (isContinuousTime()) {
275 auto prec = newEnv.solver().getPrecisionOfLinearEquationSolver(newEnv.solver().getLinearEquationSolverType());
277 bool needAdaptPrecision =
279 !newEnv.solver().getPrecisionOfLinearEquationSolver(newEnv.solver().getLinearEquationSolverType()).second.get();
284 "The precision for the current solver type is not adjusted for this solving method. Ensure that this is correct for topological computation.");
286 if (needAdaptPrecision) {
288 ValueType min = *std::min_element(exitRates->begin(), exitRates->end());
290 "An error occurred during the adjustment of the precision. Min. rate = " << min);
291 newEnv.solver().setLinearEquationSolverPrecision(
292 static_cast<storm::RationalNumber
>(prec.first.get() * storm::utility::convertNumber<storm::RationalNumber>(min)));
296 auto prec = newEnv.solver().getPrecisionOfLinearEquationSolver(newEnv.solver().getLinearEquationSolverType());
297 if (prec.first.is_initialized()) {
298 STORM_LOG_INFO(
"Precision for EVTs computation: " << storm::utility::convertNumber<double>(prec.first.get()) <<
" (exact: " << prec.first.get() <<
")"
305template<
typename ValueType>
312 auto subEnvPrec = subEnv.solver().getPrecisionOfLinearEquationSolver(subEnv.solver().getLinearEquationSolverType());
314 bool singletonSCCs =
true;
316 auto sccItEnd = std::make_reverse_iterator(sccDecomposition->begin());
317 for (
auto sccIt = std::make_reverse_iterator(sccDecomposition->end()); sccIt != sccItEnd; ++sccIt) {
318 auto const& scc = *sccIt;
319 sccAsBitVector.set(scc.begin(), scc.end(),
true);
320 if (sccAsBitVector.isSubsetOf(nonBsccStates)) {
322 if (sccAsBitVector.getNumberOfSetBits() > 1) {
323 singletonSCCs =
false;
327 sccAsBitVector.clear();
330 bool needAdaptPrecision = env.
solver().
isForceSoundness() && subEnvPrec.first.is_initialized() && subEnvPrec.second.is_initialized() &&
335 !needAdaptPrecision || subEnv.solver().getLinearEquationSolverType() == storm::solver::EquationSolverType::Native,
336 "The precision for the current solver type is not adjusted for this solving method. Ensure that this is correct for topological computation.");
338 if (needAdaptPrecision && subEnvPrec.second.get()) {
339 STORM_LOG_ASSERT(sccDecomposition->hasSccDepth(),
"Did not compute the longest SCC chain size although it is needed.");
342 auto subEnvPrec = subEnv.solver().getPrecisionOfLinearEquationSolver(subEnv.solver().getLinearEquationSolverType());
344 double scaledPrecision1 = 1 - std::pow(1 - storm::utility::convertNumber<double>(subEnvPrec.first.get()), 1.0 / sccDecomposition->getMaxSccDepth());
347 subEnv.solver().setLinearEquationSolverPrecision(storm::utility::convertNumber<storm::RationalNumber>(scaledPrecision1));
349 }
else if (needAdaptPrecision && !subEnv.solver().getPrecisionOfLinearEquationSolver(subEnv.solver().getLinearEquationSolverType()).second.get()) {
354 if (sccDecomposition->getMaxSccDepth() > 1) {
362 uint_fast64_t maxNumInc = 0;
365 ValueType boundEVT = storm::utility::zero<ValueType>();
367 auto sccItEnd = std::make_reverse_iterator(sccDecomposition->begin());
368 for (
auto sccIt = std::make_reverse_iterator(sccDecomposition->end()); sccIt != sccItEnd; ++sccIt) {
369 auto const& scc = *sccIt;
370 sccAsBitVector.set(scc.begin(), scc.end(),
true);
371 if (sccAsBitVector.isSubsetOf(nonBsccStates)) {
375 auto toSccMatrix = transitionMatrix.getSubmatrix(
false, ~sccAsBitVector, sccAsBitVector);
376 uint_fast64_t localNumInc =
377 std::count_if(toSccMatrix.begin(), toSccMatrix.end(), [](
auto const& e) { return !storm::utility::isZero(e.getValue()); });
378 maxNumInc = std::max(maxNumInc, localNumInc);
382 std::vector<ValueType> upperBounds = computeUpperBounds(sccAsBitVector);
383 boundEVT = std::max(boundEVT, (*std::max_element(upperBounds.begin(), upperBounds.end())));
385 sccAsBitVector.clear();
389 storm::RationalNumber
one = storm::RationalNumber(1);
392 if (maxNumInc != 0) {
395 for (uint64_t i = 1;
i < sccDecomposition->getMaxSccDepth();
i++) {
397 storm::utility::convertNumber<storm::RationalNumber>(boundEVT);
400 subEnv.solver().setLinearEquationSolverPrecision(
static_cast<storm::RationalNumber
>(subEnvPrec.first.get() / scale));
407template<
typename ValueType>
408void SparseDeterministicVisitingTimesHelper<ValueType>::processSingletonScc(uint64_t sccState, std::vector<ValueType>& stateValues)
const {
409 auto& stateVal = stateValues[sccState];
410 auto forwardRow = transitionMatrix.getRow(sccState);
411 auto backwardRow = backwardTransitions->getRow(sccState);
412 if (forwardRow.getNumberOfEntries() == 1 && forwardRow.begin()->getColumn() == sccState) {
415 [&stateValues](
auto const& e) { return !storm::utility::isZero(stateValues[e.getColumn()]); })) {
416 stateVal = storm::utility::infinity<ValueType>();
420 ValueType divisor = storm::utility::one<ValueType>();
421 for (
auto const& entry : backwardRow) {
422 if (entry.getColumn() == sccState) {
424 divisor -= entry.getValue();
426 stateVal += entry.getValue() * stateValues[entry.getColumn()];
433template<
typename ValueType>
434std::vector<ValueType> SparseDeterministicVisitingTimesHelper<ValueType>::computeValueForStateSet(
storm::Environment const& env,
436 std::vector<ValueType>
const& stateValues)
const {
439 auto valIt = sccVector.begin();
440 for (
auto sccState : stateSetAsBitvector) {
441 for (
auto const& entry : backwardTransitions->getRow(sccState)) {
442 if (!stateSetAsBitvector.
get(entry.getColumn())) {
443 (*valIt) += entry.getValue() * stateValues[entry.getColumn()];
448 return computeExpectedVisitingTimes(env, stateSetAsBitvector, sccVector);
451template<
typename ValueType>
454 std::vector<ValueType>
const& initialValues)
const {
457 if (initialValues.empty()) {
472 auto sccMatrix = backwardTransitions->getSubmatrix(
false, subsystem, subsystem, !isFixpointFormat);
473 if (!isFixpointFormat) {
474 sccMatrix.convertToEquationSystem();
478 auto solver = linearEquationSolverFactory.
create(env, std::move(sccMatrix));
479 solver->setLowerBound(storm::utility::zero<ValueType>());
480 auto req = solver->getRequirements(env);
481 req.clearLowerBounds();
482 if (req.upperBounds().isCritical()) {
484 std::vector<ValueType> upperBounds = computeUpperBounds(subsystem);
485 solver->setUpperBounds(upperBounds);
486 req.clearUpperBounds();
489 if (req.acyclic().isCritical()) {
491 "The solver requires an acyclic model, but the model is not acyclic.");
495 STORM_LOG_THROW(!req.hasEnabledCriticalRequirement(), storm::exceptions::UnmetRequirementException,
496 "Solver requirements " + req.getEnabledRequirementsAsString() +
" not checked.");
497 std::vector<ValueType> eqSysValues(initialValues.size());
498 solver->solveEquations(env, eqSysValues, initialValues);
SolverEnvironment & solver()
TopologicalSolverEnvironment & topological()
storm::solver::EquationSolverType const & getLinearEquationSolverType() const
bool isForceSoundness() const
storm::solver::EquationSolverType const & getUnderlyingEquationSolverType() const
bool const & isUnderlyingEquationSolverTypeSetFromDefault() const
static std::vector< ValueType > computeUpperBoundOnExpectedVisitingTimes(storm::storage::SparseMatrix< ValueType > const &transitionMatrix, storm::storage::SparseMatrix< ValueType > const &backwardTransitions, std::vector< ValueType > const &oneStepTargetProbabilities)
Computes for each state an upper bound for the maximal expected times each state is visited.
Helper class for computing for each state the expected number of times to visit that state assuming a...
std::vector< ValueType > computeExpectedVisitingTimes(Environment const &env, storm::storage::BitVector const &initialStates)
Computes for each state the expected number of times we are visiting that state assuming the given in...
void provideSCCDecomposition(storm::storage::StronglyConnectedComponentDecomposition< ValueType > const &decomposition)
Provides the decomposition into SCCs that can be used during the computation.
void provideBackwardTransitions(storm::storage::SparseMatrix< ValueType > const &backwardTransitions)
Provides the backward transitions that can be used during the computation.
std::function< ValueType(uint64_t)> ValueGetter
Function mapping from indices to values.
SparseDeterministicVisitingTimesHelper(storm::storage::SparseMatrix< ValueType > const &transitionMatrix)
Initializes the helper for a DTMC.
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 LinearEquationSolverProblemFormat getEquationProblemFormat(Environment const &env) const
Retrieves the problem format that the solver expects if it was created with the current settings.
A bit vector that is internally represented as a vector of 64-bit values.
const_iterator end() const
Returns an iterator pointing at the element past the back of the bit vector.
bool empty() const
Retrieves whether no bits are set to true in this bit vector.
void clear()
Removes all set bits from the bit vector.
bool isSubsetOf(BitVector const &other) const
Checks whether all bits that are set in the current bit vector are also set in the given bit vector.
const_iterator begin() const
Returns an iterator to the indices of the set bits in the bit vector.
void set(uint_fast64_t index, bool value=true)
Sets the given truth value at the given index.
size_t size() const
Retrieves the number of bits this bit vector can store.
uint_fast64_t getNumberOfSetBits() const
Returns the number of bits that are set to true in this bit vector.
bool get(uint_fast64_t index) const
Retrieves the truth value of the bit at the given index and performs a bound check.
A class that holds a possibly non-square matrix in the compressed row storage format.
bool isProbabilistic() const
Checks for each row whether it sums to one.
SparseMatrix getSubmatrix(bool useGroups, storm::storage::BitVector const &rowConstraint, storm::storage::BitVector const &columnConstraint, bool insertDiagonalEntries=false, storm::storage::BitVector const &makeZeroColumns=storm::storage::BitVector()) const
Creates a submatrix of the current matrix by dropping all rows and columns whose bits are not set to ...
This class represents the decomposition of a graph-like structure into its strongly connected compone...
A class that provides convenience operations to display run times.
bool updateProgress(uint64_t count)
Updates the progress to the current count and prints it if the delay passed.
void setMaxCount(uint64_t maxCount)
Sets the maximal possible count.
void startNewMeasurement(uint64_t startCount)
Starts a new measurement, dropping all progress information collected so far.
#define STORM_LOG_INFO(message)
#define STORM_LOG_WARN(message)
#define STORM_LOG_ASSERT(cond, message)
#define STORM_LOG_WARN_COND(cond, message)
#define STORM_LOG_THROW(cond, exception, message)
bool scale(int exp, storm::RationalNumber &r, storm::RationalNumber acc)
SFTBDDChecker::ValueType ValueType
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 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 applyPointwise(std::vector< InValueType1 > const &firstOperand, std::vector< InValueType2 > const &secondOperand, std::vector< OutValueType > &target, Operation f=Operation())
Applies the given operation pointwise on the two given vectors and writes the result to the third vec...
std::vector< Type > filterVector(std::vector< Type > const &in, storm::storage::BitVector const &filter)
bool isOne(ValueType const &a)
ValueType min(ValueType const &first, ValueType const &second)
bool isZero(ValueType const &a)
ValueType pow(ValueType const &value, int_fast64_t exponent)
bool isInfinity(ValueType const &a)
constexpr NullRefType NullRef
StronglyConnectedComponentDecompositionOptions & computeSccDepths(bool value=true)
Sets if scc depths can be retrieved.
StronglyConnectedComponentDecompositionOptions & forceTopologicalSort(bool value=true)
Enforces that the returned SCCs are sorted in a topological order.