Storm 1.13.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
TopologicalLinearEquationSolver.cpp
Go to the documentation of this file.
2
10
11namespace storm {
12namespace solver {
13
14template<typename ValueType>
16 // Intentionally left empty.
17}
18
19template<typename ValueType>
23
24template<typename ValueType>
28
29template<typename ValueType>
35
36template<typename ValueType>
38 localA = std::make_unique<storm::storage::SparseMatrix<ValueType>>(std::move(A));
39 this->A = localA.get();
40 clearCache();
41}
42
43template<typename ValueType>
44storm::Environment TopologicalLinearEquationSolver<ValueType>::getEnvironmentForUnderlyingSolver(storm::Environment const& env, bool adaptPrecision) const {
45 storm::Environment subEnv(env);
46 subEnv.solver().setLinearEquationSolverType(env.solver().topological().getUnderlyingEquationSolverType(),
48 if (adaptPrecision) {
49 STORM_LOG_ASSERT(this->longestSccChainSize, "Did not compute the longest SCC chain size although it is needed.");
50 auto subEnvPrec = subEnv.solver().getPrecisionOfLinearEquationSolver(subEnv.solver().getLinearEquationSolverType());
51 subEnv.solver().setLinearEquationSolverPrecision(
52 static_cast<storm::RationalNumber>(subEnvPrec.first.get() / storm::utility::convertNumber<storm::RationalNumber>(this->longestSccChainSize.get())));
53 }
54 return subEnv;
55}
56
57template<typename ValueType>
59 std::vector<ValueType> const& b) const {
60 // For sound computations we need to increase the precision in each SCC
61 bool needAdaptPrecision =
62 env.solver().isForceSoundness() &&
64
65 if (!this->sortedSccDecomposition || (needAdaptPrecision && !this->longestSccChainSize)) {
66 STORM_LOG_TRACE("Creating SCC decomposition.");
67 storm::utility::Stopwatch sccSw(true);
68 createSortedSccDecomposition(needAdaptPrecision);
69 sccSw.stop();
70 STORM_LOG_INFO("SCC decomposition computed in "
71 << sccSw << ". Found " << this->sortedSccDecomposition->size() << " SCC(s) containing a total of " << x.size()
72 << " states. Average SCC size is "
73 << static_cast<double>(this->getMatrixRowCount()) / static_cast<double>(this->sortedSccDecomposition->size()) << ".");
74 }
75
76 // We do not need to adapt the precision if all SCCs are trivial (i.e., the system is acyclic)
77 needAdaptPrecision = needAdaptPrecision && (this->sortedSccDecomposition->size() != this->getMatrixRowCount());
78
79 storm::Environment sccSolverEnvironment = getEnvironmentForUnderlyingSolver(env, needAdaptPrecision);
80
81 if (this->longestSccChainSize) {
82 STORM_LOG_INFO("Longest SCC chain size is " << this->longestSccChainSize.get() << ".");
83 }
84
85 // Handle the case where there is just one large SCC
86 bool returnValue = true;
87 if (this->sortedSccDecomposition->size() == 1) {
88 if (auto const& scc = *this->sortedSccDecomposition->begin(); scc.size() == 1) {
89 // Catch the trivial case where the whole system is just a single state.
90 returnValue = solveTrivialScc(*scc.begin(), x, b);
91 } else {
92 returnValue = solveFullyConnectedEquationSystem(sccSolverEnvironment, x, b);
93 }
94 } else {
95 // Solve each SCC individually
96 std::optional<storm::storage::BitVector> newRelevantValues;
97 if (env.solver().topological().isExtendRelevantValues() && this->hasRelevantValues() &&
98 this->sortedSccDecomposition->size() < this->A->getRowGroupCount()) {
99 newRelevantValues = this->getRelevantValues();
100 // Extend the relevant values towards those that have an incoming transition from another SCC
101 std::vector<uint64_t> rowGroupToScc = this->sortedSccDecomposition->computeStateToSccIndexMap(this->A->getRowGroupCount());
102 for (uint64_t rowGroup = 0; rowGroup < this->A->getRowGroupCount(); ++rowGroup) {
103 auto currScc = rowGroupToScc[rowGroup];
104 for (auto const& successor : this->A->getRowGroup(rowGroup)) {
105 if (rowGroupToScc[successor.getColumn()] != currScc) {
106 newRelevantValues->set(successor.getColumn(), true);
107 }
108 }
109 }
110 }
111 storm::storage::BitVector sccAsBitVector(x.size(), false);
112 uint64_t sccIndex = 0;
113 storm::utility::ProgressMeasurement progress("states");
114 progress.setMaxCount(x.size());
115 progress.startNewMeasurement(0);
116 for (auto const& scc : *this->sortedSccDecomposition) {
117 if (scc.size() == 1) {
118 returnValue = solveTrivialScc(*scc.begin(), x, b) && returnValue;
119 } else {
120 sccAsBitVector.clear();
121 for (auto const& state : scc) {
122 sccAsBitVector.set(state, true);
123 }
124 returnValue = solveScc(sccSolverEnvironment, sccAsBitVector, x, b, newRelevantValues) && returnValue;
125 }
126 ++sccIndex;
127 progress.updateProgress(sccIndex);
129 STORM_LOG_WARN("Topological solver aborted after analyzing " << sccIndex << "/" << this->sortedSccDecomposition->size() << " SCCs.");
130 break;
131 }
132 }
133 }
134
135 if (!this->isCachingEnabled()) {
136 clearCache();
137 }
138
139 return returnValue;
140}
141
142template<typename ValueType>
143void TopologicalLinearEquationSolver<ValueType>::createSortedSccDecomposition(bool needLongestChainSize) const {
144 // Obtain the scc decomposition
145 this->sortedSccDecomposition = std::make_unique<storm::storage::StronglyConnectedComponentDecomposition<ValueType>>(
147 if (needLongestChainSize) {
148 this->longestSccChainSize = this->sortedSccDecomposition->getMaxSccDepth() + 1;
149 }
150}
151
152template<typename ValueType>
153bool TopologicalLinearEquationSolver<ValueType>::solveTrivialScc(uint64_t const& sccState, std::vector<ValueType>& globalX,
154 std::vector<ValueType> const& globalB) const {
155 ValueType& xi = globalX[sccState];
156 xi = globalB[sccState];
157 bool hasDiagonalEntry = false;
158 ValueType denominator;
159 for (auto const& entry : this->A->getRow(sccState)) {
160 if (entry.getColumn() == sccState) {
161 STORM_LOG_ASSERT(!storm::utility::isOne(entry.getValue()), "Diagonal entry of fix point system has value 1.");
162 hasDiagonalEntry = true;
163 denominator = storm::utility::one<ValueType>() - entry.getValue();
164 } else {
165 xi += entry.getValue() * globalX[entry.getColumn()];
166 }
167 }
168
169 if (hasDiagonalEntry) {
172 "State " << sccState << " has a selfloop with probability '1-(" << denominator << ")'. This could be an indication for numerical issues.");
173 if (storm::utility::isZero(denominator)) {
174 STORM_LOG_THROW(storm::utility::isZero(xi), storm::exceptions::InvalidOperationException, "The equation system has no solution.");
175 } else {
176 xi /= denominator;
177 }
178 }
179 return true;
180}
181
182template<typename ValueType>
183bool TopologicalLinearEquationSolver<ValueType>::solveFullyConnectedEquationSystem(storm::Environment const& sccSolverEnvironment, std::vector<ValueType>& x,
184 std::vector<ValueType> const& b) const {
185 if (!this->sccSolver) {
186 this->sccSolver = GeneralLinearEquationSolverFactory<ValueType>().create(sccSolverEnvironment);
187 this->sccSolver->setCachingEnabled(true);
188 }
189 if (this->hasRelevantValues()) {
190 this->sccSolver->setRelevantValues(this->getRelevantValues());
191 }
192 this->sccSolver->setBoundsFromOtherSolver(*this);
193 if (this->sccSolver->getEquationProblemFormat(sccSolverEnvironment) == LinearEquationSolverProblemFormat::EquationSystem) {
194 // Convert the matrix to an equation system. Note that we need to insert diagonal entries.
195 storm::storage::SparseMatrix<ValueType> eqSysA(*this->A, true);
196 eqSysA.convertToEquationSystem();
197 this->sccSolver->setMatrix(std::move(eqSysA));
198 } else {
199 this->sccSolver->setMatrix(*this->A);
200 }
201
202 return this->sccSolver->solveEquations(sccSolverEnvironment, x, b);
203}
204
205template<typename ValueType>
206bool TopologicalLinearEquationSolver<ValueType>::solveScc(storm::Environment const& sccSolverEnvironment, storm::storage::BitVector const& scc,
207 std::vector<ValueType>& globalX, std::vector<ValueType> const& globalB,
208 std::optional<storm::storage::BitVector> const& globalRelevantValues) const {
209 // Set up the SCC solver
210 if (!this->sccSolver) {
211 this->sccSolver = GeneralLinearEquationSolverFactory<ValueType>().create(sccSolverEnvironment);
212 this->sccSolver->setCachingEnabled(true);
213 }
214 if (globalRelevantValues) {
215 this->sccSolver->setRelevantValues((*globalRelevantValues) % scc);
216 }
217
218 // Matrix
219 bool asEquationSystem = this->sccSolver->getEquationProblemFormat(sccSolverEnvironment) == LinearEquationSolverProblemFormat::EquationSystem;
220 storm::storage::SparseMatrix<ValueType> sccA = this->A->getSubmatrix(true, scc, scc, asEquationSystem);
221 if (asEquationSystem) {
223 }
224 this->sccSolver->setMatrix(std::move(sccA));
225
226 // x Vector
227 auto sccX = storm::utility::vector::filterVector(globalX, scc);
228
229 // b Vector
230 std::vector<ValueType> sccB;
231 sccB.reserve(scc.getNumberOfSetBits());
232 for (auto row : scc) {
233 ValueType bi = globalB[row];
234 for (auto const& entry : this->A->getRow(row)) {
235 if (!scc.get(entry.getColumn())) {
236 bi += entry.getValue() * globalX[entry.getColumn()];
237 }
238 }
239 sccB.push_back(std::move(bi));
240 }
241
242 // lower/upper bounds
244 this->sccSolver->setLowerBound(this->getLowerBound());
246 this->sccSolver->setLowerBounds(storm::utility::vector::filterVector(this->getLowerBounds(), scc));
247 }
249 this->sccSolver->setUpperBound(this->getUpperBound());
251 this->sccSolver->setUpperBounds(storm::utility::vector::filterVector(this->getUpperBounds(), scc));
252 }
253
254 // std::cout << "rhs is " << storm::utility::vector::toString(sccB) << '\n';
255 // std::cout << "x is " << storm::utility::vector::toString(sccX) << '\n';
256
257 bool returnvalue = this->sccSolver->solveEquations(sccSolverEnvironment, sccX, sccB);
258 storm::utility::vector::setVectorValues(globalX, scc, sccX);
259 return returnvalue;
260}
261
262template<typename ValueType>
266
267template<typename ValueType>
269 // Return the requirements of the underlying solver
270 return GeneralLinearEquationSolverFactory<ValueType>().getRequirements(getEnvironmentForUnderlyingSolver(env));
271}
272
273template<typename ValueType>
275 sortedSccDecomposition.reset();
276 longestSccChainSize = boost::none;
277 sccSolver.reset();
279}
280
281template<typename ValueType>
282uint64_t TopologicalLinearEquationSolver<ValueType>::getMatrixRowCount() const {
283 return this->A->getRowCount();
284}
285
286template<typename ValueType>
287uint64_t TopologicalLinearEquationSolver<ValueType>::getMatrixColumnCount() const {
288 return this->A->getColumnCount();
289}
290
291template<typename ValueType>
292std::unique_ptr<storm::solver::LinearEquationSolver<ValueType>> TopologicalLinearEquationSolverFactory<ValueType>::create(Environment const& env) const {
293 return std::make_unique<storm::solver::TopologicalLinearEquationSolver<ValueType>>();
294}
295
296template<typename ValueType>
297std::unique_ptr<LinearEquationSolverFactory<ValueType>> TopologicalLinearEquationSolverFactory<ValueType>::clone() const {
298 return std::make_unique<TopologicalLinearEquationSolverFactory<ValueType>>(*this);
299}
300
301// Explicitly instantiate the linear equation solver.
304
307
310
311} // namespace solver
312} // namespace storm
SolverEnvironment & solver()
TopologicalSolverEnvironment & topological()
std::pair< boost::optional< storm::RationalNumber >, boost::optional< bool > > getPrecisionOfLinearEquationSolver(storm::solver::EquationSolverType const &solverType) const
storm::solver::EquationSolverType const & getUnderlyingEquationSolverType() const
storm::storage::BitVector const & getRelevantValues() const
Retrieves the relevant values (if there are any).
virtual std::unique_ptr< LinearEquationSolver< ValueType > > create(Environment const &env) const override
Creates an equation solver with the current settings, but without a matrix.
LinearEquationSolverRequirements getRequirements(Environment const &env) const
Retrieves the requirements of the solver if it was created with the current settings.
bool isCachingEnabled() const
Retrieves whether some of the generated data during solver calls should be cached.
virtual std::unique_ptr< LinearEquationSolverFactory< ValueType > > clone() const override
Creates a copy of this factory.
virtual std::unique_ptr< storm::solver::LinearEquationSolver< ValueType > > create(Environment const &env) const override
Creates an equation solver with the current settings, but without a matrix.
virtual LinearEquationSolverProblemFormat getEquationProblemFormat(storm::Environment const &env) const override
Retrieves the format in which this solver expects to solve equations.
virtual bool internalSolveEquations(storm::Environment const &env, std::vector< ValueType > &x, std::vector< ValueType > const &b) const override
virtual void setMatrix(storm::storage::SparseMatrix< ValueType > const &A) override
virtual LinearEquationSolverRequirements getRequirements(Environment const &env) const override
Retrieves the requirements of the solver under the current settings.
A bit vector that is internally represented as a vector of 64-bit values.
Definition BitVector.h:16
void clear()
Removes all set bits from the 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.
A class that holds a possibly non-square matrix in the compressed row storage format.
void convertToEquationSystem()
Transforms the matrix into an equation system.
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.
A class that provides convenience operations to display run times.
Definition Stopwatch.h:14
void stop()
Stop stopwatch and add measured time to total time.
Definition Stopwatch.cpp:42
#define STORM_LOG_INFO(message)
Definition logging.h:24
#define STORM_LOG_WARN(message)
Definition logging.h:25
#define STORM_LOG_TRACE(message)
Definition logging.h:12
#define STORM_LOG_ASSERT(cond, message)
Definition macros.h:11
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:30
#define STORM_LOG_WARN_COND_DEBUG(cond, message)
Definition macros.h:18
SFTBDDChecker::ValueType ValueType
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.
Definition vector.h:78
std::vector< Type > filterVector(std::vector< Type > const &in, storm::storage::BitVector const &filter)
Definition vector.h:1060
bool isOne(ValueType const &a)
Definition constants.cpp:34
NumberTraits< RationalType >::IntegerType denominator(RationalType const &number)
bool isAlmostZero(ValueType const &a)
Definition constants.cpp:93
bool isZero(ValueType const &a)
Definition constants.cpp:39
ValueType one()
Definition constants.cpp:19
TargetType convertNumber(SourceType const &number)
static const bool IsExact
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.