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