From 1e5398c8b7bb85a85d88a27ce204d7fc2bc01d02 Mon Sep 17 00:00:00 2001 From: dehnert <dehnert@cs.rwth-aachen.de> Date: Sat, 20 Jun 2015 20:07:45 +0200 Subject: [PATCH] LRA finally working for ctmcs Former-commit-id: 699e4714a4cc493251ad38085fc79a93bedba75f --- .../csl/SparseCtmcCslModelChecker.cpp | 194 ++++++++++++- .../csl/SparseCtmcCslModelChecker.h | 6 +- .../prctl/SparseDtmcPrctlModelChecker.cpp | 271 +----------------- .../prctl/SparseDtmcPrctlModelChecker.h | 2 - .../modules/GmmxxEquationSolverSettings.cpp | 36 +-- .../modules/GmmxxEquationSolverSettings.h | 34 +-- .../modules/NativeEquationSolverSettings.cpp | 19 +- .../modules/NativeEquationSolverSettings.h | 18 +- src/solver/GmmxxLinearEquationSolver.cpp | 24 +- src/solver/NativeLinearEquationSolver.cpp | 131 +++++---- src/solver/NativeLinearEquationSolver.h | 5 +- src/storage/SparseMatrix.cpp | 261 ++++++++++------- src/storage/SparseMatrix.h | 9 + src/utility/solver.cpp | 21 +- src/utility/solver.h | 9 + .../NativeDtmcPrctlModelCheckerTest.cpp | 9 +- 16 files changed, 548 insertions(+), 501 deletions(-) diff --git a/src/modelchecker/csl/SparseCtmcCslModelChecker.cpp b/src/modelchecker/csl/SparseCtmcCslModelChecker.cpp index 25328e0a5..dce356de7 100644 --- a/src/modelchecker/csl/SparseCtmcCslModelChecker.cpp +++ b/src/modelchecker/csl/SparseCtmcCslModelChecker.cpp @@ -12,6 +12,8 @@ #include "src/modelchecker/results/ExplicitQualitativeCheckResult.h" #include "src/modelchecker/results/ExplicitQuantitativeCheckResult.h" +#include "src/storage/StronglyConnectedComponentDecomposition.h" + #include "src/exceptions/InvalidStateException.h" #include "src/exceptions/InvalidPropertyException.h" #include "src/exceptions/NotImplementedException.h" @@ -476,25 +478,193 @@ namespace storm { std::unique_ptr<CheckResult> subResultPointer = this->check(stateFormula); ExplicitQualitativeCheckResult const& subResult = subResultPointer->asExplicitQualitativeCheckResult(); - // Since we use the uniformized matrix to compute the steady state probabilities, we need to build it first. - ValueType uniformizationRate = 0; - for (auto const& rate : this->getModel().getExitRateVector()) { - uniformizationRate = std::max(uniformizationRate, rate); + storm::storage::SparseMatrix<ValueType> probabilityMatrix = this->computeProbabilityMatrix(this->getModel().getTransitionMatrix(), this->getModel().getExitRateVector()); + return std::unique_ptr<CheckResult>(new ExplicitQuantitativeCheckResult<ValueType>(computeLongRunAverageHelper(this->getModel(), probabilityMatrix, subResult.getTruthValuesVector(), &this->getModel().getExitRateVector(), qualitative, *linearEquationSolverFactory))); + } + + template<typename ValueType> + std::vector<ValueType> SparseCtmcCslModelChecker<ValueType>::computeLongRunAverageHelper(storm::models::sparse::DeterministicModel<ValueType> const& model, storm::storage::SparseMatrix<ValueType> const& transitionMatrix, storm::storage::BitVector const& psiStates, std::vector<ValueType> const* exitRateVector, bool qualitative, storm::utility::solver::LinearEquationSolverFactory<ValueType> const& linearEquationSolverFactory) { + // If there are no goal states, we avoid the computation and directly return zero. + uint_fast64_t numOfStates = transitionMatrix.getRowCount(); + if (psiStates.empty()) { + return std::vector<ValueType>(numOfStates, storm::utility::zero<ValueType>()); } - uniformizationRate *= 1.02; - STORM_LOG_THROW(uniformizationRate > 0, storm::exceptions::InvalidStateException, "The uniformization rate must be positive."); -// storm::storage::SparseMatrix<ValueType> uniformizedMatrix = this->computeUniformizedMatrix(this->getModel().getTransitionMatrix(), storm::storage::BitVector(this->getModel().getNumberOfStates(), true), uniformizationRate, this->getModel().getExitRateVector()); - -// storm::storage::SparseMatrix<ValueType> uniformizedMatrix = this->computeGeneratorMatrix(this->getModel().getTransitionMatrix(), this->getModel().getExitRateVector()); - storm::storage::SparseMatrix<ValueType> uniformizedMatrix = this->computeProbabilityMatrix(this->getModel().getTransitionMatrix(), this->getModel().getExitRateVector()); + // Likewise, if all bits are set, we can avoid the computation. + if (psiStates.full()) { + return std::vector<ValueType>(numOfStates, storm::utility::one<ValueType>()); + } + + // Start by decomposing the DTMC into its BSCCs. + storm::storage::StronglyConnectedComponentDecomposition<double> bsccDecomposition(model, false, true); + + // Get some data members for convenience. + ValueType one = storm::utility::one<ValueType>(); + ValueType zero = storm::utility::zero<ValueType>(); - std::vector<ValueType> result = SparseDtmcPrctlModelChecker<ValueType>::computeLongRunAverageHelper(this->getModel(), uniformizedMatrix, subResult.getTruthValuesVector(), qualitative, *linearEquationSolverFactory); + // First we check which states are in BSCCs. + storm::storage::BitVector statesInBsccs(numOfStates); + storm::storage::BitVector firstStatesInBsccs(numOfStates); + std::vector<uint_fast64_t> stateToBsccIndexMap(transitionMatrix.getColumnCount()); - return std::unique_ptr<CheckResult>(new ExplicitQuantitativeCheckResult<ValueType>()); + for (uint_fast64_t currentBsccIndex = 0; currentBsccIndex < bsccDecomposition.size(); ++currentBsccIndex) { + storm::storage::StronglyConnectedComponent const& bscc = bsccDecomposition[currentBsccIndex]; + + // Gather information for later use. + bool first = true; + for (auto const& state : bscc) { + statesInBsccs.set(state); + if (first) { + firstStatesInBsccs.set(state); + } + stateToBsccIndexMap[state] = currentBsccIndex; + first = false; + } + } + + firstStatesInBsccs = firstStatesInBsccs % statesInBsccs; + storm::storage::BitVector statesNotInBsccs = ~statesInBsccs; + + // Then we construct an equation system that yields the steady state probabilities for all states in BSCCs. + storm::storage::SparseMatrix<ValueType> bsccEquationSystem = transitionMatrix.getSubmatrix(false, statesInBsccs, statesInBsccs, true); + + // Since in the fix point equation, we need to multiply the vector from the left, we convert this to a + // multiplication from the right by transposing the system. + bsccEquationSystem = bsccEquationSystem.transpose(false, true); + + // Create an auxiliary structure that makes it easy to look up the indices within the set of BSCC states. + uint_fast64_t lastIndex = 0; + uint_fast64_t currentNumberOfSetBits = 0; + std::vector<uint_fast64_t> indexInStatesInBsccs; + indexInStatesInBsccs.reserve(transitionMatrix.getRowCount()); + for (auto index : statesInBsccs) { + while (lastIndex <= index) { + indexInStatesInBsccs.push_back(currentNumberOfSetBits); + ++lastIndex; + } + ++currentNumberOfSetBits; + } + + // Now build the final equation system matrix. + storm::storage::SparseMatrixBuilder<ValueType> builder; + uint_fast64_t currentBsccIndex = 0; + for (uint_fast64_t row = 0; row < bsccEquationSystem.getRowCount(); ++row) { + // If the current row is the first one belonging to a BSCC, we substitute it by the constraint that the + // values for states of this BSCC must sum to one. + if (firstStatesInBsccs.get(row)) { + storm::storage::StronglyConnectedComponent const& bscc = bsccDecomposition[currentBsccIndex]; + + for (auto const& state : bscc) { + builder.addNextValue(row, indexInStatesInBsccs[state], one); + } + ++currentBsccIndex; + } else { + // Otherwise, we copy the row, and subtract 1 from the diagonal. + for (auto& entry : bsccEquationSystem.getRow(row)) { + if (entry.getColumn() == row) { + builder.addNextValue(row, entry.getColumn(), entry.getValue() - one); + } else { + builder.addNextValue(row, entry.getColumn(), entry.getValue()); + } + } + } + } + + bsccEquationSystem = builder.build(); + + std::vector<ValueType> bsccEquationSystemRightSide(bsccEquationSystem.getColumnCount(), zero); + storm::utility::vector::setVectorValues(bsccEquationSystemRightSide, firstStatesInBsccs, one); + + // As an initial guess, we take the uniform distribution over all states of the containing BSCC. + std::vector<ValueType> bsccEquationSystemSolution(bsccEquationSystem.getColumnCount(), zero); + for (uint_fast64_t currentBsccIndex = 0; currentBsccIndex < bsccDecomposition.size(); ++currentBsccIndex) { + storm::storage::StronglyConnectedComponent const& bscc = bsccDecomposition[currentBsccIndex]; + + for (auto const& state : bscc) { + bsccEquationSystemSolution[indexInStatesInBsccs[state]] = one / bscc.size(); + } + } + + { + std::unique_ptr<storm::solver::LinearEquationSolver<ValueType>> solver = linearEquationSolverFactory.create(bsccEquationSystem); + solver->solveEquationSystem(bsccEquationSystemSolution, bsccEquationSystemRightSide); + } + + // If exit rates were given, we need to 'fix' the results to also account for the timing behaviour. + if (exitRateVector != nullptr) { + std::vector<ValueType> bsccTotalValue(bsccDecomposition.size(), zero); + std::size_t i = 0; + for (auto stateIter = statesInBsccs.begin(); stateIter != statesInBsccs.end(); ++i, ++stateIter) { + bsccTotalValue[stateToBsccIndexMap[*stateIter]] += bsccEquationSystemSolution[i] * (one / (*exitRateVector)[*stateIter]); + } + + i = 0; + for (auto stateIter = statesInBsccs.begin(); stateIter != statesInBsccs.end(); ++i, ++stateIter) { + bsccEquationSystemSolution[i] = (bsccEquationSystemSolution[i] * (one / (*exitRateVector)[*stateIter])) / bsccTotalValue[stateToBsccIndexMap[*stateIter]]; + } + } + + // Calculate LRA Value for each BSCC from steady state distribution in BSCCs. + std::vector<ValueType> bsccLra(bsccDecomposition.size(), zero); + size_t i = 0; + for (auto stateIter = statesInBsccs.begin(); stateIter != statesInBsccs.end(); ++i, ++stateIter) { + if (psiStates.get(*stateIter)) { + bsccLra[stateToBsccIndexMap[*stateIter]] += bsccEquationSystemSolution[i]; + } + } + + std::vector<ValueType> rewardSolution; + if (!statesNotInBsccs.empty()) { + // Calculate LRA for states not in bsccs as expected reachability rewards. + // Target states are states in bsccs, transition reward is the lra of the bscc for each transition into a + // bscc and 0 otherwise. This corresponds to the sum of LRAs in BSCC weighted by the reachability probability + // of the BSCC. + + std::vector<ValueType> rewardRightSide; + rewardRightSide.reserve(statesNotInBsccs.getNumberOfSetBits()); + + for (auto state : statesNotInBsccs) { + ValueType reward = zero; + for (auto entry : transitionMatrix.getRow(state)) { + if (statesInBsccs.get(entry.getColumn())) { + reward += entry.getValue() * bsccLra[stateToBsccIndexMap[entry.getColumn()]]; + } + } + rewardRightSide.push_back(reward); + } + + storm::storage::SparseMatrix<ValueType> rewardEquationSystemMatrix = transitionMatrix.getSubmatrix(false, statesNotInBsccs, statesNotInBsccs, true); + rewardEquationSystemMatrix.convertToEquationSystem(); + + rewardSolution = std::vector<ValueType>(rewardEquationSystemMatrix.getColumnCount(), one); + + { + std::unique_ptr<storm::solver::LinearEquationSolver<ValueType>> solver = linearEquationSolverFactory.create(rewardEquationSystemMatrix); + solver->solveEquationSystem(rewardSolution, rewardRightSide); + } + } + + // Fill the result vector. + std::vector<ValueType> result(numOfStates); + auto rewardSolutionIter = rewardSolution.begin(); + for (size_t state = 0; state < numOfStates; ++state) { + if (statesInBsccs.get(state)) { + // Assign the value of the bscc the state is in. + result[state] = bsccLra[stateToBsccIndexMap[state]]; + } else { + STORM_LOG_ASSERT(rewardSolutionIter != rewardSolution.end(), "Too few elements in solution."); + // Take the value from the reward computation. Since the n-th state not in any bscc is the n-th + // entry in rewardSolution we can just take the next value from the iterator. + result[state] = *rewardSolutionIter; + ++rewardSolutionIter; + } + } + + return result; } + // Explicitly instantiate the model checker. template class SparseCtmcCslModelChecker<double>; diff --git a/src/modelchecker/csl/SparseCtmcCslModelChecker.h b/src/modelchecker/csl/SparseCtmcCslModelChecker.h index 35edab8cf..42a1c9a0f 100644 --- a/src/modelchecker/csl/SparseCtmcCslModelChecker.h +++ b/src/modelchecker/csl/SparseCtmcCslModelChecker.h @@ -13,10 +13,14 @@ namespace storm { template<storm::dd::DdType DdType, typename ValueType> class HybridCtmcCslModelChecker; + template<typename ValueType> + class SparseDtmcPrctlModelChecker; + template<class ValueType> class SparseCtmcCslModelChecker : public SparsePropositionalModelChecker<ValueType> { public: friend class HybridCtmcCslModelChecker<storm::dd::DdType::CUDD, ValueType>; + friend class SparseDtmcPrctlModelChecker<ValueType>; explicit SparseCtmcCslModelChecker(storm::models::sparse::Ctmc<ValueType> const& model); explicit SparseCtmcCslModelChecker(storm::models::sparse::Ctmc<ValueType> const& model, std::unique_ptr<storm::utility::solver::LinearEquationSolverFactory<ValueType>>&& linearEquationSolverFactory); @@ -40,7 +44,7 @@ namespace storm { static std::vector<ValueType> computeUntilProbabilitiesHelper(storm::storage::SparseMatrix<ValueType> const& transitionMatrix, storm::storage::SparseMatrix<ValueType> const& backwardTransitions, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, bool qualitative, storm::utility::solver::LinearEquationSolverFactory<ValueType> const& linearEquationSolverFactory); std::vector<ValueType> computeInstantaneousRewardsHelper(double timeBound) const; std::vector<ValueType> computeCumulativeRewardsHelper(double timeBound) const; - + static std::vector<ValueType> computeLongRunAverageHelper(storm::models::sparse::DeterministicModel<ValueType> const& model, storm::storage::SparseMatrix<ValueType> const& transitionMatrix, storm::storage::BitVector const& psiStates, std::vector<ValueType> const* exitRateVector, bool qualitative, storm::utility::solver::LinearEquationSolverFactory<ValueType> const& linearEquationSolverFactory); /*! * Computes the matrix representing the transitions of the uniformized CTMC. * diff --git a/src/modelchecker/prctl/SparseDtmcPrctlModelChecker.cpp b/src/modelchecker/prctl/SparseDtmcPrctlModelChecker.cpp index 7237fe765..538aa78aa 100644 --- a/src/modelchecker/prctl/SparseDtmcPrctlModelChecker.cpp +++ b/src/modelchecker/prctl/SparseDtmcPrctlModelChecker.cpp @@ -8,11 +8,11 @@ #include "src/utility/graph.h" #include "src/utility/solver.h" +#include "src/modelchecker/csl/SparseCtmcCslModelChecker.h" #include "src/modelchecker/results/ExplicitQualitativeCheckResult.h" #include "src/modelchecker/results/ExplicitQuantitativeCheckResult.h" #include "src/exceptions/InvalidStateException.h" -#include "src/storage/StronglyConnectedComponentDecomposition.h" #include "src/exceptions/InvalidPropertyException.h" @@ -302,280 +302,13 @@ namespace storm { ExplicitQualitativeCheckResult const& subResult = subResultPointer->asExplicitQualitativeCheckResult(); return std::unique_ptr<CheckResult>(new ExplicitQuantitativeCheckResult<ValueType>(this->computeReachabilityRewardsHelper(this->getModel().getTransitionMatrix(), this->getModel().getOptionalStateRewardVector(), this->getModel().getOptionalTransitionRewardMatrix(), this->getModel().getBackwardTransitions(), subResult.getTruthValuesVector(), *this->linearEquationSolverFactory, qualitative))); } - - template<typename ValueType> - std::vector<ValueType> SparseDtmcPrctlModelChecker<ValueType>::computeLongRunAverageHelper(storm::models::sparse::DeterministicModel<ValueType> const& model, storm::storage::SparseMatrix<ValueType> const& transitionMatrix, storm::storage::BitVector const& psiStates, bool qualitative, storm::utility::solver::LinearEquationSolverFactory<ValueType> const& linearEquationSolverFactory) { - // If there are no goal states, we avoid the computation and directly return zero. - uint_fast64_t numOfStates = transitionMatrix.getRowCount(); - if (psiStates.empty()) { - return std::vector<ValueType>(numOfStates, storm::utility::zero<ValueType>()); - } - - // Likewise, if all bits are set, we can avoid the computation. - if (psiStates.full()) { - return std::vector<ValueType>(numOfStates, storm::utility::one<ValueType>()); - } - - // Start by decomposing the DTMC into its BSCCs. - storm::storage::StronglyConnectedComponentDecomposition<double> bsccDecomposition(model, false, true); - - // Get some data members for convenience. - ValueType one = storm::utility::one<ValueType>(); - ValueType zero = storm::utility::zero<ValueType>(); - - // First we check which states are in BSCCs. We use this later to speed up reachability analysis - storm::storage::BitVector statesInBsccs(numOfStates); - storm::storage::BitVector statesInBsccsWithoutFirst(numOfStates); - - std::vector<uint_fast64_t> stateToBsccIndexMap(transitionMatrix.getColumnCount()); - - for (uint_fast64_t currentBsccIndex = 0; currentBsccIndex < bsccDecomposition.size(); ++currentBsccIndex) { - storm::storage::StronglyConnectedComponent const& bscc = bsccDecomposition[currentBsccIndex]; - - // Gather information for later use. - bool first = true; - for (auto const& state : bscc) { - statesInBsccs.set(state); - if (!first) { - statesInBsccsWithoutFirst.set(state); - } - stateToBsccIndexMap[state] = currentBsccIndex; - first = false; - } - } - - storm::storage::BitVector statesNotInBsccs = ~statesInBsccs; - - std::cout << transitionMatrix << std::endl; - - // Calculate steady state distribution for all BSCCs by calculating an eigenvector for the eigenvalue 1 of - // the transposed transition matrix for the BSCCs. - storm::storage::SparseMatrix<ValueType> bsccEquationSystem = transitionMatrix.getSubmatrix(false, statesInBsccs, statesInBsccs, true); - - std::cout << bsccEquationSystem << std::endl; - - bsccEquationSystem = bsccEquationSystem.transpose(false, true); - - std::cout << bsccEquationSystem << std::endl; - - // Subtract identity matrix. - for (uint_fast64_t row = 0; row < bsccEquationSystem.getRowCount(); ++row) { - for (auto& entry : bsccEquationSystem.getRow(row)) { - if (entry.getColumn() == row) { - entry.setValue(entry.getValue() - one); - } - } - } - - std::cout << bsccEquationSystem << std::endl; - - std::cout << statesInBsccsWithoutFirst << " // " << statesInBsccs << std::endl; - bsccEquationSystem = bsccEquationSystem.getSubmatrix(false, statesInBsccsWithoutFirst, statesInBsccs, false); - - std::cout << bsccEquationSystem << std::endl; - - // For each BSCC, add a row to the matrix that expresses that the sum over all entries in this BSCC needs to be one. - storm::storage::SparseMatrixBuilder<ValueType> builder(std::move(bsccEquationSystem)); - typename storm::storage::SparseMatrixBuilder<ValueType>::index_type row = builder.getLastRow() + 1; - - for (uint_fast64_t currentBsccIndex = 0; currentBsccIndex < bsccDecomposition.size(); ++currentBsccIndex) { - storm::storage::StronglyConnectedComponent const& bscc = bsccDecomposition[currentBsccIndex]; - - for (auto const& state : bscc) { - builder.addNextValue(row, state, one); - } - ++row; - } - bsccEquationSystem = builder.build(); - - std::cout << bsccEquationSystem << std::endl; - - std::vector<ValueType> bsccEquationSystemRightSide(bsccEquationSystem.getColumnCount(), zero); - bsccEquationSystemRightSide.back() = one; - std::vector<ValueType> bsccEquationSystemSolution(bsccEquationSystem.getColumnCount(), one); - { - std::unique_ptr<storm::solver::LinearEquationSolver<ValueType>> solver = linearEquationSolverFactory.create(bsccEquationSystem); - solver->solveEquationSystem(bsccEquationSystemSolution, bsccEquationSystemRightSide); - } - - ValueType sum = zero; - for (auto const& elem : bsccEquationSystemSolution) { - std::cout << "sol " << elem << std::endl; - sum += elem; - } - std::cout << "sum: " << sum << std::endl; - std::cout << "in " << bsccDecomposition.size() << "bsccs" << std::endl; - - // Calculate LRA Value for each BSCC from steady state distribution in BSCCs. - // We have to scale the results, as the probabilities for each BSCC have to sum up to one, which they don't - // necessarily do in the solution of the equation system. - std::vector<ValueType> bsccLra(bsccDecomposition.size(), zero); - std::vector<ValueType> bsccTotalValue(bsccDecomposition.size(), zero); - size_t i = 0; - for (auto stateIter = statesInBsccs.begin(); stateIter != statesInBsccs.end(); ++i, ++stateIter) { - if (psiStates.get(*stateIter)) { - bsccLra[stateToBsccIndexMap[*stateIter]] += bsccEquationSystemSolution[i]; - } - bsccTotalValue[stateToBsccIndexMap[*stateIter]] += bsccEquationSystemSolution[i]; - } - for (i = 0; i < bsccLra.size(); ++i) { - bsccLra[i] /= bsccTotalValue[i]; - } - - std::vector<ValueType> rewardSolution; - if (!statesNotInBsccs.empty()) { - // Calculate LRA for states not in bsccs as expected reachability rewards. - // Target states are states in bsccs, transition reward is the lra of the bscc for each transition into a - // bscc and 0 otherwise. This corresponds to the sum of LRAs in BSCC weighted by the reachability probability - // of the BSCC. - - std::vector<ValueType> rewardRightSide; - rewardRightSide.reserve(statesNotInBsccs.getNumberOfSetBits()); - - for (auto state : statesNotInBsccs) { - ValueType reward = zero; - for (auto entry : transitionMatrix.getRow(state)) { - if (statesInBsccs.get(entry.getColumn())) { - reward += entry.getValue() * bsccLra[stateToBsccIndexMap[entry.getColumn()]]; - } - } - rewardRightSide.push_back(reward); - } - - storm::storage::SparseMatrix<ValueType> rewardEquationSystemMatrix = transitionMatrix.getSubmatrix(false, statesNotInBsccs, statesNotInBsccs, true); - rewardEquationSystemMatrix.convertToEquationSystem(); - - rewardSolution = std::vector<ValueType>(rewardEquationSystemMatrix.getColumnCount(), one); - - { - std::unique_ptr<storm::solver::LinearEquationSolver<ValueType>> solver = linearEquationSolverFactory.create(rewardEquationSystemMatrix); - solver->solveEquationSystem(rewardSolution, rewardRightSide); - } - } - - // Fill the result vector. - std::vector<ValueType> result(numOfStates); - auto rewardSolutionIter = rewardSolution.begin(); - for (size_t state = 0; state < numOfStates; ++state) { - if (statesInBsccs.get(state)) { - // Assign the value of the bscc the state is in. - result[state] = bsccLra[stateToBsccIndexMap[state]]; - } else { - STORM_LOG_ASSERT(rewardSolutionIter != rewardSolution.end(), "Too few elements in solution."); - // Take the value from the reward computation. Since the n-th state not in any bscc is the n-th - // entry in rewardSolution we can just take the next value from the iterator. - result[state] = *rewardSolutionIter; - ++rewardSolutionIter; - } - } - - return result; - - //old implementeation - - //now we calculate the long run average for each BSCC in isolation and compute the weighted contribution of the BSCC to the LRA value of all states - //for (uint_fast64_t currentBsccIndex = 0; currentBsccIndex < bsccDecomposition.size(); ++currentBsccIndex) { - // storm::storage::StronglyConnectedComponent const& bscc = bsccDecomposition[currentBsccIndex]; - - // storm::storage::BitVector statesInThisBscc(numOfStates); - // for (auto const& state : bscc) { - // statesInThisBscc.set(state); - // } - - // //ValueType lraForThisBscc = this->computeLraForBSCC(transitionMatrix, psiStates, bscc); - // ValueType lraForThisBscc = bsccLra[currentBsccIndex]; - - // //the LRA value of a BSCC contributes to the LRA value of a state with the probability of reaching that BSCC from that state - // //thus we add Prob(Eventually statesInThisBscc) * lraForThisBscc to the result vector - - // //the reachability probabilities will be zero in other BSCCs, thus we can set the left operand of the until formula to statesNotInBsccs as an optimization - // std::vector<ValueType> reachProbThisBscc = this->computeUntilProbabilitiesHelper(statesNotInBsccs, statesInThisBscc, false); - // - // storm::utility::vector::scaleVectorInPlace<ValueType>(reachProbThisBscc, lraForThisBscc); - // storm::utility::vector::addVectorsInPlace<ValueType>(result, reachProbThisBscc); - //} - - //return result; - } template<typename ValueType> std::unique_ptr<CheckResult> SparseDtmcPrctlModelChecker<ValueType>::computeLongRunAverage(storm::logic::StateFormula const& stateFormula, bool qualitative, boost::optional<storm::logic::OptimalityType> const& optimalityType) { std::unique_ptr<CheckResult> subResultPointer = this->check(stateFormula); ExplicitQualitativeCheckResult const& subResult = subResultPointer->asExplicitQualitativeCheckResult(); - return std::unique_ptr<CheckResult>(new ExplicitQuantitativeCheckResult<ValueType>(this->computeLongRunAverageHelper(this->getModel(), this->getModel().getTransitionMatrix(), subResult.getTruthValuesVector(), qualitative, *linearEquationSolverFactory))); - } - - - template<typename ValueType> - ValueType SparseDtmcPrctlModelChecker<ValueType>::computeLraForBSCC(storm::storage::SparseMatrix<ValueType> const& transitionMatrix, storm::storage::BitVector const& psiStates, storm::storage::StronglyConnectedComponent const& bscc, storm::utility::solver::LinearEquationSolverFactory<ValueType> const& linearEquationSolverFactory) { - //if size is one we can immediately derive the result - if (bscc.size() == 1){ - if (psiStates.get(*(bscc.begin()))) { - return storm::utility::one<ValueType>(); - } else{ - return storm::utility::zero<ValueType>(); - } - } - - storm::storage::BitVector subsystem = storm::storage::BitVector(transitionMatrix.getRowCount()); - subsystem.set(bscc.begin(), bscc.end()); - - //we now have to solve ((P')^t - I) * x = 0, where P' is the submatrix of transitionMatrix, - // ^t means transose, and I is the identity matrix. - - storm::storage::SparseMatrix<ValueType> subsystemMatrix = transitionMatrix.getSubmatrix(false, subsystem, subsystem, true); - subsystemMatrix = subsystemMatrix.transpose(); - - // subtract 1 on the diagonal and at the same time add a row with all ones to enforce that the result of the equation system is unique - storm::storage::SparseMatrixBuilder<ValueType> equationSystemBuilder(subsystemMatrix.getRowCount() + 1, subsystemMatrix.getColumnCount(), subsystemMatrix.getEntryCount() + subsystemMatrix.getColumnCount()); - ValueType one = storm::utility::one<ValueType>(); - ValueType zero = storm::utility::zero<ValueType>(); - bool foundDiagonalElement = false; - for (uint_fast64_t row = 0; row < subsystemMatrix.getRowCount(); ++row) { - for (auto& entry : subsystemMatrix.getRowGroup(row)) { - if (entry.getColumn() == row) { - equationSystemBuilder.addNextValue(row, entry.getColumn(), entry.getValue() - one); - foundDiagonalElement = true; - } else { - equationSystemBuilder.addNextValue(row, entry.getColumn(), entry.getValue()); - } - } - - // Throw an exception if a row did not have an element on the diagonal. - STORM_LOG_THROW(foundDiagonalElement, storm::exceptions::InvalidOperationException, "Internal Error, no diagonal entry found."); - } - for (uint_fast64_t column = 0; column + 1 < subsystemMatrix.getColumnCount(); ++column) { - equationSystemBuilder.addNextValue(subsystemMatrix.getRowCount(), column, one); - } - equationSystemBuilder.addNextValue(subsystemMatrix.getRowCount(), subsystemMatrix.getColumnCount() - 1, zero); - subsystemMatrix = equationSystemBuilder.build(); - - // create x and b for the equation system. setting the last entry of b to one enforces the sum over the unique solution vector is one - std::vector<ValueType> steadyStateDist(subsystemMatrix.getRowCount(), zero); - std::vector<ValueType> b(subsystemMatrix.getRowCount(), zero); - b[subsystemMatrix.getRowCount() - 1] = one; - - - { - auto solver = linearEquationSolverFactory.create(subsystemMatrix); - solver->solveEquationSystem(steadyStateDist, b); - } - - //remove the last entry of the vector as it was just there to enforce the unique solution - steadyStateDist.pop_back(); - - //calculate the fraction we spend in psi states on the long run - std::vector<ValueType> steadyStateForPsiStates(transitionMatrix.getRowCount() - 1, zero); - storm::utility::vector::setVectorValues(steadyStateForPsiStates, psiStates & subsystem, steadyStateDist); - - ValueType result = zero; - - for (auto value : steadyStateForPsiStates) { - result += value; - } - - return result; + return std::unique_ptr<CheckResult>(new ExplicitQuantitativeCheckResult<ValueType>(SparseCtmcCslModelChecker<ValueType>::computeLongRunAverageHelper(this->getModel(), this->getModel().getTransitionMatrix(), subResult.getTruthValuesVector(), nullptr, qualitative, *linearEquationSolverFactory))); } template<typename ValueType> diff --git a/src/modelchecker/prctl/SparseDtmcPrctlModelChecker.h b/src/modelchecker/prctl/SparseDtmcPrctlModelChecker.h index 212b99588..5409091fd 100644 --- a/src/modelchecker/prctl/SparseDtmcPrctlModelChecker.h +++ b/src/modelchecker/prctl/SparseDtmcPrctlModelChecker.h @@ -43,8 +43,6 @@ namespace storm { static std::vector<ValueType> computeReachabilityRewardsHelper(storm::storage::SparseMatrix<ValueType> const& transitionMatrix, boost::optional<std::vector<ValueType>> const& stateRewardVector, boost::optional<storm::storage::SparseMatrix<ValueType>> const& transitionRewardMatrix, storm::storage::SparseMatrix<ValueType> const& backwardTransitions, storm::storage::BitVector const& targetStates, storm::utility::solver::LinearEquationSolverFactory<ValueType> const& linearEquationSolverFactory, bool qualitative); static std::vector<ValueType> computeLongRunAverageHelper(storm::models::sparse::DeterministicModel<ValueType> const& model, storm::storage::SparseMatrix<ValueType> const& transitionMatrix, storm::storage::BitVector const& psiStates, bool qualitative, storm::utility::solver::LinearEquationSolverFactory<ValueType> const& linearEquationSolverFactory); - static ValueType computeLraForBSCC(storm::storage::SparseMatrix<ValueType> const& transitionMatrix, storm::storage::BitVector const& goalStates, storm::storage::StronglyConnectedComponent const& bscc, storm::utility::solver::LinearEquationSolverFactory<ValueType> const& linearEquationSolverFactory); - // An object that is used for retrieving linear equation solvers. std::unique_ptr<storm::utility::solver::LinearEquationSolverFactory<ValueType>> linearEquationSolverFactory; }; diff --git a/src/settings/modules/GmmxxEquationSolverSettings.cpp b/src/settings/modules/GmmxxEquationSolverSettings.cpp index 4b9f27f9e..359836968 100644 --- a/src/settings/modules/GmmxxEquationSolverSettings.cpp +++ b/src/settings/modules/GmmxxEquationSolverSettings.cpp @@ -7,7 +7,7 @@ namespace storm { namespace modules { const std::string GmmxxEquationSolverSettings::moduleName = "gmm++"; - const std::string GmmxxEquationSolverSettings::techniqueOptionName = "tech"; + const std::string GmmxxEquationSolverSettings::techniqueOptionName = "method"; const std::string GmmxxEquationSolverSettings::preconditionOptionName = "precond"; const std::string GmmxxEquationSolverSettings::restartOptionName = "restart"; const std::string GmmxxEquationSolverSettings::maximalIterationsOptionName = "maxiter"; @@ -32,38 +32,38 @@ namespace storm { this->addOption(storm::settings::OptionBuilder(moduleName, absoluteOptionName, false, "Sets whether the relative or the absolute error is considered for detecting convergence.").build()); } - bool GmmxxEquationSolverSettings::isLinearEquationSystemTechniqueSet() const { + bool GmmxxEquationSolverSettings::isLinearEquationSystemMethodSet() const { return this->getOption(techniqueOptionName).getHasOptionBeenSet(); } - GmmxxEquationSolverSettings::LinearEquationTechnique GmmxxEquationSolverSettings::getLinearEquationSystemTechnique() const { + GmmxxEquationSolverSettings::LinearEquationMethod GmmxxEquationSolverSettings::getLinearEquationSystemMethod() const { std::string linearEquationSystemTechniqueAsString = this->getOption(techniqueOptionName).getArgumentByName("name").getValueAsString(); if (linearEquationSystemTechniqueAsString == "bicgstab") { - return GmmxxEquationSolverSettings::LinearEquationTechnique::Bicgstab; + return GmmxxEquationSolverSettings::LinearEquationMethod::Bicgstab; } else if (linearEquationSystemTechniqueAsString == "qmr") { - return GmmxxEquationSolverSettings::LinearEquationTechnique::Qmr; + return GmmxxEquationSolverSettings::LinearEquationMethod::Qmr; } else if (linearEquationSystemTechniqueAsString == "gmres") { - return GmmxxEquationSolverSettings::LinearEquationTechnique::Gmres; + return GmmxxEquationSolverSettings::LinearEquationMethod::Gmres; } else if (linearEquationSystemTechniqueAsString == "jacobi") { - return GmmxxEquationSolverSettings::LinearEquationTechnique::Jacobi; + return GmmxxEquationSolverSettings::LinearEquationMethod::Jacobi; } STORM_LOG_THROW(false, storm::exceptions::IllegalArgumentValueException, "Unknown solution technique '" << linearEquationSystemTechniqueAsString << "' selected."); } - bool GmmxxEquationSolverSettings::isPreconditioningTechniqueSet() const { + bool GmmxxEquationSolverSettings::isPreconditioningMethodSet() const { return this->getOption(preconditionOptionName).getHasOptionBeenSet(); } - GmmxxEquationSolverSettings::PreconditioningTechnique GmmxxEquationSolverSettings::getPreconditioningTechnique() const { - std::string preconditioningTechniqueAsString = this->getOption(preconditionOptionName).getArgumentByName("name").getValueAsString(); - if (preconditioningTechniqueAsString == "ilu") { - return GmmxxEquationSolverSettings::PreconditioningTechnique::Ilu; - } else if (preconditioningTechniqueAsString == "diagonal") { - return GmmxxEquationSolverSettings::PreconditioningTechnique::Diagonal; - } else if (preconditioningTechniqueAsString == "none") { - return GmmxxEquationSolverSettings::PreconditioningTechnique::None; + GmmxxEquationSolverSettings::PreconditioningMethod GmmxxEquationSolverSettings::getPreconditioningMethod() const { + std::string PreconditioningMethodAsString = this->getOption(preconditionOptionName).getArgumentByName("name").getValueAsString(); + if (PreconditioningMethodAsString == "ilu") { + return GmmxxEquationSolverSettings::PreconditioningMethod::Ilu; + } else if (PreconditioningMethodAsString == "diagonal") { + return GmmxxEquationSolverSettings::PreconditioningMethod::Diagonal; + } else if (PreconditioningMethodAsString == "none") { + return GmmxxEquationSolverSettings::PreconditioningMethod::None; } - STORM_LOG_THROW(false, storm::exceptions::IllegalArgumentValueException, "Unknown preconditioning technique '" << preconditioningTechniqueAsString << "' selected."); + STORM_LOG_THROW(false, storm::exceptions::IllegalArgumentValueException, "Unknown preconditioning technique '" << PreconditioningMethodAsString << "' selected."); } bool GmmxxEquationSolverSettings::isRestartIterationCountSet() const { @@ -99,7 +99,7 @@ namespace storm { } bool GmmxxEquationSolverSettings::check() const { - bool optionsSet = isLinearEquationSystemTechniqueSet() || isPreconditioningTechniqueSet() || isRestartIterationCountSet() | isMaximalIterationCountSet() || isPrecisionSet() || isConvergenceCriterionSet(); + bool optionsSet = isLinearEquationSystemMethodSet() || isPreconditioningMethodSet() || isRestartIterationCountSet() | isMaximalIterationCountSet() || isPrecisionSet() || isConvergenceCriterionSet(); STORM_LOG_WARN_COND(storm::settings::generalSettings().getEquationSolver() == storm::settings::modules::GeneralSettings::EquationSolver::Gmmxx || !optionsSet, "gmm++ is not selected as the equation solver, so setting options for gmm++ has no effect."); diff --git a/src/settings/modules/GmmxxEquationSolverSettings.h b/src/settings/modules/GmmxxEquationSolverSettings.h index 07bc90174..8cb0761a8 100644 --- a/src/settings/modules/GmmxxEquationSolverSettings.h +++ b/src/settings/modules/GmmxxEquationSolverSettings.h @@ -12,11 +12,11 @@ namespace storm { */ class GmmxxEquationSolverSettings : public ModuleSettings { public: - // An enumeration of all available techniques for solving linear equations. - enum class LinearEquationTechnique { Bicgstab, Qmr, Gmres, Jacobi }; + // An enumeration of all available methods for solving linear equations. + enum class LinearEquationMethod { Bicgstab, Qmr, Gmres, Jacobi }; - // An enumeration of all available preconditioning techniques. - enum class PreconditioningTechnique { Ilu, Diagonal, None }; + // An enumeration of all available preconditioning methods. + enum class PreconditioningMethod { Ilu, Diagonal, None }; // An enumeration of all available convergence criteria. enum class ConvergenceCriterion { Absolute, Relative }; @@ -29,32 +29,32 @@ namespace storm { GmmxxEquationSolverSettings(storm::settings::SettingsManager& settingsManager); /*! - * Retrieves whether the linear equation system technique has been set. + * Retrieves whether the linear equation system method has been set. * - * @return True iff the linear equation system technique has been set. + * @return True iff the linear equation system method has been set. */ - bool isLinearEquationSystemTechniqueSet() const; + bool isLinearEquationSystemMethodSet() const; /*! - * Retrieves the technique that is to be used for solving systems of linear equations. + * Retrieves the method that is to be used for solving systems of linear equations. * - * @return The technique to use. + * @return The method to use. */ - LinearEquationTechnique getLinearEquationSystemTechnique() const; + LinearEquationMethod getLinearEquationSystemMethod() const; /*! - * Retrieves whether the preconditioning technique has been set. + * Retrieves whether the preconditioning method has been set. * - * @return True iff the preconditioning technique has been set. + * @return True iff the preconditioning method has been set. */ - bool isPreconditioningTechniqueSet() const; + bool isPreconditioningMethodSet() const; /*! - * Retrieves the technique that is to be used for preconditioning solving systems of linear equations. + * Retrieves the method that is to be used for preconditioning solving systems of linear equations. * - * @return The technique to use. + * @return The method to use. */ - PreconditioningTechnique getPreconditioningTechnique() const; + PreconditioningMethod getPreconditioningMethod() const; /*! * Retrieves whether the restart iteration count has been set. @@ -64,7 +64,7 @@ namespace storm { bool isRestartIterationCountSet() const; /*! - * Retrieves the number of iterations after which restarted techniques are to be restarted. + * Retrieves the number of iterations after which restarted methods are to be restarted. * * @return The number of iterations after which to restart. */ diff --git a/src/settings/modules/NativeEquationSolverSettings.cpp b/src/settings/modules/NativeEquationSolverSettings.cpp index 76af99b3e..482713ed8 100644 --- a/src/settings/modules/NativeEquationSolverSettings.cpp +++ b/src/settings/modules/NativeEquationSolverSettings.cpp @@ -7,20 +7,23 @@ namespace storm { namespace modules { const std::string NativeEquationSolverSettings::moduleName = "native"; - const std::string NativeEquationSolverSettings::techniqueOptionName = "tech"; + const std::string NativeEquationSolverSettings::techniqueOptionName = "method"; + const std::string NativeEquationSolverSettings::omegaOptionName = "soromega"; const std::string NativeEquationSolverSettings::maximalIterationsOptionName = "maxiter"; const std::string NativeEquationSolverSettings::maximalIterationsOptionShortName = "i"; const std::string NativeEquationSolverSettings::precisionOptionName = "precision"; const std::string NativeEquationSolverSettings::absoluteOptionName = "absolute"; NativeEquationSolverSettings::NativeEquationSolverSettings(storm::settings::SettingsManager& settingsManager) : ModuleSettings(settingsManager, moduleName) { - std::vector<std::string> methods = { "jacobi" }; + std::vector<std::string> methods = { "jacobi", "gaussseidel", "sor" }; this->addOption(storm::settings::OptionBuilder(moduleName, techniqueOptionName, true, "The method to be used for solving linear equation systems with the native engine. Available are: { jacobi }.").addArgument(storm::settings::ArgumentBuilder::createStringArgument("name", "The name of the method to use.").addValidationFunctionString(storm::settings::ArgumentValidators::stringInListValidator(methods)).setDefaultValueString("jacobi").build()).build()); this->addOption(storm::settings::OptionBuilder(moduleName, maximalIterationsOptionName, false, "The maximal number of iterations to perform before iterative solving is aborted.").setShortName(maximalIterationsOptionShortName).addArgument(storm::settings::ArgumentBuilder::createUnsignedIntegerArgument("count", "The maximal iteration count.").setDefaultValueUnsignedInteger(20000).build()).build()); this->addOption(storm::settings::OptionBuilder(moduleName, precisionOptionName, false, "The precision used for detecting convergence of iterative methods.").addArgument(storm::settings::ArgumentBuilder::createDoubleArgument("value", "The precision to achieve.").setDefaultValueDouble(1e-06).addValidationFunctionDouble(storm::settings::ArgumentValidators::doubleRangeValidatorExcluding(0.0, 1.0)).build()).build()); + this->addOption(storm::settings::OptionBuilder(moduleName, omegaOptionName, false, "The omega used for SOR.").addArgument(storm::settings::ArgumentBuilder::createDoubleArgument("value", "The value of the SOR parameter.").setDefaultValueDouble(0.9).addValidationFunctionDouble(storm::settings::ArgumentValidators::doubleRangeValidatorExcluding(0.0, 1.0)).build()).build()); + this->addOption(storm::settings::OptionBuilder(moduleName, absoluteOptionName, false, "Sets whether the relative or the absolute error is considered for detecting convergence.").build()); } @@ -28,10 +31,14 @@ namespace storm { return this->getOption(techniqueOptionName).getHasOptionBeenSet(); } - NativeEquationSolverSettings::LinearEquationTechnique NativeEquationSolverSettings::getLinearEquationSystemTechnique() const { + NativeEquationSolverSettings::LinearEquationMethod NativeEquationSolverSettings::getLinearEquationSystemMethod() const { std::string linearEquationSystemTechniqueAsString = this->getOption(techniqueOptionName).getArgumentByName("name").getValueAsString(); if (linearEquationSystemTechniqueAsString == "jacobi") { - return NativeEquationSolverSettings::LinearEquationTechnique::Jacobi; + return NativeEquationSolverSettings::LinearEquationMethod::Jacobi; + } else if (linearEquationSystemTechniqueAsString == "gaussseidel") { + return NativeEquationSolverSettings::LinearEquationMethod::GaussSeidel; + } else if (linearEquationSystemTechniqueAsString == "sor") { + return NativeEquationSolverSettings::LinearEquationMethod::SOR; } STORM_LOG_THROW(false, storm::exceptions::IllegalArgumentValueException, "Unknown solution technique '" << linearEquationSystemTechniqueAsString << "' selected."); } @@ -51,6 +58,10 @@ namespace storm { double NativeEquationSolverSettings::getPrecision() const { return this->getOption(precisionOptionName).getArgumentByName("value").getValueAsDouble(); } + + double NativeEquationSolverSettings::getOmega() const { + return this->getOption(omegaOptionName).getArgumentByName("value").getValueAsDouble(); + } bool NativeEquationSolverSettings::isConvergenceCriterionSet() const { return this->getOption(absoluteOptionName).getHasOptionBeenSet(); diff --git a/src/settings/modules/NativeEquationSolverSettings.h b/src/settings/modules/NativeEquationSolverSettings.h index 9d6e43ecd..01c0fd7c9 100644 --- a/src/settings/modules/NativeEquationSolverSettings.h +++ b/src/settings/modules/NativeEquationSolverSettings.h @@ -12,8 +12,8 @@ namespace storm { */ class NativeEquationSolverSettings : public ModuleSettings { public: - // An enumeration of all available techniques for solving linear equations. - enum class LinearEquationTechnique { Jacobi }; + // An enumeration of all available methods for solving linear equations. + enum class LinearEquationMethod { Jacobi, GaussSeidel, SOR }; // An enumeration of all available convergence criteria. enum class ConvergenceCriterion { Absolute, Relative }; @@ -33,11 +33,11 @@ namespace storm { bool isLinearEquationSystemTechniqueSet() const; /*! - * Retrieves the technique that is to be used for solving systems of linear equations. + * Retrieves the method that is to be used for solving systems of linear equations. * - * @return The technique to use. + * @return The method to use. */ - LinearEquationTechnique getLinearEquationSystemTechnique() const; + LinearEquationMethod getLinearEquationSystemMethod() const; /*! * Retrieves whether the maximal iteration count has been set. @@ -67,6 +67,13 @@ namespace storm { */ double getPrecision() const; + /*! + * Retrieves the value of omega to be used for the SOR method. + * + * @return The value of omega to be used. + */ + double getOmega() const; + /*! * Retrieves whether the convergence criterion has been set. * @@ -89,6 +96,7 @@ namespace storm { private: // Define the string names of the options as constants. static const std::string techniqueOptionName; + static const std::string omegaOptionName; static const std::string maximalIterationsOptionName; static const std::string maximalIterationsOptionShortName; static const std::string precisionOptionName; diff --git a/src/solver/GmmxxLinearEquationSolver.cpp b/src/solver/GmmxxLinearEquationSolver.cpp index cbf2eafc8..bd43c3e88 100644 --- a/src/solver/GmmxxLinearEquationSolver.cpp +++ b/src/solver/GmmxxLinearEquationSolver.cpp @@ -31,24 +31,24 @@ namespace storm { restart = settings.getRestartIterationCount(); // Determine the method to be used. - storm::settings::modules::GmmxxEquationSolverSettings::LinearEquationTechnique methodAsSetting = settings.getLinearEquationSystemTechnique(); - if (methodAsSetting == storm::settings::modules::GmmxxEquationSolverSettings::LinearEquationTechnique::Bicgstab) { + storm::settings::modules::GmmxxEquationSolverSettings::LinearEquationMethod methodAsSetting = settings.getLinearEquationSystemMethod(); + if (methodAsSetting == storm::settings::modules::GmmxxEquationSolverSettings::LinearEquationMethod::Bicgstab) { method = SolutionMethod::Bicgstab; - } else if (methodAsSetting == storm::settings::modules::GmmxxEquationSolverSettings::LinearEquationTechnique::Qmr) { + } else if (methodAsSetting == storm::settings::modules::GmmxxEquationSolverSettings::LinearEquationMethod::Qmr) { method = SolutionMethod::Qmr; - } else if (methodAsSetting == storm::settings::modules::GmmxxEquationSolverSettings::LinearEquationTechnique::Gmres) { + } else if (methodAsSetting == storm::settings::modules::GmmxxEquationSolverSettings::LinearEquationMethod::Gmres) { method = SolutionMethod::Gmres; - } else if (methodAsSetting == storm::settings::modules::GmmxxEquationSolverSettings::LinearEquationTechnique::Jacobi) { + } else if (methodAsSetting == storm::settings::modules::GmmxxEquationSolverSettings::LinearEquationMethod::Jacobi) { method = SolutionMethod::Jacobi; } // Check which preconditioner to use. - storm::settings::modules::GmmxxEquationSolverSettings::PreconditioningTechnique preconditionAsSetting = settings.getPreconditioningTechnique(); - if (preconditionAsSetting == storm::settings::modules::GmmxxEquationSolverSettings::PreconditioningTechnique::Ilu) { + storm::settings::modules::GmmxxEquationSolverSettings::PreconditioningMethod preconditionAsSetting = settings.getPreconditioningMethod(); + if (preconditionAsSetting == storm::settings::modules::GmmxxEquationSolverSettings::PreconditioningMethod::Ilu) { preconditioner = Preconditioner::Ilu; - } else if (preconditionAsSetting == storm::settings::modules::GmmxxEquationSolverSettings::PreconditioningTechnique::Diagonal) { + } else if (preconditionAsSetting == storm::settings::modules::GmmxxEquationSolverSettings::PreconditioningMethod::Diagonal) { preconditioner = Preconditioner::Diagonal; - } else if (preconditionAsSetting == storm::settings::modules::GmmxxEquationSolverSettings::PreconditioningTechnique::None) { + } else if (preconditionAsSetting == storm::settings::modules::GmmxxEquationSolverSettings::PreconditioningMethod::None) { preconditioner = Preconditioner::None; } } @@ -149,12 +149,6 @@ namespace storm { uint_fast64_t GmmxxLinearEquationSolver<ValueType>::solveLinearEquationSystemWithJacobi(storm::storage::SparseMatrix<ValueType> const& A, std::vector<ValueType>& x, std::vector<ValueType> const& b, std::vector<ValueType>* multiplyResult) const { // Get a Jacobi decomposition of the matrix A. std::pair<storm::storage::SparseMatrix<ValueType>, std::vector<ValueType>> jacobiDecomposition = A.getJacobiDecomposition(); - std::cout << "LU" << std::endl; - std::cout << jacobiDecomposition.first << std::endl; - for (auto const& elem : jacobiDecomposition.second) { - std::cout << elem << std::endl; - } - std::cout << "----" << std::endl; // Convert the LU matrix to gmm++'s format. std::unique_ptr<gmm::csr_matrix<ValueType>> gmmLU = storm::adapters::GmmxxAdapter::toGmmxxSparseMatrix<ValueType>(std::move(jacobiDecomposition.first)); diff --git a/src/solver/NativeLinearEquationSolver.cpp b/src/solver/NativeLinearEquationSolver.cpp index a2480ddc5..a389853c6 100644 --- a/src/solver/NativeLinearEquationSolver.cpp +++ b/src/solver/NativeLinearEquationSolver.cpp @@ -15,7 +15,7 @@ namespace storm { } template<typename ValueType> - NativeLinearEquationSolver<ValueType>::NativeLinearEquationSolver(storm::storage::SparseMatrix<ValueType> const& A) : A(A) { + NativeLinearEquationSolver<ValueType>::NativeLinearEquationSolver(storm::storage::SparseMatrix<ValueType> const& A, SolutionMethod method) : A(A), method(method) { // Get the settings object to customize linear solving. storm::settings::modules::NativeEquationSolverSettings const& settings = storm::settings::nativeEquationSolverSettings(); @@ -23,61 +23,94 @@ namespace storm { maximalNumberOfIterations = settings.getMaximalIterationCount(); precision = settings.getPrecision(); relative = settings.getConvergenceCriterion() == storm::settings::modules::NativeEquationSolverSettings::ConvergenceCriterion::Relative; - - // Determine the method to be used. - storm::settings::modules::NativeEquationSolverSettings::LinearEquationTechnique methodAsSetting = settings.getLinearEquationSystemTechnique(); - if (methodAsSetting == storm::settings::modules::NativeEquationSolverSettings::LinearEquationTechnique::Jacobi) { - method = SolutionMethod::Jacobi; - } } template<typename ValueType> void NativeLinearEquationSolver<ValueType>::solveEquationSystem(std::vector<ValueType>& x, std::vector<ValueType> const& b, std::vector<ValueType>* multiplyResult) const { - // Get a Jacobi decomposition of the matrix A. - std::pair<storm::storage::SparseMatrix<ValueType>, std::vector<ValueType>> jacobiDecomposition = A.getJacobiDecomposition(); - - // To avoid copying the contents of the vector in the loop, we create a temporary x to swap with. - bool multiplyResultProvided = true; - std::vector<ValueType>* nextX = multiplyResult; - if (nextX == nullptr) { - nextX = new std::vector<ValueType>(x.size()); - multiplyResultProvided = false; - } - std::vector<ValueType> const* copyX = nextX; - std::vector<ValueType>* currentX = &x; - - // Target vector for precision calculation. - std::vector<ValueType> tmpX(x.size()); - - // Set up additional environment variables. - uint_fast64_t iterationCount = 0; - bool converged = false; - - while (!converged && iterationCount < maximalNumberOfIterations) { - // Compute D^-1 * (b - LU * x) and store result in nextX. - jacobiDecomposition.first.multiplyWithVector(*currentX, tmpX); - storm::utility::vector::subtractVectors(b, tmpX, tmpX); - storm::utility::vector::multiplyVectorsPointwise(jacobiDecomposition.second, tmpX, *nextX); + if (method == SolutionMethod::SOR || method == SolutionMethod::GaussSeidel) { + // Define the omega used for SOR. + ValueType omega = method == SolutionMethod::SOR ? storm::settings::nativeEquationSolverSettings().getOmega() : storm::utility::one<ValueType>(); - // Swap the two pointers as a preparation for the next iteration. - std::swap(nextX, currentX); + // To avoid copying the contents of the vector in the loop, we create a temporary x to swap with. + bool tmpXProvided = true; + std::vector<ValueType>* tmpX = multiplyResult; + if (multiplyResult == nullptr) { + tmpX = new std::vector<ValueType>(x); + tmpXProvided = false; + } else { + *tmpX = x; + } - // Now check if the process already converged within our precision. - converged = storm::utility::vector::equalModuloPrecision<ValueType>(*currentX, *nextX, static_cast<ValueType>(precision), relative); + // Set up additional environment variables. + uint_fast64_t iterationCount = 0; + bool converged = false; - // Increase iteration count so we can abort if convergence is too slow. - ++iterationCount; - } - - // If the last iteration did not write to the original x we have to swap the contents, because the - // output has to be written to the input parameter x. - if (currentX == copyX) { - std::swap(x, *currentX); - } - - // If the vector for the temporary multiplication result was not provided, we need to delete it. - if (!multiplyResultProvided) { - delete copyX; + while (!converged && iterationCount < maximalNumberOfIterations) { + A.performSuccessiveOverRelaxationStep(omega, x, b); + + // Now check if the process already converged within our precision. + converged = storm::utility::vector::equalModuloPrecision<ValueType>(x, *tmpX, static_cast<ValueType>(precision), relative); + + // If we did not yet converge, we need to copy the contents of x to *tmpX. + if (!converged) { + *tmpX = x; + } + + // Increase iteration count so we can abort if convergence is too slow. + ++iterationCount; + } + + // If the vector for the temporary multiplication result was not provided, we need to delete it. + if (!tmpXProvided) { + delete tmpX; + } + } else { + // Get a Jacobi decomposition of the matrix A. + std::pair<storm::storage::SparseMatrix<ValueType>, std::vector<ValueType>> jacobiDecomposition = A.getJacobiDecomposition(); + + // To avoid copying the contents of the vector in the loop, we create a temporary x to swap with. + bool multiplyResultProvided = true; + std::vector<ValueType>* nextX = multiplyResult; + if (nextX == nullptr) { + nextX = new std::vector<ValueType>(x.size()); + multiplyResultProvided = false; + } + std::vector<ValueType> const* copyX = nextX; + std::vector<ValueType>* currentX = &x; + + // Target vector for precision calculation. + std::vector<ValueType> tmpX(x.size()); + + // Set up additional environment variables. + uint_fast64_t iterationCount = 0; + bool converged = false; + + while (!converged && iterationCount < maximalNumberOfIterations) { + // Compute D^-1 * (b - LU * x) and store result in nextX. + jacobiDecomposition.first.multiplyWithVector(*currentX, tmpX); + storm::utility::vector::subtractVectors(b, tmpX, tmpX); + storm::utility::vector::multiplyVectorsPointwise(jacobiDecomposition.second, tmpX, *nextX); + + // Swap the two pointers as a preparation for the next iteration. + std::swap(nextX, currentX); + + // Now check if the process already converged within our precision. + converged = storm::utility::vector::equalModuloPrecision<ValueType>(*currentX, *nextX, static_cast<ValueType>(precision), relative); + + // Increase iteration count so we can abort if convergence is too slow. + ++iterationCount; + } + + // If the last iteration did not write to the original x we have to swap the contents, because the + // output has to be written to the input parameter x. + if (currentX == copyX) { + std::swap(x, *currentX); + } + + // If the vector for the temporary multiplication result was not provided, we need to delete it. + if (!multiplyResultProvided) { + delete copyX; + } } } diff --git a/src/solver/NativeLinearEquationSolver.h b/src/solver/NativeLinearEquationSolver.h index f8f5a1378..aa20b1e71 100644 --- a/src/solver/NativeLinearEquationSolver.h +++ b/src/solver/NativeLinearEquationSolver.h @@ -14,15 +14,16 @@ namespace storm { public: // An enumeration specifying the available solution methods. enum class SolutionMethod { - Jacobi + Jacobi, GaussSeidel, SOR }; /*! * Constructs a linear equation solver with parameters being set according to the settings object. * * @param A The matrix defining the coefficients of the linear equation system. + * @param method The method to use for solving linear equations. */ - NativeLinearEquationSolver(storm::storage::SparseMatrix<ValueType> const& A); + NativeLinearEquationSolver(storm::storage::SparseMatrix<ValueType> const& A, SolutionMethod method = SolutionMethod::Jacobi); /*! * Constructs a linear equation solver with the given parameters. diff --git a/src/storage/SparseMatrix.cpp b/src/storage/SparseMatrix.cpp index 2db8b576b..3dd8910dc 100644 --- a/src/storage/SparseMatrix.cpp +++ b/src/storage/SparseMatrix.cpp @@ -50,7 +50,7 @@ namespace storm { void MatrixEntry<IndexType, ValueType>::setValue(ValueType const& value) { this->entry.second = value; } - + template<typename IndexType, typename ValueType> std::pair<IndexType, ValueType> const& MatrixEntry<IndexType, ValueType>::getColumnValuePair() const { return this->entry; @@ -102,7 +102,7 @@ namespace storm { if (row < lastRow) { throw storm::exceptions::InvalidArgumentException() << "Illegal call to SparseMatrixBuilder::addNextValue: adding an element in row " << row << ", but an element in row " << lastRow << " has already been added."; } - + // Check that we did not move backwards wrt. to column. if (row == lastRow && column < lastColumn) { throw storm::exceptions::InvalidArgumentException() << "Illegal call to SparseMatrixBuilder::addNextValue: adding an element in column " << column << " in row " << row << ", but an element in column " << lastColumn << " has already been added in that row."; @@ -117,14 +117,14 @@ namespace storm { lastRow = row; } - + lastColumn = column; // Finally, set the element and increase the current size. columnsAndValues.emplace_back(column, value); highestColumn = std::max(highestColumn, column); ++currentEntryCount; - + // In case we did not expect this value, we throw an exception. if (forceInitialDimensions) { STORM_LOG_THROW(!initialRowCountSet || lastRow < initialRowCount, storm::exceptions::OutOfRangeException, "Cannot insert value at illegal row " << lastRow << "."); @@ -159,14 +159,14 @@ namespace storm { // as now the indices of row i are always between rowIndications[i] and rowIndications[i + 1], also for // the first and last row. rowIndications.push_back(currentEntryCount); - + uint_fast64_t columnCount = highestColumn + 1; if (initialColumnCountSet && forceInitialDimensions) { STORM_LOG_THROW(columnCount <= initialColumnCount, storm::exceptions::InvalidStateException, "Expected not more than " << initialColumnCount << " columns, but got " << columnCount << "."); columnCount = std::max(columnCount, initialColumnCount); } columnCount = std::max(columnCount, overriddenColumnCount); - + uint_fast64_t entryCount = currentEntryCount; if (initialEntryCountSet && forceInitialDimensions) { STORM_LOG_THROW(entryCount == initialEntryCount, storm::exceptions::InvalidStateException, "Expected " << initialEntryCount << " entries, but got " << entryCount << "."); @@ -184,12 +184,12 @@ namespace storm { rowGroupCount = std::max(rowGroupCount, initialRowGroupCount); } rowGroupCount = std::max(rowGroupCount, overriddenRowGroupCount); - + for (index_type i = currentRowGroup; i <= rowGroupCount; ++i) { rowGroupIndices.push_back(rowCount); } } - + return SparseMatrix<ValueType>(columnCount, std::move(rowIndications), std::move(columnsAndValues), std::move(rowGroupIndices), hasCustomRowGrouping); } @@ -270,14 +270,14 @@ namespace storm { template<typename ValueType> SparseMatrix<ValueType>::SparseMatrix(index_type columnCount, std::vector<index_type> const& rowIndications, std::vector<MatrixEntry<index_type, ValueType>> const& columnsAndValues, std::vector<index_type> const& rowGroupIndices, bool nontrivialRowGrouping) : rowCount(rowIndications.size() - 1), columnCount(columnCount), entryCount(columnsAndValues.size()), nonzeroEntryCount(0), columnsAndValues(columnsAndValues), rowIndications(rowIndications), nontrivialRowGrouping(nontrivialRowGrouping), rowGroupIndices(rowGroupIndices) { - this->updateNonzeroEntryCount(); + this->updateNonzeroEntryCount(); } template<typename ValueType> SparseMatrix<ValueType>::SparseMatrix(index_type columnCount, std::vector<index_type>&& rowIndications, std::vector<MatrixEntry<index_type, ValueType>>&& columnsAndValues, std::vector<index_type>&& rowGroupIndices, bool nontrivialRowGrouping) : rowCount(rowIndications.size() - 1), columnCount(columnCount), entryCount(columnsAndValues.size()), nonzeroEntryCount(0), columnsAndValues(std::move(columnsAndValues)), rowIndications(std::move(rowIndications)), nontrivialRowGrouping(nontrivialRowGrouping), rowGroupIndices(std::move(rowGroupIndices)) { - this->updateNonzeroEntryCount(); + this->updateNonzeroEntryCount(); } - + template<typename ValueType> SparseMatrix<ValueType>& SparseMatrix<ValueType>::operator=(SparseMatrix<ValueType> const& other) { // Only perform assignment if source and target are not the same. @@ -321,7 +321,7 @@ namespace storm { } bool equalityResult = true; - + equalityResult &= this->getRowCount() == other.getRowCount(); if (!equalityResult) { return false; @@ -379,35 +379,35 @@ namespace storm { return entryCount; } - template<typename T> - uint_fast64_t SparseMatrix<T>::getRowGroupEntryCount(uint_fast64_t const group) const { - uint_fast64_t result = 0; - for (uint_fast64_t row = this->getRowGroupIndices()[group]; row < this->getRowGroupIndices()[group + 1]; ++row) { - result += (this->rowIndications[row + 1] - this->rowIndications[row]); - } - return result; - } - + template<typename T> + uint_fast64_t SparseMatrix<T>::getRowGroupEntryCount(uint_fast64_t const group) const { + uint_fast64_t result = 0; + for (uint_fast64_t row = this->getRowGroupIndices()[group]; row < this->getRowGroupIndices()[group + 1]; ++row) { + result += (this->rowIndications[row + 1] - this->rowIndications[row]); + } + return result; + } + template<typename ValueType> typename SparseMatrix<ValueType>::index_type SparseMatrix<ValueType>::getNonzeroEntryCount() const { return nonzeroEntryCount; - } - - template<typename ValueType> - void SparseMatrix<ValueType>::updateNonzeroEntryCount() const { - //SparseMatrix<ValueType>* self = const_cast<SparseMatrix<ValueType>*>(this); - this->nonzeroEntryCount = 0; - for (auto const& element : *this) { - if (element.getValue() != storm::utility::zero<ValueType>()) { - ++this->nonzeroEntryCount; - } - } - } - - template<typename ValueType> - void SparseMatrix<ValueType>::updateNonzeroEntryCount(std::make_signed<index_type>::type difference) { - this->nonzeroEntryCount += difference; - } + } + + template<typename ValueType> + void SparseMatrix<ValueType>::updateNonzeroEntryCount() const { + //SparseMatrix<ValueType>* self = const_cast<SparseMatrix<ValueType>*>(this); + this->nonzeroEntryCount = 0; + for (auto const& element : *this) { + if (element.getValue() != storm::utility::zero<ValueType>()) { + ++this->nonzeroEntryCount; + } + } + } + + template<typename ValueType> + void SparseMatrix<ValueType>::updateNonzeroEntryCount(std::make_signed<index_type>::type difference) { + this->nonzeroEntryCount += difference; + } template<typename ValueType> typename SparseMatrix<ValueType>::index_type SparseMatrix<ValueType>::getRowGroupCount() const { @@ -423,7 +423,7 @@ namespace storm { std::vector<typename SparseMatrix<ValueType>::index_type> const& SparseMatrix<ValueType>::getRowGroupIndices() const { return rowGroupIndices; } - + template<typename ValueType> void SparseMatrix<ValueType>::makeRowsAbsorbing(storm::storage::BitVector const& rows) { for (auto row : rows) { @@ -457,7 +457,7 @@ namespace storm { columnValuePtr->setValue(storm::utility::one<ValueType>()); ++columnValuePtr; for (; columnValuePtr != columnValuePtrEnd; ++columnValuePtr) { - ++this->nonzeroEntryCount; + ++this->nonzeroEntryCount; columnValuePtr->setColumn(0); columnValuePtr->setValue(storm::utility::zero<ValueType>()); } @@ -510,33 +510,42 @@ namespace storm { return getSubmatrix(rowConstraint, columnConstraint, fakeRowGroupIndices, insertDiagonalElements); } } - + template<typename ValueType> SparseMatrix<ValueType> SparseMatrix<ValueType>::getSubmatrix(storm::storage::BitVector const& rowGroupConstraint, storm::storage::BitVector const& columnConstraint, std::vector<index_type> const& rowGroupIndices, bool insertDiagonalEntries) const { uint_fast64_t submatrixColumnCount = columnConstraint.getNumberOfSetBits(); // Start by creating a temporary vector that stores for each index whose bit is set to true the number of // bits that were set before that particular index. - std::vector<index_type> bitsSetBeforeIndex; - bitsSetBeforeIndex.reserve(columnCount); + std::vector<index_type> columnBitsSetBeforeIndex; + columnBitsSetBeforeIndex.reserve(columnCount); // Compute the information to fill this vector. index_type lastIndex = 0; index_type currentNumberOfSetBits = 0; - - // If we are requested to add missing diagonal entries, we need to make sure the corresponding rows are also - // taken. -// storm::storage::BitVector columnBitCountConstraint = columnConstraint; -// if (insertDiagonalEntries) { -// columnBitCountConstraint |= rowGroupConstraint; -// } for (auto index : columnConstraint) { while (lastIndex <= index) { - bitsSetBeforeIndex.push_back(currentNumberOfSetBits); + columnBitsSetBeforeIndex.push_back(currentNumberOfSetBits); ++lastIndex; } ++currentNumberOfSetBits; } + std::vector<index_type>* rowBitsSetBeforeIndex; + if (&rowGroupConstraint == &columnConstraint) { + rowBitsSetBeforeIndex = &columnBitsSetBeforeIndex; + } else { + rowBitsSetBeforeIndex = new std::vector<index_type>(rowCount); + + lastIndex = 0; + currentNumberOfSetBits = 0; + for (auto index : rowGroupConstraint) { + while (lastIndex <= index) { + rowBitsSetBeforeIndex->push_back(currentNumberOfSetBits); + ++lastIndex; + } + ++currentNumberOfSetBits; + } + } // Then, we need to determine the number of entries and the number of rows of the submatrix. index_type subEntries = 0; @@ -551,7 +560,7 @@ namespace storm { if (columnConstraint.get(it->getColumn())) { ++subEntries; - if (index == it->getColumn()) { + if (columnBitsSetBeforeIndex[it->getColumn()] == (*rowBitsSetBeforeIndex)[index]) { foundDiagonalElement = true; } } @@ -580,13 +589,13 @@ namespace storm { for (const_iterator it = this->begin(i), ite = this->end(i); it != ite; ++it) { if (columnConstraint.get(it->getColumn())) { - if (index == it->getColumn()) { + if (columnBitsSetBeforeIndex[it->getColumn()] == (*rowBitsSetBeforeIndex)[index]) { insertedDiagonalElement = true; - } else if (insertDiagonalEntries && !insertedDiagonalElement && it->getColumn() > index) { - matrixBuilder.addNextValue(rowCount, rowCount, storm::utility::zero<ValueType>()); + } else if (insertDiagonalEntries && !insertedDiagonalElement && columnBitsSetBeforeIndex[it->getColumn()] > (*rowBitsSetBeforeIndex)[index]) { + matrixBuilder.addNextValue(rowCount, rowGroupCount, storm::utility::zero<ValueType>()); insertedDiagonalElement = true; } - matrixBuilder.addNextValue(rowCount, bitsSetBeforeIndex[it->getColumn()], it->getValue()); + matrixBuilder.addNextValue(rowCount, columnBitsSetBeforeIndex[it->getColumn()], it->getValue()); } } if (insertDiagonalEntries && !insertedDiagonalElement && rowGroupCount < submatrixColumnCount) { @@ -597,6 +606,11 @@ namespace storm { ++rowGroupCount; } + // If the constraints were not the same, we have allocated some additional memory we need to free now. + if (&rowGroupConstraint != &columnConstraint) { + delete rowBitsSetBeforeIndex; + } + return matrixBuilder.build(); } @@ -655,12 +669,12 @@ namespace storm { SparseMatrix<ValueType> SparseMatrix<ValueType>::transpose(bool joinGroups, bool keepZeros) const { index_type rowCount = this->getColumnCount(); index_type columnCount = joinGroups ? this->getRowGroupCount() : this->getRowCount(); - if (keepZeros) { - index_type entryCount = this->getEntryCount(); - } else { - this->updateNonzeroEntryCount(); - index_type entryCount = this->getNonzeroEntryCount(); - } + if (keepZeros) { + index_type entryCount = this->getEntryCount(); + } else { + this->updateNonzeroEntryCount(); + index_type entryCount = this->getNonzeroEntryCount(); + } std::vector<index_type> rowIndications(rowCount + 1); std::vector<MatrixEntry<index_type, ValueType>> columnsAndValues(entryCount); @@ -668,7 +682,7 @@ namespace storm { // First, we need to count how many entries each column has. for (index_type group = 0; group < columnCount; ++group) { for (auto const& transition : joinGroups ? this->getRowGroup(group) : this->getRow(group)) { - if (transition.getValue() != storm::utility::zero<ValueType>() || keepZeros) { + if (transition.getValue() != storm::utility::zero<ValueType>() || keepZeros) { ++rowIndications[transition.getColumn() + 1]; } } @@ -687,7 +701,7 @@ namespace storm { // Now we are ready to actually fill in the values of the transposed matrix. for (index_type group = 0; group < columnCount; ++group) { for (auto const& transition : joinGroups ? this->getRowGroup(group) : this->getRow(group)) { - if (transition.getValue() != storm::utility::zero<ValueType>() || keepZeros) { + if (transition.getValue() != storm::utility::zero<ValueType>() || keepZeros) { columnsAndValues[nextIndices[transition.getColumn()]] = std::make_pair(group, transition.getValue()); nextIndices[transition.getColumn()]++; } @@ -700,10 +714,10 @@ namespace storm { } storm::storage::SparseMatrix<ValueType> transposedMatrix(columnCount, std::move(rowIndications), std::move(columnsAndValues), std::move(rowGroupIndices), false); - + return transposedMatrix; } - + template<typename ValueType> void SparseMatrix<ValueType>::convertToEquationSystem() { invertDiagonal(); @@ -713,22 +727,22 @@ namespace storm { template<typename ValueType> void SparseMatrix<ValueType>::invertDiagonal() { // Now iterate over all row groups and set the diagonal elements to the inverted value. - // If there is a row without the diagonal element, an exception is thrown. - ValueType one = storm::utility::one<ValueType>(); - ValueType zero = storm::utility::zero<ValueType>(); + // If there is a row without the diagonal element, an exception is thrown. + ValueType one = storm::utility::one<ValueType>(); + ValueType zero = storm::utility::zero<ValueType>(); bool foundDiagonalElement = false; for (index_type group = 0; group < this->getRowGroupCount(); ++group) { for (auto& entry : this->getRowGroup(group)) { if (entry.getColumn() == group) { - if (entry.getValue() == one) { - --this->nonzeroEntryCount; - entry.setValue(zero); - } else if (entry.getValue() == zero) { - ++this->nonzeroEntryCount; - entry.setValue(one); - } else { - entry.setValue(one - entry.getValue()); - } + if (entry.getValue() == one) { + --this->nonzeroEntryCount; + entry.setValue(zero); + } else if (entry.getValue() == zero) { + ++this->nonzeroEntryCount; + entry.setValue(one); + } else { + entry.setValue(one - entry.getValue()); + } foundDiagonalElement = true; } } @@ -758,7 +772,7 @@ namespace storm { for (index_type group = 0; group < this->getRowGroupCount(); ++group) { for (auto& entry : this->getRowGroup(group)) { if (entry.getColumn() == group) { - --this->nonzeroEntryCount; + --this->nonzeroEntryCount; entry.setValue(storm::utility::zero<ValueType>()); } } @@ -839,15 +853,28 @@ namespace storm { typename std::vector<ValueType>::iterator resultIterator = result.begin(); typename std::vector<ValueType>::iterator resultIteratorEnd = result.end(); - for (; resultIterator != resultIteratorEnd; ++rowIterator, ++resultIterator) { - *resultIterator = storm::utility::zero<ValueType>(); - - for (ite = this->begin() + *(rowIterator + 1); it != ite; ++it) { - *resultIterator += it->getValue() * vector[it->getColumn()]; + // If the vector to multiply with and the target vector are actually the same, we need an auxiliary variable + // to store the intermediate result. + if (&vector == &result) { + for (; resultIterator != resultIteratorEnd; ++rowIterator, ++resultIterator) { + ValueType tmpValue = storm::utility::zero<ValueType>(); + + for (ite = this->begin() + *(rowIterator + 1); it != ite; ++it) { + tmpValue += it->getValue() * vector[it->getColumn()]; + } + *resultIterator = tmpValue; + } + } else { + for (; resultIterator != resultIteratorEnd; ++rowIterator, ++resultIterator) { + *resultIterator = storm::utility::zero<ValueType>(); + + for (ite = this->begin() + *(rowIterator + 1); it != ite; ++it) { + *resultIterator += it->getValue() * vector[it->getColumn()]; + } } } } - + #ifdef STORM_HAVE_INTELTBB template<typename ValueType> void SparseMatrix<ValueType>::multiplyWithVectorParallel(std::vector<ValueType> const& vector, std::vector<ValueType>& result) const { @@ -872,7 +899,37 @@ namespace storm { }); } #endif - + + template<typename ValueType> + void SparseMatrix<ValueType>::performSuccessiveOverRelaxationStep(ValueType omega, std::vector<ValueType>& x, std::vector<ValueType> const& b) const { + const_iterator it = this->begin(); + const_iterator ite; + std::vector<index_type>::const_iterator rowIterator = rowIndications.begin(); + typename std::vector<ValueType>::const_iterator bIt = b.begin(); + typename std::vector<ValueType>::const_iterator bIte = b.end(); + typename std::vector<ValueType>::iterator resultIterator = x.begin(); + typename std::vector<ValueType>::iterator resultIteratorEnd = x.end(); + + // If the vector to multiply with and the target vector are actually the same, we need an auxiliary variable + // to store the intermediate result. + index_type currentRow = 0; + for (; resultIterator != resultIteratorEnd; ++rowIterator, ++resultIterator, ++bIt) { + ValueType tmpValue = storm::utility::zero<ValueType>(); + ValueType diagonalElement = storm::utility::zero<ValueType>(); + + for (ite = this->begin() + *(rowIterator + 1); it != ite; ++it) { + if (it->getColumn() != currentRow) { + tmpValue += it->getValue() * x[it->getColumn()]; + } else { + diagonalElement += it->getValue(); + } + } + + *resultIterator = ((storm::utility::one<ValueType>() - omega) * *resultIterator) + (omega / diagonalElement) * (*bIt - tmpValue); + ++currentRow; + } + } + template<typename ValueType> void SparseMatrix<ValueType>::multiplyVectorWithMatrix(std::vector<value_type> const& vector, std::vector<value_type>& result) const { const_iterator it = this->begin(); @@ -982,7 +1039,7 @@ namespace storm { if (this->getRowCount() != matrix.getRowCount()) return false; if (this->getColumnCount() != matrix.getColumnCount()) return false; if (this->getRowGroupIndices() != matrix.getRowGroupIndices()) return false; - + // Check the subset property for all rows individually. for (index_type row = 0; row < this->getRowCount(); ++row) { for (const_iterator it1 = this->begin(row), ite1 = this->end(row), it2 = matrix.begin(row), ite2 = matrix.end(row); it1 != ite1; ++it1) { @@ -1054,21 +1111,21 @@ namespace storm { } // Explicitly instantiate the entry, builder and the matrix. - //double - template class MatrixEntry<typename SparseMatrix<double>::index_type, double>; - template std::ostream& operator<<(std::ostream& out, MatrixEntry<uint_fast64_t, double> const& entry); - template class SparseMatrixBuilder<double>; - template class SparseMatrix<double>; - template std::ostream& operator<<(std::ostream& out, SparseMatrix<double> const& matrix); - //float - template class MatrixEntry<typename SparseMatrix<float>::index_type, float>; - template std::ostream& operator<<(std::ostream& out, MatrixEntry<uint_fast64_t, float> const& entry); - template class SparseMatrixBuilder<float>; - template class SparseMatrix<float>; - template std::ostream& operator<<(std::ostream& out, SparseMatrix<float> const& matrix); - //int - template class MatrixEntry<typename SparseMatrix<int>::index_type, int>; - template std::ostream& operator<<(std::ostream& out, MatrixEntry<uint_fast64_t, int> const& entry); + //double + template class MatrixEntry<typename SparseMatrix<double>::index_type, double>; + template std::ostream& operator<<(std::ostream& out, MatrixEntry<uint_fast64_t, double> const& entry); + template class SparseMatrixBuilder<double>; + template class SparseMatrix<double>; + template std::ostream& operator<<(std::ostream& out, SparseMatrix<double> const& matrix); + //float + template class MatrixEntry<typename SparseMatrix<float>::index_type, float>; + template std::ostream& operator<<(std::ostream& out, MatrixEntry<uint_fast64_t, float> const& entry); + template class SparseMatrixBuilder<float>; + template class SparseMatrix<float>; + template std::ostream& operator<<(std::ostream& out, SparseMatrix<float> const& matrix); + //int + template class MatrixEntry<typename SparseMatrix<int>::index_type, int>; + template std::ostream& operator<<(std::ostream& out, MatrixEntry<uint_fast64_t, int> const& entry); template class SparseMatrixBuilder<int>; template class SparseMatrix<int>; template std::ostream& operator<<(std::ostream& out, SparseMatrix<int> const& matrix); diff --git a/src/storage/SparseMatrix.h b/src/storage/SparseMatrix.h index 33863da48..df682d756 100644 --- a/src/storage/SparseMatrix.h +++ b/src/storage/SparseMatrix.h @@ -692,6 +692,15 @@ namespace storm { */ void multiplyVectorWithMatrix(std::vector<value_type> const& vector, std::vector<value_type>& result) const; + /*! + * Performs one step of the successive over-relaxation technique. + * + * @param omega The Omega parameter for SOR. + * @param x The current solution vector. The result will be written to the very same vector. + * @param b The 'right-hand side' of the problem. + */ + void performSuccessiveOverRelaxationStep(ValueType omega, std::vector<ValueType>& x, std::vector<ValueType> const& b) const; + /*! * Multiplies the matrix with the given vector in a sequential way and writes the result to the given result * vector. diff --git a/src/utility/solver.cpp b/src/utility/solver.cpp index 2be080beb..785f1d00c 100644 --- a/src/utility/solver.cpp +++ b/src/utility/solver.cpp @@ -41,9 +41,28 @@ namespace storm { return std::unique_ptr<storm::solver::LinearEquationSolver<ValueType>>(new storm::solver::GmmxxLinearEquationSolver<ValueType>(matrix)); } + template<typename ValueType> + NativeLinearEquationSolverFactory<ValueType>::NativeLinearEquationSolverFactory() { + switch (storm::settings::nativeEquationSolverSettings().getLinearEquationSystemMethod()) { + case settings::modules::NativeEquationSolverSettings::LinearEquationMethod::Jacobi: + this->method = storm::solver::NativeLinearEquationSolver<ValueType>::SolutionMethod::Jacobi; + break; + case settings::modules::NativeEquationSolverSettings::LinearEquationMethod::GaussSeidel: + this->method = storm::solver::NativeLinearEquationSolver<ValueType>::SolutionMethod::GaussSeidel; + case settings::modules::NativeEquationSolverSettings::LinearEquationMethod::SOR: + this->method = storm::solver::NativeLinearEquationSolver<ValueType>::SolutionMethod::SOR; + } + omega = storm::settings::nativeEquationSolverSettings().getOmega(); + } + + template<typename ValueType> + NativeLinearEquationSolverFactory<ValueType>::NativeLinearEquationSolverFactory(typename storm::solver::NativeLinearEquationSolver<ValueType>::SolutionMethod method, ValueType omega) : method(method), omega(omega) { + // Intentionally left empty. + } + template<typename ValueType> std::unique_ptr<storm::solver::LinearEquationSolver<ValueType>> NativeLinearEquationSolverFactory<ValueType>::create(storm::storage::SparseMatrix<ValueType> const& matrix) const { - return std::unique_ptr<storm::solver::LinearEquationSolver<ValueType>>(new storm::solver::NativeLinearEquationSolver<ValueType>(matrix)); + return std::unique_ptr<storm::solver::LinearEquationSolver<ValueType>>(new storm::solver::NativeLinearEquationSolver<ValueType>(matrix, method)); } template<typename ValueType> diff --git a/src/utility/solver.h b/src/utility/solver.h index 38cc7fbc6..cd56695f0 100644 --- a/src/utility/solver.h +++ b/src/utility/solver.h @@ -5,10 +5,12 @@ #include "src/solver/SymbolicLinearEquationSolver.h" #include "src/solver/LinearEquationSolver.h" +#include "src/solver/NativeLinearEquationSolver.h" #include "src/solver/MinMaxLinearEquationSolver.h" #include "src/solver/LpSolver.h" #include "src/storage/dd/DdType.h" +#include "src/settings/modules/NativeEquationSolverSettings.h" #include "src/exceptions/InvalidSettingsException.h" @@ -42,7 +44,14 @@ namespace storm { template<typename ValueType> class NativeLinearEquationSolverFactory : public LinearEquationSolverFactory<ValueType> { public: + NativeLinearEquationSolverFactory(); + NativeLinearEquationSolverFactory(typename storm::solver::NativeLinearEquationSolver<ValueType>::SolutionMethod method, ValueType omega); + virtual std::unique_ptr<storm::solver::LinearEquationSolver<ValueType>> create(storm::storage::SparseMatrix<ValueType> const& matrix) const override; + + private: + typename storm::solver::NativeLinearEquationSolver<ValueType>::SolutionMethod method; + ValueType omega; }; template<typename ValueType> diff --git a/test/functional/modelchecker/NativeDtmcPrctlModelCheckerTest.cpp b/test/functional/modelchecker/NativeDtmcPrctlModelCheckerTest.cpp index 387f54ad4..f58b4259e 100644 --- a/test/functional/modelchecker/NativeDtmcPrctlModelCheckerTest.cpp +++ b/test/functional/modelchecker/NativeDtmcPrctlModelCheckerTest.cpp @@ -1,6 +1,7 @@ #include "gtest/gtest.h" #include "storm-config.h" +#include "src/settings/SettingMemento.h" #include "src/logic/Formulas.h" #include "src/utility/solver.h" #include "src/modelchecker/prctl/SparseDtmcPrctlModelChecker.h" @@ -144,7 +145,7 @@ TEST(SparseDtmcPrctlModelCheckerTest, LRASingleBscc) { dtmc.reset(new storm::models::sparse::Dtmc<double>(transitionMatrix, ap, boost::none, boost::none, boost::none)); - storm::modelchecker::SparseDtmcPrctlModelChecker<double> checker(*dtmc, std::unique_ptr<storm::utility::solver::LinearEquationSolverFactory<double>>(new storm::utility::solver::NativeLinearEquationSolverFactory<double>())); + storm::modelchecker::SparseDtmcPrctlModelChecker<double> checker(*dtmc, std::unique_ptr<storm::utility::solver::LinearEquationSolverFactory<double>>(new storm::utility::solver::NativeLinearEquationSolverFactory<double>(storm::solver::NativeLinearEquationSolver<double>::SolutionMethod::SOR, 0.9))); auto labelFormula = std::make_shared<storm::logic::AtomicLabelFormula>("a"); auto lraFormula = std::make_shared<storm::logic::LongRunAverageOperatorFormula>(labelFormula); @@ -169,7 +170,7 @@ TEST(SparseDtmcPrctlModelCheckerTest, LRASingleBscc) { dtmc.reset(new storm::models::sparse::Dtmc<double>(transitionMatrix, ap, boost::none, boost::none, boost::none)); - storm::modelchecker::SparseDtmcPrctlModelChecker<double> checker(*dtmc, std::unique_ptr<storm::utility::solver::LinearEquationSolverFactory<double>>(new storm::utility::solver::NativeLinearEquationSolverFactory<double>())); + storm::modelchecker::SparseDtmcPrctlModelChecker<double> checker(*dtmc, std::unique_ptr<storm::utility::solver::LinearEquationSolverFactory<double>>(new storm::utility::solver::NativeLinearEquationSolverFactory<double>(storm::solver::NativeLinearEquationSolver<double>::SolutionMethod::SOR, 0.9))); auto labelFormula = std::make_shared<storm::logic::AtomicLabelFormula>("a"); auto lraFormula = std::make_shared<storm::logic::LongRunAverageOperatorFormula>(labelFormula); @@ -194,7 +195,7 @@ TEST(SparseDtmcPrctlModelCheckerTest, LRASingleBscc) { dtmc.reset(new storm::models::sparse::Dtmc<double>(transitionMatrix, ap, boost::none, boost::none, boost::none)); - storm::modelchecker::SparseDtmcPrctlModelChecker<double> checker(*dtmc, std::unique_ptr<storm::utility::solver::LinearEquationSolverFactory<double>>(new storm::utility::solver::NativeLinearEquationSolverFactory<double>())); + storm::modelchecker::SparseDtmcPrctlModelChecker<double> checker(*dtmc, std::unique_ptr<storm::utility::solver::LinearEquationSolverFactory<double>>(new storm::utility::solver::NativeLinearEquationSolverFactory<double>(storm::solver::NativeLinearEquationSolver<double>::SolutionMethod::SOR, 0.9))); auto labelFormula = std::make_shared<storm::logic::AtomicLabelFormula>("a"); auto lraFormula = std::make_shared<storm::logic::LongRunAverageOperatorFormula>(labelFormula); @@ -255,7 +256,7 @@ TEST(SparseDtmcPrctlModelCheckerTest, LRA) { mdp.reset(new storm::models::sparse::Dtmc<double>(transitionMatrix, ap, boost::none, boost::none, boost::none)); - storm::modelchecker::SparseDtmcPrctlModelChecker<double> checker(*mdp, std::unique_ptr<storm::utility::solver::LinearEquationSolverFactory<double>>(new storm::utility::solver::NativeLinearEquationSolverFactory<double>())); + storm::modelchecker::SparseDtmcPrctlModelChecker<double> checker(*mdp, std::unique_ptr<storm::utility::solver::LinearEquationSolverFactory<double>>(new storm::utility::solver::NativeLinearEquationSolverFactory<double>(storm::solver::NativeLinearEquationSolver<double>::SolutionMethod::SOR, 0.9))); auto labelFormula = std::make_shared<storm::logic::AtomicLabelFormula>("a"); auto lraFormula = std::make_shared<storm::logic::LongRunAverageOperatorFormula>(labelFormula);