24template<
typename ValueType>
29template<
typename ValueType>
34template<
typename ValueType>
39template<
typename ValueType>
46template<
typename ValueType>
48 localA = std::make_unique<storm::storage::SparseMatrix<ValueType>>(std::move(A));
49 this->A = localA.get();
53template<
typename ValueType>
54void NativeLinearEquationSolver<ValueType>::setUpViOperator()
const {
56 viOperator = std::make_shared<helper::ValueIterationOperator<ValueType, true>>();
57 viOperator->setMatrixBackwards(*this->A);
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 <<
")");
66 if (!this->cachedRowVector) {
67 this->cachedRowVector = std::make_unique<std::vector<ValueType>>(getMatrixRowCount());
71 uint64_t maxIter = env.solver().native().getMaximalNumberOfIterations();
72 bool relative = env.solver().native().getRelativeTerminationCriterion();
75 uint_fast64_t iterations = 0;
78 this->startMeasureProgress();
80 A->performSuccessiveOverRelaxationStep(omega, x, b);
88 *this->cachedRowVector = x;
92 this->showProgressIterative(iterations);
100 if (!this->isCachingEnabled()) {
104 this->reportStatus(status, iterations);
109template<
typename ValueType>
110NativeLinearEquationSolver<ValueType>::JacobiDecomposition::JacobiDecomposition(Environment
const& env, storm::storage::SparseMatrix<ValueType>
const& A) {
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);
117template<
typename ValueType>
119 STORM_LOG_INFO(
"Solving linear equation system (" << x.size() <<
" rows) with NativeLinearEquationSolver (Jacobi)");
122 this->
cachedRowVector = std::make_unique<std::vector<ValueType>>(getMatrixRowCount());
126 if (!jacobiDecomposition) {
127 jacobiDecomposition = std::make_unique<JacobiDecomposition>(env, *A);
131 uint64_t maxIter = env.solver().native().getMaximalNumberOfIterations();
132 bool relative = env.solver().native().getRelativeTerminationCriterion();
134 std::vector<ValueType>* currentX = &x;
138 uint_fast64_t iterations = 0;
144 jacobiDecomposition->multiplier->multiply(env, *currentX,
nullptr, *nextX);
153 std::swap(nextX, currentX);
167 std::swap(x, *currentX);
179template<
typename ValueType>
181 std::vector<ValueType>
const& originalB)
183 computeWalkerChaeMatrix(originalMatrix);
184 computeNewB(originalB);
185 precomputeAuxiliaryData();
186 multiplier = storm::solver::MultiplierFactory<ValueType>().create(env, this->matrix);
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());
198 std::vector<uint64_t> columnsWithNegativeEntriesBefore = columnsWithNegativeEntries.getNumberOfSetBitsBeforeIndices();
201 storm::storage::SparseMatrixBuilder<ValueType> builder;
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());
209 builder.
addNextValue(row, entry.getColumn(), entry.getValue());
214 for (
auto column : columnsWithNegativeEntries) {
216 builder.
addNextValue(row, originalMatrix.getRowCount() + columnsWithNegativeEntriesBefore[column], one);
220 matrix = builder.
build();
223template<
typename ValueType>
225 b = std::vector<ValueType>(originalB);
226 b.resize(matrix.getRowCount());
229template<
typename ValueType>
231 columnSums = std::vector<ValueType>(matrix.getColumnCount());
232 for (
auto const& e : matrix) {
233 columnSums[e.getColumn()] += e.getValue();
236 newX.resize(matrix.getRowCount());
239template<
typename ValueType>
241 STORM_LOG_INFO(
"Solving linear equation system (" << x.size() <<
" rows) with NativeLinearEquationSolver (WalkerChae)");
244 if (!walkerChaeData) {
245 walkerChaeData = std::make_unique<WalkerChaeData>(env, *this->A, b);
249 x.resize(walkerChaeData->matrix.getRowCount());
255 uint64_t maxIter = env.solver().native().getMaximalNumberOfIterations();
258 std::vector<ValueType>* currentX = &x;
259 std::vector<ValueType>* nextX = &walkerChaeData->newX;
261 std::vector<ValueType> tmp = walkerChaeData->matrix.getRowSumVector();
263 [
this](ValueType
const& first, ValueType
const& second) -> ValueType { return walkerChaeData->t * first + second; });
269 std::vector<ValueType> currentAx(x.size());
270 walkerChaeData->multiplier->multiply(env, *currentX,
nullptr, currentAx);
274 uint64_t iterations = 0;
278 walkerChaeData->matrix.performWalkerChaeStep(*currentX, walkerChaeData->columnSums, walkerChaeData->b, currentAx, *nextX);
281 walkerChaeData->multiplier->multiply(env, *nextX,
nullptr, currentAx);
289 std::swap(currentX, nextX);
304 if (currentX == &walkerChaeData->newX) {
305 std::swap(x, *currentX);
309 x.resize(this->A->getRowCount());
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,
330 uint64_t iterations = currentIterations;
333 if (useGaussSeidelMultiplication) {
335 this->multiplier->multiplyGaussSeidel(env, *newX, &b);
337 this->multiplier->multiply(env, *currentX, &b, *newX);
346 std::swap(currentX, newX);
349 status = this->
updateStatus(status, *currentX, guarantee, iterations, maxIterations);
355 return PowerIterationResult(iterations - currentIterations, status);
358template<
typename ValueType>
360 STORM_LOG_INFO(
"Solving linear equation system (" << x.size() <<
" rows) with NativeLinearEquationSolver (Power)");
375 storm::solver::helper::ValueIterationHelper<ValueType, true> viHelper(viOperator);
376 uint64_t numIterations{0};
379 return this->
updateStatus(current, x, guarantee, numIterations, env.solver().native().getMaximalNumberOfIterations());
382 auto status = viHelper.VI(x, b, numIterations, env.solver().native().getRelativeTerminationCriterion(),
384 env.solver().native().getPowerMethodMultiplicationStyle());
395template<
typename ValueType>
400template<
typename ValueType>
403 auto oldValueIt = oldValues.begin();
410template<
typename ValueType>
411ValueType
computeMaxAbsDiff(std::vector<ValueType>
const& allOldValues, std::vector<ValueType>
const& allNewValues,
420template<
typename ValueType>
421bool NativeLinearEquationSolver<ValueType>::solveEquationsIntervalIteration(
Environment const& env, std::vector<ValueType>& x,
422 std::vector<ValueType>
const& b)
const {
423 STORM_LOG_THROW(this->
hasLowerBound(), storm::exceptions::UnmetRequirementException,
"Solver requires lower bound, but none was given.");
424 STORM_LOG_THROW(this->
hasUpperBound(), storm::exceptions::UnmetRequirementException,
"Solver requires upper bound, but none was given.");
425 STORM_LOG_INFO(
"Solving linear equation system (" << x.size() <<
" rows) with NativeLinearEquationSolver (IntervalIteration)");
427 helper::IntervalIterationHelper<ValueType, true> iiHelper(viOperator);
432 uint64_t numIterations{0};
433 auto iiCallback = [&](helper::IIData<ValueType>
const& data) {
439 std::optional<storm::storage::BitVector> optionalRelevantValues;
445 iiCallback, optionalRelevantValues);
455template<
typename ValueType>
457 std::vector<ValueType>
const& b)
const {
459 assert(x.size() == this->A->getRowGroupCount());
472 uint64_t numIterations{0};
473 auto sviCallback = [&](
typename helper::SoundValueIterationHelper<ValueType, true>::SVIData
const& current) {
476 this->hasCustomTerminationCondition() && current.checkCustomTerminationCondition(this->getTerminationCondition()),
477 numIterations, env.solver().native().getMaximalNumberOfIterations());
479 std::optional<storm::storage::BitVector> optionalRelevantValues;
484 helper::SoundValueIterationHelper<ValueType, true> sviHelper(viOperator);
485 auto status = sviHelper.SVI(x, b, numIterations, env.solver().native().getRelativeTerminationCriterion(), precision, {},
lowerBound,
upperBound,
486 sviCallback, optionalRelevantValues);
497template<
typename ValueType>
499 std::vector<ValueType>
const& b)
const {
508 helper::OptimisticValueIterationHelper<ValueType, true> oviHelper(viOperator);
517 uint64_t numIterations{0};
518 auto oviCallback = [&](
SolverStatus const& current, std::vector<ValueType>
const& v) {
523 std::optional<ValueType> guessingFactor;
524 if (env.solver().ovi().getUpperBoundGuessingFactor()) {
528 auto status = oviHelper.OVI(x, b, env.solver().native().getRelativeTerminationCriterion(), prec, {}, guessingFactor,
lowerBound,
upperBound, oviCallback);
538template<
typename ValueType>
540 const std::vector<ValueType>& b)
const {
542 this->
cachedRowVector = std::make_unique<std::vector<ValueType>>(this->A->getRowCount());
546 std::vector<ValueType>* lowerX = &x;
551 storm::solver::helper::GuessingValueIterationHelper<ValueType, true> helper(viOperator, *this->A);
553 uint64_t numIterations{0};
554 auto gviCallback = [&](helper::GVIData<ValueType>
const& data) {
558 return this->
updateStatus(data.status, terminateEarly, numIterations, env.solver().native().getMaximalNumberOfIterations());
567 *lowerX, *upperX, x, [&two](ValueType
const& first, ValueType
const& second) -> ValueType {
return (first + second) / two; });
576template<
typename ValueType>
578 std::vector<ValueType>
const& b)
const {
581 std::shared_ptr<helper::ValueIterationOperator<storm::RationalNumber, true>> exactOp;
582 std::shared_ptr<helper::ValueIterationOperator<double, true>> impreciseOp;
584 if constexpr (std::is_same_v<ValueType, storm::RationalNumber>) {
585 exactOp = viOperator;
586 impreciseOp = std::make_shared<helper::ValueIterationOperator<double, true>>();
587 impreciseOp->setMatrixBackwards(this->A->template toValueType<double>());
589 impreciseOp = viOperator;
590 exactOp = std::make_shared<helper::ValueIterationOperator<storm::RationalNumber, true>>();
591 exactOp->setMatrixBackwards(this->A->template toValueType<storm::RationalNumber>());
594 storm::solver::helper::RationalSearchHelper<ValueType, storm::RationalNumber, double, true> rsHelper(exactOp, impreciseOp);
595 uint64_t numIterations{0};
612template<
typename ValueType>
615 auto method = env.solver().native().getMethod();
617 if (isExactMode && method != NativeLinearEquationSolverMethod::RationalSearch) {
618 if (env.solver().native().isMethodSetFromDefault()) {
619 method = NativeLinearEquationSolverMethod::RationalSearch;
622 "' as the solution technique to guarantee exact results. If you want to override this, please explicitly specify a different method.");
624 STORM_LOG_WARN(
"The selected solution method does not guarantee exact results.");
626 }
else if (env.solver().isForceSoundness() && method != NativeLinearEquationSolverMethod::SoundValueIteration &&
627 method != NativeLinearEquationSolverMethod::OptimisticValueIteration && method != NativeLinearEquationSolverMethod::IntervalIteration &&
628 method != NativeLinearEquationSolverMethod::RationalSearch) {
629 if (env.solver().native().isMethodSetFromDefault()) {
630 method = NativeLinearEquationSolverMethod::OptimisticValueIteration;
633 "' as the solution technique to guarantee sound results. If you want to override this, please explicitly specify a different method.");
635 STORM_LOG_WARN(
"The selected solution method does not guarantee sound results.");
641template<
typename ValueType>
644 case NativeLinearEquationSolverMethod::SOR:
646 case NativeLinearEquationSolverMethod::GaussSeidel:
648 case NativeLinearEquationSolverMethod::Jacobi:
649 return this->solveEquationsJacobi(env, x, b);
650 case NativeLinearEquationSolverMethod::WalkerChae:
651 return this->solveEquationsWalkerChae(env, x, b);
652 case NativeLinearEquationSolverMethod::Power:
653 return this->solveEquationsPower(env, x, b);
654 case NativeLinearEquationSolverMethod::SoundValueIteration:
655 return this->solveEquationsSoundValueIteration(env, x, b);
656 case NativeLinearEquationSolverMethod::OptimisticValueIteration:
657 return this->solveEquationsOptimisticValueIteration(env, x, b);
658 case NativeLinearEquationSolverMethod::GuessingValueIteration:
659 return this->solveEquationsGuessingValueIteration(env, x, b);
660 case NativeLinearEquationSolverMethod::IntervalIteration:
661 return this->solveEquationsIntervalIteration(env, x, b);
662 case NativeLinearEquationSolverMethod::RationalSearch:
663 return this->solveEquationsRationalSearch(env, x, b);
665 STORM_LOG_THROW(
false, storm::exceptions::InvalidEnvironmentException,
"Unknown solving technique.");
669template<
typename ValueType>
672 if (method == NativeLinearEquationSolverMethod::Power || method == NativeLinearEquationSolverMethod::SoundValueIteration ||
673 method == NativeLinearEquationSolverMethod::OptimisticValueIteration || method == NativeLinearEquationSolverMethod::RationalSearch ||
674 method == NativeLinearEquationSolverMethod::IntervalIteration || method == NativeLinearEquationSolverMethod::GuessingValueIteration) {
681template<
typename ValueType>
685 if (method == NativeLinearEquationSolverMethod::IntervalIteration || method == NativeLinearEquationSolverMethod::GuessingValueIteration) {
687 }
else if (method == NativeLinearEquationSolverMethod::RationalSearch || method == NativeLinearEquationSolverMethod::OptimisticValueIteration) {
689 }
else if (method == NativeLinearEquationSolverMethod::SoundValueIteration) {
695template<
typename ValueType>
697 jacobiDecomposition.reset();
698 walkerChaeData.reset();
704template<
typename ValueType>
705uint64_t NativeLinearEquationSolver<ValueType>::getMatrixRowCount()
const {
709template<
typename ValueType>
710uint64_t NativeLinearEquationSolver<ValueType>::getMatrixColumnCount()
const {
714template<
typename ValueType>
716 return std::make_unique<storm::solver::NativeLinearEquationSolver<ValueType>>();
719template<
typename ValueType>
721 return std::make_unique<NativeLinearEquationSolverFactory<ValueType>>(*this);
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()
void createLowerBoundsVector(std::vector< ValueType > &lowerBoundsVector) const
bool hasUpperBound(BoundType const &type=BoundType::Any) const
Retrieves whether this solver has an upper bound.
bool hasLowerBound(BoundType const &type=BoundType::Any) const
Retrieves whether this solver has a lower bound.
void showProgressIterative(uint64_t iterations, boost::optional< uint64_t > const &bound=boost::none) const
Shows progress if this solver is asked to do so.
storm::storage::BitVector const & getRelevantValues() const
Retrieves the relevant values (if there are any).
ValueType const & getUpperBound() const
Retrieves the upper bound (if there is any).
boost::optional< ValueType > lowerBound
boost::optional< ValueType > upperBound
bool terminateNow(std::vector< ValueType > const &values, SolverGuarantee const &guarantee) const
Checks whether the solver can terminate wrt.
void startMeasureProgress(uint64_t startingIteration=0) const
Starts to measure progress.
void createUpperBoundsVector(std::vector< ValueType > &upperBoundsVector) const
TerminationCondition< ValueType > const & getTerminationCondition() const
Retrieves the custom termination condition (if any was set).
bool hasRelevantValues() const
Retrieves whether this solver has particularly relevant values.
bool hasCustomTerminationCondition() const
Retrieves whether a custom termination condition has been set.
ValueType const & getLowerBound() const
Retrieves the lower bound (if there is any).
SolverStatus updateStatus(SolverStatus status, std::vector< ValueType > const &x, SolverGuarantee const &guarantee, uint64_t iterations, uint64_t maximalNumberOfIterations) const
Update the status of the solver with respect to convergence, early termination, abortion,...
boost::optional< storm::storage::BitVector > relevantValues
void reportStatus(SolverStatus status, boost::optional< uint64_t > const &iterations=boost::none) const
Report the current status of the solver.
virtual void clearCache() 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.
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.
A bit vector that is internally represented as a vector of 64-bit values.
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).
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.
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 max(ValueType const &first, ValueType const &second)
ValueType abs(ValueType const &number)
ValueType pow(ValueType const &value, int_fast64_t exponent)
TargetType convertNumber(SourceType const &number)
static const bool IsExact