Storm 1.11.1.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>
31 localA.reset();
32 this->A = &A;
33 clearCache();
34}
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>
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>
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>
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
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: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.
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.
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: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
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.