20namespace modelchecker {
22template<
typename ValueType>
24 : transitionMatrix(transitionMatrix),
28 nonBsccStates(transitionMatrix.getRowCount(), false) {
32template<
typename ValueType>
34 std::vector<ValueType>
const& exitRates)
35 : transitionMatrix(transitionMatrix),
39 nonBsccStates(transitionMatrix.getRowCount(), false) {
43template<
typename ValueType>
45 STORM_LOG_WARN_COND(!backwardTransitions,
"Backward transition matrix was provided but it was already computed or provided before.");
46 backwardTransitions.reset(providedBackwardTransitions);
49template<
typename ValueType>
52 STORM_LOG_WARN_COND(!sccDecomposition,
"SCC Decomposition was provided but it was already computed or provided before.");
53 sccDecomposition.reset(decomposition);
56template<
typename ValueType>
61 ValueType
const p = storm::utility::one<ValueType>() / storm::utility::convertNumber<ValueType, uint64_t>(initialStates.
getNumberOfSetBits());
62 std::vector<ValueType> result(transitionMatrix.getRowCount(), storm::utility::zero<ValueType>());
68template<
typename ValueType>
70 STORM_LOG_ASSERT(initialState < transitionMatrix.getRowCount(),
"Invalid initial state index.");
71 std::vector<ValueType> result(transitionMatrix.getRowCount(), storm::utility::zero<ValueType>());
72 result[initialState] = storm::utility::one<ValueType>();
73 computeExpectedVisitingTimes(env, result);
77template<
typename ValueType>
80 std::vector<ValueType> result;
81 result.reserve(transitionMatrix.getRowCount());
82 for (uint64_t s = 0; s != transitionMatrix.getRowCount(); ++s) {
83 result.push_back(initialStateValueGetter(s));
85 computeExpectedVisitingTimes(env, result);
89template<
typename ValueType>
91 STORM_LOG_ASSERT(stateValues.size() == transitionMatrix.getRowCount(),
"Dimension missmatch.");
92 createBackwardTransitions();
93 createDecomposition(env);
94 createNonBsccStateVector();
98 auto isLeavingTransition = [&sccAsBitVector](
auto const& e) {
return !sccAsBitVector.
get(e.getColumn()); };
99 auto isLeavingTransitionWithNonZeroValue = [&isLeavingTransition, &stateValues](
auto const& e) {
102 auto isReachableInState = [
this, &isLeavingTransitionWithNonZeroValue, &stateValues](uint64_t state) {
106 auto row = this->backwardTransitions->getRow(state);
107 return std::any_of(row.begin(), row.end(), isLeavingTransitionWithNonZeroValue);
113 auto sccEnv = getEnvironmentForSolver(env,
true);
119 uint64_t sccIndex = 0;
120 auto sccItEnd = std::make_reverse_iterator(sccDecomposition->begin());
121 for (
auto sccIt = std::make_reverse_iterator(sccDecomposition->end()); sccIt != sccItEnd; ++sccIt) {
122 auto const& scc = *sccIt;
123 if (scc.size() == 1) {
124 processSingletonScc(*scc.begin(), stateValues);
126 sccAsBitVector.
set(scc.begin(), scc.end(),
true);
127 if (sccAsBitVector.
isSubsetOf(nonBsccStates)) {
129 auto sccResult = computeValueForStateSet(sccEnv, sccAsBitVector, stateValues);
133 if (std::any_of(sccAsBitVector.
begin(), sccAsBitVector.
end(), isReachableInState)) {
139 sccAsBitVector.
clear();
144 STORM_LOG_WARN(
"Visiting times computation aborted after analyzing " << sccIndex <<
"/" << this->computedSccDecomposition->size() <<
" SCCs.");
150 if (!nonBsccStates.empty()) {
152 Environment adjustedEnv = getEnvironmentForSolver(env,
false);
153 auto result = computeValueForStateSet(adjustedEnv, nonBsccStates, stateValues);
158 auto sccItEnd = std::make_reverse_iterator(sccDecomposition->begin());
159 for (
auto sccIt = std::make_reverse_iterator(sccDecomposition->end()); sccIt != sccItEnd; ++sccIt) {
160 auto const& scc = *sccIt;
161 sccAsBitVector.
set(scc.begin(), scc.end(),
true);
162 if (sccAsBitVector.
isSubsetOf(~nonBsccStates)) {
164 if (std::any_of(sccAsBitVector.
begin(), sccAsBitVector.
end(), isReachableInState)) {
172 sccAsBitVector.
clear();
176 if (isContinuousTime()) {
185template<
typename ValueType>
190template<
typename ValueType>
191void SparseDeterministicVisitingTimesHelper<ValueType>::createBackwardTransitions() {
192 if (!this->backwardTransitions) {
193 this->computedBackwardTransitions =
194 std::make_unique<storm::storage::SparseMatrix<ValueType>>(transitionMatrix.transpose(
true,
false));
195 this->backwardTransitions.reset(*this->computedBackwardTransitions);
199template<
typename ValueType>
200void SparseDeterministicVisitingTimesHelper<ValueType>::createDecomposition(Environment
const& env) {
201 if (this->sccDecomposition && !this->sccDecomposition->hasSccDepth() && env.solver().isForceSoundness()) {
203 STORM_LOG_WARN(
"Recomputing SCC Decomposition because the currently available decomposition is computed without SCCDepths.");
204 this->computedSccDecomposition.reset();
205 this->sccDecomposition.reset();
208 if (!this->sccDecomposition) {
212 this->computedSccDecomposition = std::make_unique<storm::storage::StronglyConnectedComponentDecomposition<ValueType>>(this->transitionMatrix, options);
213 this->sccDecomposition.reset(*this->computedSccDecomposition);
217template<
typename ValueType>
218void SparseDeterministicVisitingTimesHelper<ValueType>::createNonBsccStateVector() {
221 auto isLeavingTransition = [&sccAsBitVector](
auto const& e) {
return !sccAsBitVector.get(e.getColumn()); };
222 auto isExitState = [
this, &isLeavingTransition](uint64_t state) {
223 auto row = this->transitionMatrix.getRow(state);
224 return std::any_of(row.begin(), row.end(), isLeavingTransition);
227 auto sccItEnd = std::make_reverse_iterator(sccDecomposition->begin());
228 for (
auto sccIt = std::make_reverse_iterator(sccDecomposition->end()); sccIt != sccItEnd; ++sccIt) {
229 auto const& scc = *sccIt;
230 sccAsBitVector.set(scc.begin(), scc.end(),
true);
231 if (std::any_of(sccAsBitVector.begin(), sccAsBitVector.end(), isExitState)) {
233 nonBsccStates = nonBsccStates | sccAsBitVector;
235 sccAsBitVector.clear();
240std::vector<storm::RationalFunction> SparseDeterministicVisitingTimesHelper<storm::RationalFunction>::computeUpperBounds(
243 "Computing upper bounds for expected visiting times over rational functions is not supported.");
246template<
typename ValueType>
247std::vector<ValueType> SparseDeterministicVisitingTimesHelper<ValueType>::computeUpperBounds(
storm::storage::BitVector const& stateSetAsBitvector)
const {
249 std::vector<ValueType> leavingTransitions = transitionMatrix.getConstrainedRowGroupSumVector(stateSetAsBitvector, ~stateSetAsBitvector);
256 subTransitionMatrix, leavingTransitions);
260template<
typename ValueType>
266 newEnv = getEnvironmentForTopologicalSolver(env);
270 if (isContinuousTime()) {
271 auto prec = newEnv.solver().getPrecisionOfLinearEquationSolver(newEnv.solver().getLinearEquationSolverType());
273 bool needAdaptPrecision =
275 !newEnv.solver().getPrecisionOfLinearEquationSolver(newEnv.solver().getLinearEquationSolverType()).second.get();
280 "The precision for the current solver type is not adjusted for this solving method. Ensure that this is correct for topological computation.");
282 if (needAdaptPrecision) {
284 ValueType min = *std::min_element(exitRates->begin(), exitRates->end());
286 "An error occurred during the adjustment of the precision. Min. rate = " << min);
287 newEnv.solver().setLinearEquationSolverPrecision(
288 static_cast<storm::RationalNumber
>(prec.first.get() * storm::utility::convertNumber<storm::RationalNumber>(min)));
292 auto prec = newEnv.solver().getPrecisionOfLinearEquationSolver(newEnv.solver().getLinearEquationSolverType());
293 if (prec.first.is_initialized()) {
294 STORM_LOG_INFO(
"Precision for EVTs computation: " << storm::utility::convertNumber<double>(prec.first.get()) <<
" (exact: " << prec.first.get() <<
")"
301template<
typename ValueType>
308 auto subEnvPrec = subEnv.solver().getPrecisionOfLinearEquationSolver(subEnv.solver().getLinearEquationSolverType());
310 bool singletonSCCs =
true;
312 auto sccItEnd = std::make_reverse_iterator(sccDecomposition->begin());
313 for (
auto sccIt = std::make_reverse_iterator(sccDecomposition->end()); sccIt != sccItEnd; ++sccIt) {
314 auto const& scc = *sccIt;
315 sccAsBitVector.set(scc.begin(), scc.end(),
true);
316 if (sccAsBitVector.isSubsetOf(nonBsccStates)) {
318 if (sccAsBitVector.getNumberOfSetBits() > 1) {
319 singletonSCCs =
false;
323 sccAsBitVector.clear();
326 bool needAdaptPrecision = env.
solver().
isForceSoundness() && subEnvPrec.first.is_initialized() && subEnvPrec.second.is_initialized() &&
331 !needAdaptPrecision || subEnv.solver().getLinearEquationSolverType() == storm::solver::EquationSolverType::Native,
332 "The precision for the current solver type is not adjusted for this solving method. Ensure that this is correct for topological computation.");
334 if (needAdaptPrecision && subEnvPrec.second.get()) {
335 STORM_LOG_ASSERT(sccDecomposition->hasSccDepth(),
"Did not compute the longest SCC chain size although it is needed.");
338 auto subEnvPrec = subEnv.solver().getPrecisionOfLinearEquationSolver(subEnv.solver().getLinearEquationSolverType());
340 double scaledPrecision1 = 1 - std::pow(1 - storm::utility::convertNumber<double>(subEnvPrec.first.get()), 1.0 / sccDecomposition->getMaxSccDepth());
343 subEnv.solver().setLinearEquationSolverPrecision(storm::utility::convertNumber<storm::RationalNumber>(scaledPrecision1));
345 }
else if (needAdaptPrecision && !subEnv.solver().getPrecisionOfLinearEquationSolver(subEnv.solver().getLinearEquationSolverType()).second.get()) {
350 if (sccDecomposition->getMaxSccDepth() > 1) {
358 uint_fast64_t maxNumInc = 0;
361 ValueType boundEVT = storm::utility::zero<ValueType>();
363 auto sccItEnd = std::make_reverse_iterator(sccDecomposition->begin());
364 for (
auto sccIt = std::make_reverse_iterator(sccDecomposition->end()); sccIt != sccItEnd; ++sccIt) {
365 auto const& scc = *sccIt;
366 sccAsBitVector.set(scc.begin(), scc.end(),
true);
367 if (sccAsBitVector.isSubsetOf(nonBsccStates)) {
371 auto toSccMatrix = transitionMatrix.getSubmatrix(
false, ~sccAsBitVector, sccAsBitVector);
372 uint_fast64_t localNumInc =
373 std::count_if(toSccMatrix.begin(), toSccMatrix.end(), [](
auto const& e) { return !storm::utility::isZero(e.getValue()); });
374 maxNumInc = std::max(maxNumInc, localNumInc);
378 std::vector<ValueType> upperBounds = computeUpperBounds(sccAsBitVector);
379 boundEVT = std::max(boundEVT, (*std::max_element(upperBounds.begin(), upperBounds.end())));
381 sccAsBitVector.clear();
385 storm::RationalNumber
one = storm::RationalNumber(1);
388 if (maxNumInc != 0) {
391 for (uint64_t i = 1;
i < sccDecomposition->getMaxSccDepth();
i++) {
393 storm::utility::convertNumber<storm::RationalNumber>(boundEVT);
396 subEnv.solver().setLinearEquationSolverPrecision(
static_cast<storm::RationalNumber
>(subEnvPrec.first.get() / scale));
403template<
typename ValueType>
404void SparseDeterministicVisitingTimesHelper<ValueType>::processSingletonScc(uint64_t sccState, std::vector<ValueType>& stateValues)
const {
405 auto& stateVal = stateValues[sccState];
406 auto forwardRow = transitionMatrix.getRow(sccState);
407 auto backwardRow = backwardTransitions->getRow(sccState);
408 if (forwardRow.getNumberOfEntries() == 1 && forwardRow.begin()->getColumn() == sccState) {
411 [&stateValues](
auto const& e) { return !storm::utility::isZero(stateValues[e.getColumn()]); })) {
412 stateVal = storm::utility::infinity<ValueType>();
416 ValueType divisor = storm::utility::one<ValueType>();
417 for (
auto const& entry : backwardRow) {
418 if (entry.getColumn() == sccState) {
420 divisor -= entry.getValue();
422 stateVal += entry.getValue() * stateValues[entry.getColumn()];
429template<
typename ValueType>
430std::vector<ValueType> SparseDeterministicVisitingTimesHelper<ValueType>::computeValueForStateSet(
storm::Environment const& env,
432 std::vector<ValueType>
const& stateValues)
const {
435 auto valIt = sccVector.begin();
436 for (
auto sccState : stateSetAsBitvector) {
437 for (
auto const& entry : backwardTransitions->getRow(sccState)) {
438 if (!stateSetAsBitvector.
get(entry.getColumn())) {
439 (*valIt) += entry.getValue() * stateValues[entry.getColumn()];
444 return computeExpectedVisitingTimes(env, stateSetAsBitvector, sccVector);
447template<
typename ValueType>
450 std::vector<ValueType>
const& initialValues)
const {
453 if (initialValues.empty()) {
468 auto sccMatrix = backwardTransitions->getSubmatrix(
false, subsystem, subsystem, !isFixpointFormat);
469 if (!isFixpointFormat) {
470 sccMatrix.convertToEquationSystem();
474 auto solver = linearEquationSolverFactory.
create(env, std::move(sccMatrix));
475 solver->setLowerBound(storm::utility::zero<ValueType>());
476 auto req = solver->getRequirements(env);
477 req.clearLowerBounds();
478 if (req.upperBounds().isCritical()) {
480 std::vector<ValueType> upperBounds = computeUpperBounds(subsystem);
481 solver->setUpperBounds(upperBounds);
482 req.clearUpperBounds();
485 if (req.acyclic().isCritical()) {
487 "The solver requires an acyclic model, but the model is not acyclic.");
491 STORM_LOG_THROW(!req.hasEnabledCriticalRequirement(), storm::exceptions::UnmetRequirementException,
492 "Solver requirements " + req.getEnabledRequirementsAsString() +
" not checked.");
493 std::vector<ValueType> eqSysValues(initialValues.size());
494 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.
uint64_t getNumberOfSetBits() const
Returns the number of bits that are set to true in this bit vector.
void set(uint64_t index, bool value=true)
Sets the given truth value at the given index.
const_iterator begin() const
Returns an iterator to the indices of the set bits in the bit vector.
size_t size() const
Retrieves the number of bits this bit vector can store.
bool get(uint64_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.
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.