Storm 1.14.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
NativeLinearEquationSolver.cpp
Go to the documentation of this file.
2
3#include <limits>
4
20
21namespace storm {
22namespace solver {
23
24template<typename ValueType>
26 // Intentionally left empty.
27}
28
29template<typename ValueType>
33
34template<typename ValueType>
38
39template<typename ValueType>
45
46template<typename ValueType>
48 localA = std::make_unique<storm::storage::SparseMatrix<ValueType>>(std::move(A));
49 this->A = localA.get();
50 clearCache();
51}
52
53template<typename ValueType>
54void NativeLinearEquationSolver<ValueType>::setUpViOperator() const {
55 if (!viOperator) {
56 viOperator = std::make_shared<helper::ValueIterationOperator<ValueType, true>>();
57 viOperator->setMatrixBackwards(*this->A);
58 }
59}
60
61template<typename ValueType>
62bool NativeLinearEquationSolver<ValueType>::solveEquationsSOR(Environment const& env, std::vector<ValueType>& x, std::vector<ValueType> const& b,
63 ValueType const& omega) const {
64 STORM_LOG_INFO("Solving linear equation system (" << x.size() << " rows) with NativeLinearEquationSolver (Gauss-Seidel, SOR omega = " << omega << ")");
65
66 if (!this->cachedRowVector) {
67 this->cachedRowVector = std::make_unique<std::vector<ValueType>>(getMatrixRowCount());
68 }
69
70 ValueType precision = storm::utility::convertNumber<ValueType>(env.solver().native().getPrecision());
71 uint64_t maxIter = env.solver().native().getMaximalNumberOfIterations();
72 bool relative = env.solver().native().getRelativeTerminationCriterion();
73
74 // Set up additional environment variables.
75 uint_fast64_t iterations = 0;
77
78 this->startMeasureProgress();
79 while (status == SolverStatus::InProgress && iterations < maxIter) {
80 A->performSuccessiveOverRelaxationStep(omega, x, b);
81
82 // Now check if the process already converged within our precision.
83 if (storm::utility::vector::equalModuloPrecision<ValueType>(*this->cachedRowVector, x, precision, relative)) {
85 }
86 // If we did not yet converge, we need to backup the contents of x.
87 if (status != SolverStatus::Converged) {
88 *this->cachedRowVector = x;
89 }
90
91 // Potentially show progress.
92 this->showProgressIterative(iterations);
93
94 // Increase iteration count so we can abort if convergence is too slow.
95 ++iterations;
96
97 status = this->updateStatus(status, x, SolverGuarantee::None, iterations, maxIter);
98 }
99
100 if (!this->isCachingEnabled()) {
101 clearCache();
102 }
103
104 this->reportStatus(status, iterations);
105
106 return status == SolverStatus::Converged;
107}
108
109template<typename ValueType>
110NativeLinearEquationSolver<ValueType>::JacobiDecomposition::JacobiDecomposition(Environment const& env, storm::storage::SparseMatrix<ValueType> const& A) {
111 auto decomposition = A.getJacobiDecomposition();
112 this->LUMatrix = std::move(decomposition.first);
113 this->DVector = std::move(decomposition.second);
114 this->multiplier = storm::solver::MultiplierFactory<ValueType>().create(env, this->LUMatrix);
115}
116
117template<typename ValueType>
118bool NativeLinearEquationSolver<ValueType>::solveEquationsJacobi(Environment const& env, std::vector<ValueType>& x, std::vector<ValueType> const& b) const {
119 STORM_LOG_INFO("Solving linear equation system (" << x.size() << " rows) with NativeLinearEquationSolver (Jacobi)");
120
121 if (!this->cachedRowVector) {
122 this->cachedRowVector = std::make_unique<std::vector<ValueType>>(getMatrixRowCount());
123 }
124
125 // Get a Jacobi decomposition of the matrix A.
126 if (!jacobiDecomposition) {
127 jacobiDecomposition = std::make_unique<JacobiDecomposition>(env, *A);
128 }
129
130 ValueType precision = storm::utility::convertNumber<ValueType>(env.solver().native().getPrecision());
131 uint64_t maxIter = env.solver().native().getMaximalNumberOfIterations();
132 bool relative = env.solver().native().getRelativeTerminationCriterion();
133
134 std::vector<ValueType>* currentX = &x;
135 std::vector<ValueType>* nextX = this->cachedRowVector.get();
136
137 // Set up additional environment variables.
138 uint_fast64_t iterations = 0;
140
141 this->startMeasureProgress();
142 while (status == SolverStatus::InProgress && iterations < maxIter) {
143 // Compute D^-1 * (b - LU * x) and store result in nextX.
144 jacobiDecomposition->multiplier->multiply(env, *currentX, nullptr, *nextX);
146 storm::utility::vector::multiplyVectorsPointwise(jacobiDecomposition->DVector, *nextX, *nextX);
147
148 // Now check if the process already converged within our precision.
149 if (storm::utility::vector::equalModuloPrecision<ValueType>(*currentX, *nextX, precision, relative)) {
151 }
152 // Swap the two pointers as a preparation for the next iteration.
153 std::swap(nextX, currentX);
154
155 // Potentially show progress.
156 this->showProgressIterative(iterations);
157
158 // Increase iteration count so we can abort if convergence is too slow.
159 ++iterations;
160
161 status = this->updateStatus(status, *currentX, SolverGuarantee::None, iterations, maxIter);
162 }
163
164 // If the last iteration did not write to the original x we have to swap the contents, because the
165 // output has to be written to the input parameter x.
166 if (currentX == this->cachedRowVector.get()) {
167 std::swap(x, *currentX);
168 }
169
170 if (!this->isCachingEnabled()) {
171 clearCache();
172 }
173
174 this->reportStatus(status, iterations);
175
176 return status == SolverStatus::Converged;
177}
178
179template<typename ValueType>
180NativeLinearEquationSolver<ValueType>::WalkerChaeData::WalkerChaeData(Environment const& env, storm::storage::SparseMatrix<ValueType> const& originalMatrix,
181 std::vector<ValueType> const& originalB)
182 : t(storm::utility::convertNumber<ValueType>(1000.0)) {
183 computeWalkerChaeMatrix(originalMatrix);
184 computeNewB(originalB);
185 precomputeAuxiliaryData();
186 multiplier = storm::solver::MultiplierFactory<ValueType>().create(env, this->matrix);
187}
188
189template<typename ValueType>
190void NativeLinearEquationSolver<ValueType>::WalkerChaeData::computeWalkerChaeMatrix(storm::storage::SparseMatrix<ValueType> const& originalMatrix) {
191 storm::storage::BitVector columnsWithNegativeEntries(originalMatrix.getColumnCount());
193 for (auto const& e : originalMatrix) {
194 if (e.getValue() < zero) {
195 columnsWithNegativeEntries.set(e.getColumn());
196 }
197 }
198 std::vector<uint64_t> columnsWithNegativeEntriesBefore = columnsWithNegativeEntries.getNumberOfSetBitsBeforeIndices();
199
200 // We now build an extended equation system matrix that only has non-negative coefficients.
201 storm::storage::SparseMatrixBuilder<ValueType> builder;
202
203 uint64_t row = 0;
204 for (; row < originalMatrix.getRowCount(); ++row) {
205 for (auto const& entry : originalMatrix.getRow(row)) {
206 if (entry.getValue() < zero) {
207 builder.addNextValue(row, originalMatrix.getRowCount() + columnsWithNegativeEntriesBefore[entry.getColumn()], -entry.getValue());
208 } else {
209 builder.addNextValue(row, entry.getColumn(), entry.getValue());
210 }
211 }
212 }
214 for (uint64_t column : columnsWithNegativeEntries) {
215 builder.addNextValue(row, column, one);
216 builder.addNextValue(row, originalMatrix.getRowCount() + columnsWithNegativeEntriesBefore[column], one);
217 ++row;
218 }
219
220 matrix = builder.build();
221}
222
223template<typename ValueType>
224void NativeLinearEquationSolver<ValueType>::WalkerChaeData::computeNewB(std::vector<ValueType> const& originalB) {
225 b = std::vector<ValueType>(originalB);
226 b.resize(matrix.getRowCount());
227}
228
229template<typename ValueType>
231 columnSums = std::vector<ValueType>(matrix.getColumnCount());
232 for (auto const& e : matrix) {
233 columnSums[e.getColumn()] += e.getValue();
234 }
235
236 newX.resize(matrix.getRowCount());
237}
238
239template<typename ValueType>
240bool NativeLinearEquationSolver<ValueType>::solveEquationsWalkerChae(Environment const& env, std::vector<ValueType>& x, std::vector<ValueType> const& b) const {
241 STORM_LOG_INFO("Solving linear equation system (" << x.size() << " rows) with NativeLinearEquationSolver (WalkerChae)");
242
243 // (1) Compute an equivalent equation system that has only non-negative coefficients.
244 if (!walkerChaeData) {
245 walkerChaeData = std::make_unique<WalkerChaeData>(env, *this->A, b);
246 }
247
248 // (2) Enlarge the vectors x and b to account for additional variables.
249 x.resize(walkerChaeData->matrix.getRowCount());
250
251 // Square the error bound, so we can use it to check for convergence. We take the squared error, because we
252 // do not want to compute the root in the 2-norm computation.
253 ValueType squaredErrorBound = storm::utility::pow(storm::utility::convertNumber<ValueType>(env.solver().native().getPrecision()), 2);
254
255 uint64_t maxIter = env.solver().native().getMaximalNumberOfIterations();
256
257 // Set up references to the x-vectors used in the iteration loop.
258 std::vector<ValueType>* currentX = &x;
259 std::vector<ValueType>* nextX = &walkerChaeData->newX;
260
261 std::vector<ValueType> tmp = walkerChaeData->matrix.getRowSumVector();
262 storm::utility::vector::applyPointwise(tmp, walkerChaeData->b, walkerChaeData->b,
263 [this](ValueType const& first, ValueType const& second) -> ValueType { return walkerChaeData->t * first + second; });
264
265 // Add t to all entries of x.
266 storm::utility::vector::applyPointwise(x, x, [this](ValueType const& value) -> ValueType { return value + walkerChaeData->t; });
267
268 // Create a vector that always holds Ax.
269 std::vector<ValueType> currentAx(x.size());
270 walkerChaeData->multiplier->multiply(env, *currentX, nullptr, currentAx);
271
272 // (3) Perform iterations until convergence.
274 uint64_t iterations = 0;
275 this->startMeasureProgress();
276 while (status == SolverStatus::InProgress && iterations < maxIter) {
277 // Perform one Walker-Chae step.
278 walkerChaeData->matrix.performWalkerChaeStep(*currentX, walkerChaeData->columnSums, walkerChaeData->b, currentAx, *nextX);
279
280 // Compute new Ax.
281 walkerChaeData->multiplier->multiply(env, *nextX, nullptr, currentAx);
282
283 // Check for convergence.
284 if (storm::utility::vector::computeSquaredNorm2Difference(currentAx, walkerChaeData->b) <= squaredErrorBound) {
286 }
287
288 // Swap the x vectors for the next iteration.
289 std::swap(currentX, nextX);
290
291 // Potentially show progress.
292 this->showProgressIterative(iterations);
293
294 // Increase iteration count so we can abort if convergence is too slow.
295 ++iterations;
296
298 status = SolverStatus::Aborted;
299 }
300 }
301
302 // If the last iteration did not write to the original x we have to swap the contents, because the
303 // output has to be written to the input parameter x.
304 if (currentX == &walkerChaeData->newX) {
305 std::swap(x, *currentX);
306 }
307
308 // Resize the solution to the right size.
309 x.resize(this->A->getRowCount());
310
311 // Finalize solution vector.
312 storm::utility::vector::applyPointwise(x, x, [this](ValueType const& value) -> ValueType { return value - walkerChaeData->t; });
313
314 if (!this->isCachingEnabled()) {
315 clearCache();
316 }
317
318 this->reportStatus(status, iterations);
319
320 return status == SolverStatus::Converged;
321}
322
323template<typename ValueType>
325 Environment const& env, std::vector<ValueType>*& currentX, std::vector<ValueType>*& newX, std::vector<ValueType> const& b, ValueType const& precision,
326 bool relative, SolverGuarantee const& guarantee, uint64_t currentIterations, uint64_t maxIterations,
327 storm::solver::MultiplicationStyle const& multiplicationStyle) const {
328 bool useGaussSeidelMultiplication = multiplicationStyle == storm::solver::MultiplicationStyle::GaussSeidel;
329
330 uint64_t iterations = currentIterations;
332 while (status == SolverStatus::InProgress && iterations < maxIterations) {
333 if (useGaussSeidelMultiplication) {
334 *newX = *currentX;
335 this->multiplier->multiplyGaussSeidel(env, *newX, &b);
336 } else {
337 this->multiplier->multiply(env, *currentX, &b, *newX);
338 }
339
340 // Check for convergence.
341 if (storm::utility::vector::equalModuloPrecision<ValueType>(*currentX, *newX, precision, relative)) {
343 }
344
345 // Check for termination.
346 std::swap(currentX, newX);
347 ++iterations;
348
349 status = this->updateStatus(status, *currentX, guarantee, iterations, maxIterations);
350
351 // Potentially show progress.
352 this->showProgressIterative(iterations);
353 }
354
355 return PowerIterationResult(iterations - currentIterations, status);
356}
357
358template<typename ValueType>
359bool NativeLinearEquationSolver<ValueType>::solveEquationsPower(Environment const& env, std::vector<ValueType>& x, std::vector<ValueType> const& b) const {
360 STORM_LOG_INFO("Solving linear equation system (" << x.size() << " rows) with NativeLinearEquationSolver (Power)");
361 // Prepare the solution vectors.
362 setUpViOperator();
363
365 if (this->hasCustomTerminationCondition()) {
366 if (this->getTerminationCondition().requiresGuarantee(SolverGuarantee::LessOrEqual) && this->hasLowerBound()) {
369 } else if (this->getTerminationCondition().requiresGuarantee(SolverGuarantee::GreaterOrEqual) && this->hasUpperBound()) {
372 }
373 }
374
375 storm::solver::helper::ValueIterationHelper<ValueType, true> viHelper(viOperator);
376 uint64_t numIterations{0};
377 auto viCallback = [&](SolverStatus const& current) {
378 this->showProgressIterative(numIterations);
379 return this->updateStatus(current, x, guarantee, numIterations, env.solver().native().getMaximalNumberOfIterations());
380 };
381 this->startMeasureProgress();
382 // Reading the direction of every sweep costs in the innermost loop, so unless the initial vector already carries
383 // a guarantee, only do it where soundness was asked for.
384 storm::solver::SolutionBounds<ValueType> solutionBounds;
386 if (guarantee != SolverGuarantee::None || env.solver().isForceSoundness()) {
387 solutionBoundsRef.reset(solutionBounds);
388 }
389 auto status = viHelper.VI(x, b, numIterations, env.solver().native().getRelativeTerminationCriterion(),
390 storm::utility::convertNumber<ValueType>(env.solver().native().getPrecision()), {}, viCallback,
391 env.solver().native().getPowerMethodMultiplicationStyle(), UncertaintyResolutionMode::Unset, solutionBoundsRef, guarantee);
392 if (solutionBounds.hasAny()) {
393 this->setSolutionBounds(std::move(solutionBounds));
394 }
395
396 this->reportStatus(status, numIterations);
397
398 if (!this->isCachingEnabled()) {
399 clearCache();
400 }
401
402 return status == SolverStatus::Converged || status == SolverStatus::TerminatedEarly;
403}
404
405template<typename ValueType>
406void preserveOldRelevantValues(std::vector<ValueType> const& allValues, storm::storage::BitVector const& relevantValues, std::vector<ValueType>& oldValues) {
408}
409
410template<typename ValueType>
411ValueType computeMaxAbsDiff(std::vector<ValueType> const& allValues, storm::storage::BitVector const& relevantValues, std::vector<ValueType> const& oldValues) {
412 ValueType result = storm::utility::zero<ValueType>();
413 auto oldValueIt = oldValues.begin();
414 for (uint64_t value : relevantValues) {
415 result = storm::utility::max<ValueType>(result, storm::utility::abs<ValueType>(allValues[value] - *oldValueIt));
416 }
417 return result;
418}
419
420template<typename ValueType>
421ValueType computeMaxAbsDiff(std::vector<ValueType> const& allOldValues, std::vector<ValueType> const& allNewValues,
423 ValueType result = storm::utility::zero<ValueType>();
424 for (uint64_t value : relevantValues) {
425 result = storm::utility::max<ValueType>(result, storm::utility::abs<ValueType>(allNewValues[value] - allOldValues[value]));
426 }
427 return result;
428}
429
430template<typename ValueType>
431bool NativeLinearEquationSolver<ValueType>::solveEquationsIntervalIteration(Environment const& env, std::vector<ValueType>& x,
432 std::vector<ValueType> const& b) const {
433 STORM_LOG_THROW(this->hasLowerBound(), storm::exceptions::UnmetRequirementException, "Solver requires lower bound, but none was given.");
434 STORM_LOG_THROW(this->hasUpperBound(), storm::exceptions::UnmetRequirementException, "Solver requires upper bound, but none was given.");
435 STORM_LOG_INFO("Solving linear equation system (" << x.size() << " rows) with NativeLinearEquationSolver (IntervalIteration)");
436 setUpViOperator();
437 helper::IntervalIterationHelper<ValueType, true> iiHelper(viOperator);
439 auto lowerBoundsCallback = [&](std::vector<ValueType>& vector) { this->createLowerBoundsVector(vector); };
440 auto upperBoundsCallback = [&](std::vector<ValueType>& vector) { this->createUpperBoundsVector(vector); };
441
442 uint64_t numIterations{0};
443 auto iiCallback = [&](helper::IIData<ValueType> const& data) {
444 this->showProgressIterative(numIterations);
445 bool terminateEarly = this->hasCustomTerminationCondition() && (this->getTerminationCondition().terminateNow(data.x, SolverGuarantee::LessOrEqual) ||
446 this->getTerminationCondition().terminateNow(data.y, SolverGuarantee::GreaterOrEqual));
447 return this->updateStatus(data.status, terminateEarly, numIterations, env.solver().native().getMaximalNumberOfIterations());
448 };
449 std::optional<storm::storage::BitVector> optionalRelevantValues;
450 if (this->hasRelevantValues()) {
451 optionalRelevantValues = this->getRelevantValues();
452 }
453 this->startMeasureProgress();
454 storm::solver::SolutionBounds<ValueType> solutionBounds;
455 auto status = iiHelper.II(x, b, numIterations, env.solver().native().getRelativeTerminationCriterion(), prec, lowerBoundsCallback, upperBoundsCallback, {},
456 iiCallback, optionalRelevantValues, solutionBounds);
457 if (solutionBounds.hasAny()) {
458 this->setSolutionBounds(std::move(solutionBounds));
459 }
460 this->reportStatus(status, numIterations);
461
462 if (!this->isCachingEnabled()) {
463 clearCache();
464 }
465
466 return status == SolverStatus::Converged || status == SolverStatus::TerminatedEarly;
467}
468
469template<typename ValueType>
470bool NativeLinearEquationSolver<ValueType>::solveEquationsSoundValueIteration(Environment const& env, std::vector<ValueType>& x,
471 std::vector<ValueType> const& b) const {
472 // Prepare the solution vectors and the helper.
473 STORM_LOG_ASSERT(x.size() == this->A->getRowGroupCount(), "Solution vector size mismatch.");
474
475 std::optional<ValueType> lowerBound, upperBound;
476 if (this->hasLowerBound()) {
477 lowerBound = this->getLowerBound(true);
478 }
479 if (this->hasUpperBound()) {
480 upperBound = this->getUpperBound(true);
481 }
482
483 setUpViOperator();
484
485 auto precision = storm::utility::convertNumber<ValueType>(env.solver().native().getPrecision());
486 uint64_t numIterations{0};
487 auto sviCallback = [&](typename helper::SoundValueIterationHelper<ValueType, true>::SVIData const& current) {
488 this->showProgressIterative(numIterations);
489 return this->updateStatus(current.status,
490 this->hasCustomTerminationCondition() && current.checkCustomTerminationCondition(this->getTerminationCondition()),
491 numIterations, env.solver().native().getMaximalNumberOfIterations());
492 };
493 std::optional<storm::storage::BitVector> optionalRelevantValues;
494 if (this->hasRelevantValues()) {
495 optionalRelevantValues = this->getRelevantValues();
496 }
497 this->startMeasureProgress();
498 helper::SoundValueIterationHelper<ValueType, true> sviHelper(viOperator);
499 storm::solver::SolutionBounds<ValueType> solutionBounds;
500 auto status = sviHelper.SVI(x, b, numIterations, env.solver().native().getRelativeTerminationCriterion(), precision, {}, lowerBound, upperBound,
501 sviCallback, optionalRelevantValues, solutionBounds);
502 if (solutionBounds.hasAny()) {
503 this->setSolutionBounds(std::move(solutionBounds));
504 }
505
506 this->reportStatus(status, numIterations);
507
508 if (!this->isCachingEnabled()) {
509 clearCache();
510 }
511
512 return status == SolverStatus::Converged || status == SolverStatus::TerminatedEarly;
513}
514
515template<typename ValueType>
516bool NativeLinearEquationSolver<ValueType>::solveEquationsOptimisticValueIteration(Environment const& env, std::vector<ValueType>& x,
517 std::vector<ValueType> const& b) const {
519 // If all entries are zero, OVI might run in an endless loop. However, the result is easy in this case.
520 x.assign(x.size(), storm::utility::zero<ValueType>());
521 return true;
522 }
523
524 setUpViOperator();
525
526 helper::OptimisticValueIterationHelper<ValueType, true> oviHelper(viOperator);
527 auto prec = storm::utility::convertNumber<ValueType>(env.solver().native().getPrecision());
528 std::optional<ValueType> lowerBound, upperBound;
529 if (this->hasLowerBound()) {
530 lowerBound = this->getLowerBound(true);
531 }
532 if (this->hasUpperBound()) {
533 upperBound = this->getUpperBound(true);
534 }
535 uint64_t numIterations{0};
536 auto oviCallback = [&](SolverStatus const& current, std::vector<ValueType> const& v) {
537 this->showProgressIterative(numIterations);
538 return this->updateStatus(current, v, SolverGuarantee::LessOrEqual, numIterations, env.solver().native().getMaximalNumberOfIterations());
539 };
541 std::optional<ValueType> guessingFactor;
542 if (env.solver().ovi().getUpperBoundGuessingFactor()) {
543 guessingFactor = storm::utility::convertNumber<ValueType>(*env.solver().ovi().getUpperBoundGuessingFactor());
544 }
545 this->startMeasureProgress();
546 storm::solver::SolutionBounds<ValueType> solutionBounds;
547 auto status = oviHelper.OVI(x, b, numIterations, env.solver().native().getRelativeTerminationCriterion(), prec, {}, guessingFactor, lowerBound, upperBound,
548 oviCallback, solutionBounds);
549 if (solutionBounds.hasAny()) {
550 this->setSolutionBounds(std::move(solutionBounds));
551 }
552 this->reportStatus(status, numIterations);
553
554 if (!this->isCachingEnabled()) {
555 clearCache();
556 }
557
558 return status == SolverStatus::Converged || status == SolverStatus::TerminatedEarly;
559}
560
561template<typename ValueType>
562bool NativeLinearEquationSolver<ValueType>::solveEquationsGuessingValueIteration(const Environment& env, std::vector<ValueType>& x,
563 const std::vector<ValueType>& b) const {
564 if (!this->cachedRowVector) {
565 this->cachedRowVector = std::make_unique<std::vector<ValueType>>(this->A->getRowCount());
566 }
567 setUpViOperator();
568
569 std::vector<ValueType>* lowerX = &x;
570 this->createLowerBoundsVector(*lowerX);
571 this->createUpperBoundsVector(this->cachedRowVector, this->getMatrixRowCount());
572 std::vector<ValueType>* upperX = this->cachedRowVector.get();
573
574 storm::solver::helper::GuessingValueIterationHelper<ValueType, true> helper(viOperator, *this->A);
575
576 uint64_t numIterations{0};
577 auto gviCallback = [&](helper::GVIData<ValueType> const& data) {
578 this->showProgressIterative(numIterations);
579 bool terminateEarly = this->hasCustomTerminationCondition() && (this->getTerminationCondition().terminateNow(data.x, SolverGuarantee::LessOrEqual) ||
580 this->getTerminationCondition().terminateNow(data.y, SolverGuarantee::GreaterOrEqual));
581 return this->updateStatus(data.status, terminateEarly, numIterations, env.solver().native().getMaximalNumberOfIterations());
582 };
583
584 this->startMeasureProgress();
585 auto status = helper.solveEquations(*lowerX, *upperX, b, numIterations, env.solver().native().getRelativeTerminationCriterion(),
586 storm::utility::convertNumber<ValueType>(env.solver().native().getPrecision()), {}, // No optimization dir
587 gviCallback);
588 // A verified guess encloses the solution in every iteration.
589 storm::solver::SolutionBounds<ValueType> solutionBounds;
590 solutionBounds.lower = *lowerX;
591 solutionBounds.upper = *upperX;
592 this->setSolutionBounds(std::move(solutionBounds));
593
596 *lowerX, *upperX, x, [&two](ValueType const& first, ValueType const& second) -> ValueType { return (first + second) / two; });
597 this->reportStatus(status, numIterations);
598
599 if (!this->isCachingEnabled()) {
600 clearCache();
601 }
602 return status == SolverStatus::Converged || status == SolverStatus::TerminatedEarly;
603}
604
605template<typename ValueType>
606bool NativeLinearEquationSolver<ValueType>::solveEquationsRationalSearch(Environment const& env, std::vector<ValueType>& x,
607 std::vector<ValueType> const& b) const {
608 // Set up two value iteration operators. One for exact and one for imprecise computations
609 setUpViOperator();
610 std::shared_ptr<helper::ValueIterationOperator<storm::RationalNumber, true>> exactOp;
611 std::shared_ptr<helper::ValueIterationOperator<double, true>> impreciseOp;
612
613 if constexpr (std::is_same_v<ValueType, storm::RationalNumber>) {
614 exactOp = viOperator;
615 impreciseOp = std::make_shared<helper::ValueIterationOperator<double, true>>();
616 impreciseOp->setMatrixBackwards(this->A->template toValueType<double>());
617 } else {
618 impreciseOp = viOperator;
619 exactOp = std::make_shared<helper::ValueIterationOperator<storm::RationalNumber, true>>();
620 exactOp->setMatrixBackwards(this->A->template toValueType<storm::RationalNumber>());
621 }
622
623 storm::solver::helper::RationalSearchHelper<ValueType, storm::RationalNumber, double, true> rsHelper(exactOp, impreciseOp);
624 uint64_t numIterations{0};
625 auto rsCallback = [&](SolverStatus const& current) {
626 this->showProgressIterative(numIterations);
627 return this->updateStatus(current, x, SolverGuarantee::None, numIterations, env.solver().native().getMaximalNumberOfIterations());
628 };
629 this->startMeasureProgress();
630 auto status = rsHelper.RS(x, b, numIterations, storm::utility::convertNumber<ValueType>(env.solver().native().getPrecision()), {}, rsCallback);
631
632 // Convergence here means a sharpened candidate was verified to be an exact fixed point.
633 if (status == SolverStatus::Converged) {
634 this->setSolutionBoundsExact(x);
635 }
636
637 this->reportStatus(status, numIterations);
638
639 if (!this->isCachingEnabled()) {
640 clearCache();
641 }
642
643 return status == SolverStatus::Converged || status == SolverStatus::TerminatedEarly;
644}
645
646template<typename ValueType>
647NativeLinearEquationSolverMethod NativeLinearEquationSolver<ValueType>::getMethod(Environment const& env, bool isExactMode) const {
648 // Adjust the method if none was specified and we want exact or sound computations
649 auto method = env.solver().native().getMethod();
650
651 if (isExactMode && method != NativeLinearEquationSolverMethod::RationalSearch) {
652 if (env.solver().native().isMethodSetFromDefault()) {
653 method = NativeLinearEquationSolverMethod::RationalSearch;
655 "Selecting '" + toString(method) +
656 "' as the solution technique to guarantee exact results. If you want to override this, please explicitly specify a different method.");
657 } else {
658 STORM_LOG_WARN("The selected solution method does not guarantee exact results.");
659 }
660 } else if (env.solver().isForceSoundness() && method != NativeLinearEquationSolverMethod::SoundValueIteration &&
661 method != NativeLinearEquationSolverMethod::OptimisticValueIteration && method != NativeLinearEquationSolverMethod::IntervalIteration &&
662 method != NativeLinearEquationSolverMethod::RationalSearch) {
663 if (env.solver().native().isMethodSetFromDefault()) {
664 method = NativeLinearEquationSolverMethod::OptimisticValueIteration;
666 "Selecting '" + toString(method) +
667 "' as the solution technique to guarantee sound results. If you want to override this, please explicitly specify a different method.");
668 } else {
669 STORM_LOG_WARN("The selected solution method does not guarantee sound results.");
670 }
671 }
672 return method;
673}
674
675template<typename ValueType>
676bool NativeLinearEquationSolver<ValueType>::internalSolveEquations(Environment const& env, std::vector<ValueType>& x, std::vector<ValueType> const& b) const {
677 switch (getMethod(env, storm::NumberTraits<ValueType>::IsExact || env.solver().isForceExact())) {
678 case NativeLinearEquationSolverMethod::SOR:
679 return this->solveEquationsSOR(env, x, b, storm::utility::convertNumber<ValueType>(env.solver().native().getSorOmega()));
680 case NativeLinearEquationSolverMethod::GaussSeidel:
681 return this->solveEquationsSOR(env, x, b, storm::utility::one<ValueType>());
682 case NativeLinearEquationSolverMethod::Jacobi:
683 return this->solveEquationsJacobi(env, x, b);
684 case NativeLinearEquationSolverMethod::WalkerChae:
685 return this->solveEquationsWalkerChae(env, x, b);
686 case NativeLinearEquationSolverMethod::Power:
687 return this->solveEquationsPower(env, x, b);
688 case NativeLinearEquationSolverMethod::SoundValueIteration:
689 return this->solveEquationsSoundValueIteration(env, x, b);
690 case NativeLinearEquationSolverMethod::OptimisticValueIteration:
691 return this->solveEquationsOptimisticValueIteration(env, x, b);
692 case NativeLinearEquationSolverMethod::GuessingValueIteration:
693 return this->solveEquationsGuessingValueIteration(env, x, b);
694 case NativeLinearEquationSolverMethod::IntervalIteration:
695 return this->solveEquationsIntervalIteration(env, x, b);
696 case NativeLinearEquationSolverMethod::RationalSearch:
697 return this->solveEquationsRationalSearch(env, x, b);
698 }
699 STORM_LOG_THROW(false, storm::exceptions::InvalidEnvironmentException, "Unknown solving technique.");
700 return false;
701}
702
703template<typename ValueType>
705 auto method = getMethod(env, storm::NumberTraits<ValueType>::IsExact || env.solver().isForceExact());
706 if (method == NativeLinearEquationSolverMethod::Power || method == NativeLinearEquationSolverMethod::SoundValueIteration ||
707 method == NativeLinearEquationSolverMethod::OptimisticValueIteration || method == NativeLinearEquationSolverMethod::RationalSearch ||
708 method == NativeLinearEquationSolverMethod::IntervalIteration || method == NativeLinearEquationSolverMethod::GuessingValueIteration) {
710 } else {
712 }
713}
714
715template<typename ValueType>
718 auto method = getMethod(env, storm::NumberTraits<ValueType>::IsExact || env.solver().isForceExact());
719 if (method == NativeLinearEquationSolverMethod::IntervalIteration || method == NativeLinearEquationSolverMethod::GuessingValueIteration) {
720 requirements.requireBounds();
721 } else if (method == NativeLinearEquationSolverMethod::RationalSearch || method == NativeLinearEquationSolverMethod::OptimisticValueIteration) {
722 requirements.requireLowerBounds();
723 } else if (method == NativeLinearEquationSolverMethod::SoundValueIteration) {
724 requirements.requireBounds(false);
725 }
726 return requirements;
727}
728
729template<typename ValueType>
731 jacobiDecomposition.reset();
732 walkerChaeData.reset();
733 multiplier.reset();
734 viOperator.reset();
736}
737
738template<typename ValueType>
739uint64_t NativeLinearEquationSolver<ValueType>::getMatrixRowCount() const {
740 return this->A->getRowCount();
741}
742
743template<typename ValueType>
744uint64_t NativeLinearEquationSolver<ValueType>::getMatrixColumnCount() const {
745 return this->A->getColumnCount();
746}
747
748template<typename ValueType>
749std::unique_ptr<storm::solver::LinearEquationSolver<ValueType>> NativeLinearEquationSolverFactory<ValueType>::create(Environment const& env) const {
750 auto solver = std::make_unique<storm::solver::NativeLinearEquationSolver<ValueType>>();
751 solver->setShowProgress(env.solver().isVerboseSet(), env.solver().getShowProgressDelay());
752 return solver;
753}
754
755template<typename ValueType>
756std::unique_ptr<LinearEquationSolverFactory<ValueType>> NativeLinearEquationSolverFactory<ValueType>::clone() const {
757 return std::make_unique<NativeLinearEquationSolverFactory<ValueType>>(*this);
758}
759
760// Explicitly instantiate the linear equation solver.
763
766
767} // namespace solver
768} // namespace storm
SolverEnvironment & solver()
storm::RationalNumber const & getPrecision() const
uint64_t const & getMaximalNumberOfIterations() const
bool const & getRelativeTerminationCriterion() const
storm::RationalNumber const & getSorOmega() const
void reset()
Unsets the reference.
uint64_t getShowProgressDelay() const
NativeSolverEnvironment & native()
bool hasLowerBound(BoundType const &type=BoundType::Any) const
void setSolutionBoundsExact(std::vector< ValueType > const &x) const
void setSolutionBounds(SolutionBounds< ValueType > bounds) const
void showProgressIterative(uint64_t iterations, boost::optional< uint64_t > const &bound=boost::none) const
storm::storage::BitVector const & getRelevantValues() const
bool hasUpperBound(BoundType const &type=BoundType::Any) const
SolverStatus updateStatus(SolverStatus status, std::vector< ValueType > const &x, SolverGuarantee const &guarantee, uint64_t iterations, uint64_t maximalNumberOfIterations) const
void createUpperBoundsVector(std::vector< ValueType > &upperBoundsVector) const
boost::optional< storm::storage::BitVector > relevantValues
void startMeasureProgress(uint64_t startingIteration=0) const
TerminationCondition< ValueType > const & getTerminationCondition() const
bool terminateNow(std::vector< ValueType > const &values, SolverGuarantee const &guarantee) const
void reportStatus(SolverStatus status, boost::optional< uint64_t > const &iterations=boost::none) const
void createLowerBoundsVector(std::vector< ValueType > &lowerBoundsVector) const
bool isCachingEnabled() const
Retrieves whether some of the generated data during solver calls should be cached.
std::unique_ptr< std::vector< ValueType > > cachedRowVector
LinearEquationSolverRequirements & requireLowerBounds(bool critical=true)
LinearEquationSolverRequirements & requireBounds(bool critical=true)
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 std::unique_ptr< LinearEquationSolverFactory< ValueType > > clone() const override
Creates a copy of this factory.
A class that uses storm's native matrix operations to implement the LinearEquationSolver interface.
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 addNextValue(index_type row, index_type column, value_type const &value)
Sets the matrix entry at the given row and column to the given value.
SparseMatrix< value_type > build(index_type overriddenRowCount=0, index_type overriddenColumnCount=0, index_type overriddenRowGroupCount=0)
A class that holds a possibly non-square matrix in the compressed row storage format.
index_type getColumnCount() const
Returns the number of columns of the matrix.
std::pair< storm::storage::SparseMatrix< value_type >, std::vector< value_type > > getJacobiDecomposition() const
Calculates the Jacobi decomposition of this sparse matrix.
index_type getRowCount() const
Returns the number of rows of the matrix.
#define STORM_LOG_INFO(message)
Definition logging.h:34
#define STORM_LOG_WARN(message)
Definition logging.h:35
#define STORM_LOG_ASSERT(cond, message)
Definition macros.h:9
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:39
SFTBDDChecker::ValueType ValueType
void preserveOldRelevantValues(std::vector< ValueType > const &allValues, storm::storage::BitVector const &relevantValues, std::vector< ValueType > &oldValues)
std::string toString(GurobiSolverMethod const &method)
Yields a string representation of the GurobiSolverMethod.
ValueType computeMaxAbsDiff(std::vector< ValueType > const &allValues, storm::storage::BitVector const &relevantValues, std::vector< ValueType > const &oldValues)
bool isTerminate()
Check whether the program should terminate (due to some abort signal).
bool equalModuloPrecision(T const &val1, T const &val2, T const &precision, bool relativeError=true)
Compares the given elements and determines whether they are equal modulo the given precision.
Definition vector.h:732
void selectVectorValues(std::vector< T > &vector, storm::storage::BitVector const &positions, std::vector< T > const &values)
Selects the elements from a vector at the specified positions and writes them consecutively into anot...
Definition vector.h:185
T computeSquaredNorm2Difference(std::vector< T > const &b1, std::vector< T > const &b2)
Definition vector.h:872
void multiplyVectorsPointwise(std::vector< InValueType1 > const &firstOperand, std::vector< InValueType2 > const &secondOperand, std::vector< OutValueType > &target)
Multiplies the two given vectors (pointwise) and writes the result to the target vector.
Definition vector.h:424
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...
Definition vector.h:375
void subtractVectors(std::vector< InValueType1 > const &firstOperand, std::vector< InValueType2 > const &secondOperand, std::vector< OutValueType > &target)
Subtracts the two given vectors and writes the result to the target vector.
Definition vector.h:412
bool hasNonZeroEntry(std::vector< T > const &v)
Definition vector.h:1142
ValueType max(ValueType const &first, ValueType const &second)
ValueType abs(ValueType const &number)
ValueType zero()
Definition constants.cpp:26
ValueType pow(ValueType const &value, int_fast64_t exponent)
ValueType one()
Definition constants.cpp:21
TargetType convertNumber(SourceType const &number)
OptionalRef(T &) -> OptionalRef< T >
deduction guides
static const bool IsExact