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 (uint64_t 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());
384 storm::solver::SolutionBounds<ValueType> solutionBounds;
387 solutionBoundsRef.
reset(solutionBounds);
389 auto status = viHelper.VI(x, b, numIterations, env.solver().native().getRelativeTerminationCriterion(),
391 env.solver().native().getPowerMethodMultiplicationStyle(), UncertaintyResolutionMode::Unset, solutionBoundsRef, guarantee);
392 if (solutionBounds.hasAny()) {
405template<
typename ValueType>
410template<
typename ValueType>
413 auto oldValueIt = oldValues.begin();
420template<
typename ValueType>
421ValueType
computeMaxAbsDiff(std::vector<ValueType>
const& allOldValues, std::vector<ValueType>
const& allNewValues,
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)");
437 helper::IntervalIterationHelper<ValueType, true> iiHelper(viOperator);
442 uint64_t numIterations{0};
443 auto iiCallback = [&](helper::IIData<ValueType>
const& data) {
449 std::optional<storm::storage::BitVector> optionalRelevantValues;
454 storm::solver::SolutionBounds<ValueType> solutionBounds;
456 iiCallback, optionalRelevantValues, solutionBounds);
457 if (solutionBounds.hasAny()) {
469template<
typename ValueType>
471 std::vector<ValueType>
const& b)
const {
473 STORM_LOG_ASSERT(x.size() == this->A->getRowGroupCount(),
"Solution vector size mismatch.");
486 uint64_t numIterations{0};
487 auto sviCallback = [&](
typename helper::SoundValueIterationHelper<ValueType, true>::SVIData
const& current) {
490 this->hasCustomTerminationCondition() && current.checkCustomTerminationCondition(this->getTerminationCondition()),
491 numIterations, env.solver().native().getMaximalNumberOfIterations());
493 std::optional<storm::storage::BitVector> optionalRelevantValues;
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()) {
515template<
typename ValueType>
517 std::vector<ValueType>
const& b)
const {
526 helper::OptimisticValueIterationHelper<ValueType, true> oviHelper(viOperator);
535 uint64_t numIterations{0};
536 auto oviCallback = [&](
SolverStatus const& current, std::vector<ValueType>
const& v) {
541 std::optional<ValueType> guessingFactor;
542 if (env.solver().ovi().getUpperBoundGuessingFactor()) {
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()) {
561template<
typename ValueType>
563 const std::vector<ValueType>& b)
const {
565 this->
cachedRowVector = std::make_unique<std::vector<ValueType>>(this->A->getRowCount());
569 std::vector<ValueType>* lowerX = &x;
574 storm::solver::helper::GuessingValueIterationHelper<ValueType, true> helper(viOperator, *this->A);
576 uint64_t numIterations{0};
577 auto gviCallback = [&](helper::GVIData<ValueType>
const& data) {
581 return this->
updateStatus(data.status, terminateEarly, numIterations, env.solver().native().getMaximalNumberOfIterations());
585 auto status = helper.solveEquations(*lowerX, *upperX, b, numIterations, env.solver().native().getRelativeTerminationCriterion(),
589 storm::solver::SolutionBounds<ValueType> solutionBounds;
590 solutionBounds.lower = *lowerX;
591 solutionBounds.upper = *upperX;
596 *lowerX, *upperX, x, [&two](ValueType
const& first, ValueType
const& second) -> ValueType {
return (first + second) / two; });
605template<
typename ValueType>
607 std::vector<ValueType>
const& b)
const {
610 std::shared_ptr<helper::ValueIterationOperator<storm::RationalNumber, true>> exactOp;
611 std::shared_ptr<helper::ValueIterationOperator<double, true>> impreciseOp;
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>());
618 impreciseOp = viOperator;
619 exactOp = std::make_shared<helper::ValueIterationOperator<storm::RationalNumber, true>>();
620 exactOp->setMatrixBackwards(this->A->template toValueType<storm::RationalNumber>());
623 storm::solver::helper::RationalSearchHelper<ValueType, storm::RationalNumber, double, true> rsHelper(exactOp, impreciseOp);
624 uint64_t numIterations{0};
646template<
typename ValueType>
649 auto method = env.solver().native().getMethod();
651 if (isExactMode && method != NativeLinearEquationSolverMethod::RationalSearch) {
652 if (env.solver().native().isMethodSetFromDefault()) {
653 method = NativeLinearEquationSolverMethod::RationalSearch;
656 "' as the solution technique to guarantee exact results. If you want to override this, please explicitly specify a different method.");
658 STORM_LOG_WARN(
"The selected solution method does not guarantee exact results.");
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;
667 "' as the solution technique to guarantee sound results. If you want to override this, please explicitly specify a different method.");
669 STORM_LOG_WARN(
"The selected solution method does not guarantee sound results.");
675template<
typename ValueType>
678 case NativeLinearEquationSolverMethod::SOR:
680 case NativeLinearEquationSolverMethod::GaussSeidel:
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);
699 STORM_LOG_THROW(
false, storm::exceptions::InvalidEnvironmentException,
"Unknown solving technique.");
703template<
typename ValueType>
706 if (method == NativeLinearEquationSolverMethod::Power || method == NativeLinearEquationSolverMethod::SoundValueIteration ||
707 method == NativeLinearEquationSolverMethod::OptimisticValueIteration || method == NativeLinearEquationSolverMethod::RationalSearch ||
708 method == NativeLinearEquationSolverMethod::IntervalIteration || method == NativeLinearEquationSolverMethod::GuessingValueIteration) {
715template<
typename ValueType>
719 if (method == NativeLinearEquationSolverMethod::IntervalIteration || method == NativeLinearEquationSolverMethod::GuessingValueIteration) {
721 }
else if (method == NativeLinearEquationSolverMethod::RationalSearch || method == NativeLinearEquationSolverMethod::OptimisticValueIteration) {
723 }
else if (method == NativeLinearEquationSolverMethod::SoundValueIteration) {
729template<
typename ValueType>
731 jacobiDecomposition.reset();
732 walkerChaeData.reset();
738template<
typename ValueType>
739uint64_t NativeLinearEquationSolver<ValueType>::getMatrixRowCount()
const {
743template<
typename ValueType>
744uint64_t NativeLinearEquationSolver<ValueType>::getMatrixColumnCount()
const {
748template<
typename ValueType>
750 auto solver = std::make_unique<storm::solver::NativeLinearEquationSolver<ValueType>>();
755template<
typename ValueType>
757 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
void reset()
Unsets the reference.
uint64_t getShowProgressDelay() const
bool isForceExact() const
NativeSolverEnvironment & native()
bool isVerboseSet() const
bool hasLowerBound(BoundType const &type=BoundType::Any) const
void setSolutionBoundsExact(std::vector< ValueType > const &x) const
void setSolutionBounds(SolutionBounds< ValueType > bounds) const
bool hasCustomTerminationCondition() const
void showProgressIterative(uint64_t iterations, boost::optional< uint64_t > const &bound=boost::none) const
boost::optional< ValueType > lowerBound
storm::storage::BitVector const & getRelevantValues() const
bool hasUpperBound(BoundType const &type=BoundType::Any) const
ValueType const & getUpperBound() const
SolverStatus updateStatus(SolverStatus status, std::vector< ValueType > const &x, SolverGuarantee const &guarantee, uint64_t iterations, uint64_t maximalNumberOfIterations) const
boost::optional< ValueType > upperBound
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
ValueType const & getLowerBound() const
bool hasRelevantValues() 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
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_ASSERT(cond, 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)
OptionalRef(T &) -> OptionalRef< T >
deduction guides
static const bool IsExact