25template<
typename ValueType>
30template<
typename ValueType>
35template<
typename ValueType>
40template<
typename ValueType>
47template<
typename ValueType>
49 localA = std::make_unique<storm::storage::SparseMatrix<ValueType>>(std::move(A));
50 this->A = localA.get();
54template<
typename ValueType>
57 viOperator = std::make_shared<helper::ValueIterationOperator<ValueType, true>>();
58 viOperator->setMatrixBackwards(*this->A);
62template<
typename ValueType>
63bool NativeLinearEquationSolver<ValueType>::solveEquationsSOR(Environment
const& env, std::vector<ValueType>& x, std::vector<ValueType>
const& b,
64 ValueType
const& omega)
const {
65 STORM_LOG_INFO(
"Solving linear equation system (" << x.size() <<
" rows) with NativeLinearEquationSolver (Gauss-Seidel, SOR omega = " << omega <<
")");
67 if (!this->cachedRowVector) {
68 this->cachedRowVector = std::make_unique<std::vector<ValueType>>(getMatrixRowCount());
71 ValueType precision = storm::utility::convertNumber<ValueType>(env.solver().native().getPrecision());
72 uint64_t maxIter = env.solver().native().getMaximalNumberOfIterations();
73 bool relative = env.solver().native().getRelativeTerminationCriterion();
76 uint_fast64_t iterations = 0;
79 this->startMeasureProgress();
81 A->performSuccessiveOverRelaxationStep(omega, x, b);
84 if (storm::utility::vector::equalModuloPrecision<ValueType>(*this->cachedRowVector, x, precision, relative)) {
89 *this->cachedRowVector = x;
93 this->showProgressIterative(iterations);
101 if (!this->isCachingEnabled()) {
105 this->reportStatus(status, iterations);
110template<
typename ValueType>
113 this->LUMatrix = std::move(decomposition.first);
114 this->DVector = std::move(decomposition.second);
118template<
typename ValueType>
119bool NativeLinearEquationSolver<ValueType>::solveEquationsJacobi(Environment
const& env, std::vector<ValueType>& x, std::vector<ValueType>
const& b)
const {
120 STORM_LOG_INFO(
"Solving linear equation system (" << x.size() <<
" rows) with NativeLinearEquationSolver (Jacobi)");
122 if (!this->cachedRowVector) {
123 this->cachedRowVector = std::make_unique<std::vector<ValueType>>(getMatrixRowCount());
127 if (!jacobiDecomposition) {
128 jacobiDecomposition = std::make_unique<JacobiDecomposition>(env, *A);
131 ValueType precision = storm::utility::convertNumber<ValueType>(env.solver().native().getPrecision());
132 uint64_t maxIter = env.solver().native().getMaximalNumberOfIterations();
133 bool relative = env.solver().native().getRelativeTerminationCriterion();
135 std::vector<ValueType>* currentX = &x;
136 std::vector<ValueType>* nextX = this->cachedRowVector.get();
139 uint_fast64_t iterations = 0;
142 this->startMeasureProgress();
145 jacobiDecomposition->multiplier->multiply(env, *currentX,
nullptr, *nextX);
150 if (storm::utility::vector::equalModuloPrecision<ValueType>(*currentX, *nextX, precision, relative)) {
154 std::swap(nextX, currentX);
157 this->showProgressIterative(iterations);
167 if (currentX == this->cachedRowVector.get()) {
168 std::swap(x, *currentX);
171 if (!this->isCachingEnabled()) {
175 this->reportStatus(status, iterations);
180template<
typename ValueType>
182 std::vector<ValueType>
const& originalB)
184 computeWalkerChaeMatrix(originalMatrix);
185 computeNewB(originalB);
186 precomputeAuxiliaryData();
190template<
typename ValueType>
194 for (
auto const& e : originalMatrix) {
195 if (e.getValue() < zero) {
196 columnsWithNegativeEntries.set(e.getColumn());
199 std::vector<uint64_t> columnsWithNegativeEntriesBefore = columnsWithNegativeEntries.getNumberOfSetBitsBeforeIndices();
205 for (; row < originalMatrix.
getRowCount(); ++row) {
206 for (
auto const& entry : originalMatrix.getRow(row)) {
207 if (entry.getValue() < zero) {
208 builder.
addNextValue(row, originalMatrix.
getRowCount() + columnsWithNegativeEntriesBefore[entry.getColumn()], -entry.getValue());
210 builder.
addNextValue(row, entry.getColumn(), entry.getValue());
215 for (
auto column : columnsWithNegativeEntries) {
221 matrix = builder.
build();
224template<
typename ValueType>
225void NativeLinearEquationSolver<ValueType>::WalkerChaeData::computeNewB(std::vector<ValueType>
const& originalB) {
226 b = std::vector<ValueType>(originalB);
227 b.resize(matrix.getRowCount());
230template<
typename ValueType>
231void NativeLinearEquationSolver<ValueType>::WalkerChaeData::precomputeAuxiliaryData() {
232 columnSums = std::vector<ValueType>(matrix.getColumnCount());
233 for (
auto const& e : matrix) {
234 columnSums[e.getColumn()] += e.getValue();
237 newX.resize(matrix.getRowCount());
240template<
typename ValueType>
241bool NativeLinearEquationSolver<ValueType>::solveEquationsWalkerChae(Environment
const& env, std::vector<ValueType>& x, std::vector<ValueType>
const& b)
const {
242 STORM_LOG_INFO(
"Solving linear equation system (" << x.size() <<
" rows) with NativeLinearEquationSolver (WalkerChae)");
245 if (!walkerChaeData) {
246 walkerChaeData = std::make_unique<WalkerChaeData>(env, *this->A, b);
250 x.resize(walkerChaeData->matrix.getRowCount());
256 uint64_t maxIter = env.solver().native().getMaximalNumberOfIterations();
259 std::vector<ValueType>* currentX = &x;
260 std::vector<ValueType>* nextX = &walkerChaeData->newX;
262 std::vector<ValueType> tmp = walkerChaeData->matrix.getRowSumVector();
264 [
this](ValueType
const& first, ValueType
const& second) -> ValueType { return walkerChaeData->t * first + second; });
270 std::vector<ValueType> currentAx(x.size());
271 walkerChaeData->multiplier->multiply(env, *currentX,
nullptr, currentAx);
275 uint64_t iterations = 0;
276 this->startMeasureProgress();
279 walkerChaeData->matrix.performWalkerChaeStep(*currentX, walkerChaeData->columnSums, walkerChaeData->b, currentAx, *nextX);
282 walkerChaeData->multiplier->multiply(env, *nextX,
nullptr, currentAx);
290 std::swap(currentX, nextX);
293 this->showProgressIterative(iterations);
305 if (currentX == &walkerChaeData->newX) {
306 std::swap(x, *currentX);
315 if (!this->isCachingEnabled()) {
319 this->reportStatus(status, iterations);
324template<
typename ValueType>
325typename NativeLinearEquationSolver<ValueType>::PowerIterationResult NativeLinearEquationSolver<ValueType>::performPowerIteration(
326 Environment
const& env, std::vector<ValueType>*& currentX, std::vector<ValueType>*& newX, std::vector<ValueType>
const& b, ValueType
const& precision,
327 bool relative,
SolverGuarantee const& guarantee, uint64_t currentIterations, uint64_t maxIterations,
331 uint64_t iterations = currentIterations;
334 if (useGaussSeidelMultiplication) {
336 this->multiplier->multiplyGaussSeidel(env, *newX, &b);
338 this->multiplier->multiply(env, *currentX, &b, *newX);
342 if (storm::utility::vector::equalModuloPrecision<ValueType>(*currentX, *newX, precision, relative)) {
347 std::swap(currentX, newX);
350 status = this->updateStatus(status, *currentX, guarantee, iterations, maxIterations);
353 this->showProgressIterative(iterations);
356 return PowerIterationResult(iterations - currentIterations, status);
359template<
typename ValueType>
360bool NativeLinearEquationSolver<ValueType>::solveEquationsPower(Environment
const& env, std::vector<ValueType>& x, std::vector<ValueType>
const& b)
const {
361 STORM_LOG_INFO(
"Solving linear equation system (" << x.size() <<
" rows) with NativeLinearEquationSolver (Power)");
366 if (this->hasCustomTerminationCondition()) {
368 this->createLowerBoundsVector(x);
371 this->createUpperBoundsVector(x);
377 uint64_t numIterations{0};
379 this->showProgressIterative(numIterations);
380 return this->updateStatus(current, x, guarantee, numIterations, env.solver().native().getMaximalNumberOfIterations());
382 this->startMeasureProgress();
383 auto status = viHelper.VI(x, b, numIterations, env.solver().native().getRelativeTerminationCriterion(),
384 storm::utility::convertNumber<ValueType>(env.solver().native().getPrecision()), {}, viCallback,
385 env.solver().native().getPowerMethodMultiplicationStyle());
387 this->reportStatus(status, numIterations);
389 if (!this->isCachingEnabled()) {
396template<
typename ValueType>
401template<
typename ValueType>
403 ValueType result = storm::utility::zero<ValueType>();
404 auto oldValueIt = oldValues.begin();
405 for (
auto value : relevantValues) {
406 result = storm::utility::max<ValueType>(result, storm::utility::abs<ValueType>(allValues[value] - *oldValueIt));
411template<
typename ValueType>
412ValueType
computeMaxAbsDiff(std::vector<ValueType>
const& allOldValues, std::vector<ValueType>
const& allNewValues,
414 ValueType result = storm::utility::zero<ValueType>();
415 for (
auto value : relevantValues) {
416 result = storm::utility::max<ValueType>(result, storm::utility::abs<ValueType>(allNewValues[value] - allOldValues[value]));
421template<
typename ValueType>
422bool NativeLinearEquationSolver<ValueType>::solveEquationsIntervalIteration(
Environment const& env, std::vector<ValueType>& x,
423 std::vector<ValueType>
const& b)
const {
424 STORM_LOG_THROW(this->hasLowerBound(), storm::exceptions::UnmetRequirementException,
"Solver requires lower bound, but none was given.");
425 STORM_LOG_THROW(this->hasUpperBound(), storm::exceptions::UnmetRequirementException,
"Solver requires upper bound, but none was given.");
426 STORM_LOG_INFO(
"Solving linear equation system (" << x.size() <<
" rows) with NativeLinearEquationSolver (IntervalIteration)");
428 helper::IntervalIterationHelper<ValueType, true> iiHelper(viOperator);
430 auto lowerBoundsCallback = [&](std::vector<ValueType>& vector) { this->createLowerBoundsVector(vector); };
431 auto upperBoundsCallback = [&](std::vector<ValueType>& vector) { this->createUpperBoundsVector(vector); };
433 uint64_t numIterations{0};
434 auto iiCallback = [&](helper::IIData<ValueType>
const& data) {
435 this->showProgressIterative(numIterations);
436 bool terminateEarly = this->hasCustomTerminationCondition() && this->getTerminationCondition().terminateNow(data.x,
SolverGuarantee::LessOrEqual) &&
440 std::optional<storm::storage::BitVector> optionalRelevantValues;
441 if (this->hasRelevantValues()) {
442 optionalRelevantValues = this->getRelevantValues();
444 this->startMeasureProgress();
446 iiCallback, optionalRelevantValues);
447 this->reportStatus(status, numIterations);
449 if (!this->isCachingEnabled()) {
456template<
typename ValueType>
457bool NativeLinearEquationSolver<ValueType>::solveEquationsSoundValueIteration(Environment
const& env, std::vector<ValueType>& x,
458 std::vector<ValueType>
const& b)
const {
460 assert(x.size() == this->A->getRowGroupCount());
462 std::optional<ValueType> lowerBound, upperBound;
463 if (this->hasLowerBound()) {
464 lowerBound = this->getLowerBound(
true);
466 if (this->hasUpperBound()) {
467 upperBound = this->getUpperBound(
true);
472 auto precision = storm::utility::convertNumber<ValueType>(env.solver().native().getPrecision());
473 uint64_t numIterations{0};
474 auto sviCallback = [&](
typename helper::SoundValueIterationHelper<ValueType, true>::SVIData
const& current) {
475 this->showProgressIterative(numIterations);
476 return this->updateStatus(current.status,
477 this->hasCustomTerminationCondition() && current.checkCustomTerminationCondition(this->getTerminationCondition()),
478 numIterations, env.solver().native().getMaximalNumberOfIterations());
480 std::optional<storm::storage::BitVector> optionalRelevantValues;
481 if (this->hasRelevantValues()) {
482 optionalRelevantValues = this->getRelevantValues();
484 this->startMeasureProgress();
485 helper::SoundValueIterationHelper<ValueType, true> sviHelper(viOperator);
486 auto status = sviHelper.SVI(x, b, numIterations, env.solver().native().getRelativeTerminationCriterion(), precision, {}, lowerBound, upperBound,
487 sviCallback, optionalRelevantValues);
489 this->reportStatus(status, numIterations);
491 if (!this->isCachingEnabled()) {
498template<
typename ValueType>
499bool NativeLinearEquationSolver<ValueType>::solveEquationsOptimisticValueIteration(Environment
const& env, std::vector<ValueType>& x,
500 std::vector<ValueType>
const& b)
const {
503 x.assign(x.size(), storm::utility::zero<ValueType>());
509 helper::OptimisticValueIterationHelper<ValueType, true> oviHelper(viOperator);
510 auto prec = storm::utility::convertNumber<ValueType>(env.solver().native().getPrecision());
511 std::optional<ValueType> lowerBound, upperBound;
512 if (this->hasLowerBound()) {
513 lowerBound = this->getLowerBound(
true);
515 if (this->hasUpperBound()) {
516 upperBound = this->getUpperBound(
true);
518 uint64_t numIterations{0};
519 auto oviCallback = [&](
SolverStatus const& current, std::vector<ValueType>
const& v) {
520 this->showProgressIterative(numIterations);
521 return this->updateStatus(current, v,
SolverGuarantee::LessOrEqual, numIterations, env.solver().native().getMaximalNumberOfIterations());
523 this->createLowerBoundsVector(x);
524 std::optional<ValueType> guessingFactor;
525 if (env.solver().ovi().getUpperBoundGuessingFactor()) {
526 guessingFactor = storm::utility::convertNumber<ValueType>(*env.solver().ovi().getUpperBoundGuessingFactor());
528 this->startMeasureProgress();
529 auto status = oviHelper.OVI(x, b, env.solver().native().getRelativeTerminationCriterion(), prec, {}, guessingFactor, lowerBound, upperBound, oviCallback);
530 this->reportStatus(status, numIterations);
532 if (!this->isCachingEnabled()) {
539template<
typename ValueType>
540bool NativeLinearEquationSolver<ValueType>::solveEquationsRationalSearch(Environment
const& env, std::vector<ValueType>& x,
541 std::vector<ValueType>
const& b)
const {
544 std::shared_ptr<helper::ValueIterationOperator<storm::RationalNumber, true>> exactOp;
545 std::shared_ptr<helper::ValueIterationOperator<double, true>> impreciseOp;
547 if constexpr (std::is_same_v<ValueType, storm::RationalNumber>) {
548 exactOp = viOperator;
549 impreciseOp = std::make_shared<helper::ValueIterationOperator<double, true>>();
550 impreciseOp->setMatrixBackwards(this->A->template toValueType<double>());
552 impreciseOp = viOperator;
553 exactOp = std::make_shared<helper::ValueIterationOperator<storm::RationalNumber, true>>();
554 exactOp->setMatrixBackwards(this->A->template toValueType<storm::RationalNumber>());
558 uint64_t numIterations{0};
560 this->showProgressIterative(numIterations);
561 return this->updateStatus(current, x,
SolverGuarantee::None, numIterations, env.solver().native().getMaximalNumberOfIterations());
563 this->startMeasureProgress();
564 auto status = rsHelper.RS(x, b, numIterations, storm::utility::convertNumber<ValueType>(env.solver().native().getPrecision()), {}, rsCallback);
566 this->reportStatus(status, numIterations);
568 if (!this->isCachingEnabled()) {
575template<
typename ValueType>
576NativeLinearEquationSolverMethod NativeLinearEquationSolver<ValueType>::getMethod(Environment
const& env,
bool isExactMode)
const {
578 auto method = env.solver().native().getMethod();
580 if (isExactMode && method != NativeLinearEquationSolverMethod::RationalSearch) {
581 if (env.solver().native().isMethodSetFromDefault()) {
582 method = NativeLinearEquationSolverMethod::RationalSearch;
585 "' as the solution technique to guarantee exact results. If you want to override this, please explicitly specify a different method.");
587 STORM_LOG_WARN(
"The selected solution method does not guarantee exact results.");
589 }
else if (env.solver().isForceSoundness() && method != NativeLinearEquationSolverMethod::SoundValueIteration &&
590 method != NativeLinearEquationSolverMethod::OptimisticValueIteration && method != NativeLinearEquationSolverMethod::IntervalIteration &&
591 method != NativeLinearEquationSolverMethod::RationalSearch) {
592 if (env.solver().native().isMethodSetFromDefault()) {
593 method = NativeLinearEquationSolverMethod::OptimisticValueIteration;
596 "' as the solution technique to guarantee sound results. If you want to override this, please explicitly specify a different method.");
598 STORM_LOG_WARN(
"The selected solution method does not guarantee sound results.");
604template<
typename ValueType>
607 case NativeLinearEquationSolverMethod::SOR:
608 return this->solveEquationsSOR(env, x, b, storm::utility::convertNumber<ValueType>(env.
solver().
native().
getSorOmega()));
609 case NativeLinearEquationSolverMethod::GaussSeidel:
610 return this->solveEquationsSOR(env, x, b, storm::utility::one<ValueType>());
611 case NativeLinearEquationSolverMethod::Jacobi:
612 return this->solveEquationsJacobi(env, x, b);
613 case NativeLinearEquationSolverMethod::WalkerChae:
614 return this->solveEquationsWalkerChae(env, x, b);
615 case NativeLinearEquationSolverMethod::Power:
616 return this->solveEquationsPower(env, x, b);
617 case NativeLinearEquationSolverMethod::SoundValueIteration:
618 return this->solveEquationsSoundValueIteration(env, x, b);
619 case NativeLinearEquationSolverMethod::OptimisticValueIteration:
620 return this->solveEquationsOptimisticValueIteration(env, x, b);
621 case NativeLinearEquationSolverMethod::IntervalIteration:
622 return this->solveEquationsIntervalIteration(env, x, b);
623 case NativeLinearEquationSolverMethod::RationalSearch:
624 return this->solveEquationsRationalSearch(env, x, b);
626 STORM_LOG_THROW(
false, storm::exceptions::InvalidEnvironmentException,
"Unknown solving technique.");
630template<
typename ValueType>
633 if (method == NativeLinearEquationSolverMethod::Power || method == NativeLinearEquationSolverMethod::SoundValueIteration ||
634 method == NativeLinearEquationSolverMethod::OptimisticValueIteration || method == NativeLinearEquationSolverMethod::RationalSearch ||
635 method == NativeLinearEquationSolverMethod::IntervalIteration) {
642template<
typename ValueType>
646 if (method == NativeLinearEquationSolverMethod::IntervalIteration) {
648 }
else if (method == NativeLinearEquationSolverMethod::RationalSearch || method == NativeLinearEquationSolverMethod::OptimisticValueIteration) {
650 }
else if (method == NativeLinearEquationSolverMethod::SoundValueIteration) {
656template<
typename ValueType>
658 jacobiDecomposition.reset();
659 walkerChaeData.reset();
665template<
typename ValueType>
670template<
typename ValueType>
671uint64_t NativeLinearEquationSolver<ValueType>::getMatrixColumnCount()
const {
675template<
typename ValueType>
677 return std::make_unique<storm::solver::NativeLinearEquationSolver<ValueType>>();
680template<
typename ValueType>
682 return std::make_unique<NativeLinearEquationSolverFactory<ValueType>>(*this);
689#ifdef STORM_HAVE_CARL
SolverEnvironment & solver()
storm::RationalNumber const & getPrecision() const
uint64_t const & getMaximalNumberOfIterations() const
bool const & getRelativeTerminationCriterion() const
storm::RationalNumber const & getSorOmega() const
bool isForceExact() const
NativeSolverEnvironment & native()
virtual void clearCache() const
LinearEquationSolverRequirements & requireLowerBounds(bool critical=true)
LinearEquationSolverRequirements & requireBounds(bool critical=true)
std::unique_ptr< Multiplier< ValueType > > create(Environment const &env, storm::storage::SparseMatrix< ValueType > const &matrix)
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.
friend class NativeLinearEquationSolver
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 void clearCache() const override
virtual LinearEquationSolverRequirements getRequirements(Environment const &env) const override
Retrieves the requirements of the solver under the current settings.
Implements rational search.
A bit vector that is internally represented as a vector of 64-bit values.
A class that can be used to build a sparse matrix by adding value by value.
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)
#define STORM_LOG_WARN(message)
#define STORM_LOG_THROW(cond, exception, message)
SFTBDDChecker::ValueType ValueType
LinearEquationSolverProblemFormat
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).
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...
T computeSquaredNorm2Difference(std::vector< T > const &b1, std::vector< T > const &b2)
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.
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...
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.
bool hasNonZeroEntry(std::vector< T > const &v)
ValueType pow(ValueType const &value, int_fast64_t exponent)
TargetType convertNumber(SourceType const &number)