Browse Source

Merge branch 'imca'

Former-commit-id: 680ee6406d
tempestpy_adaptions
dehnert 11 years ago
parent
commit
e2cec086a3
  1. 20
      CMakeLists.txt
  2. 11
      src/counterexamples/MILPMinimalLabelSetGenerator.h
  3. 2
      src/counterexamples/PathBasedSubsystemGenerator.h
  4. 14
      src/counterexamples/SMTMinimalCommandSetGenerator.h
  5. 8
      src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.cpp
  6. 442
      src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.h
  7. 148
      src/modelchecker/prctl/SparseMdpPrctlModelChecker.h
  8. 2
      src/models/AtomicPropositionsLabeling.h
  9. 4
      src/models/Dtmc.h
  10. 8
      src/models/MarkovAutomaton.h
  11. 76
      src/solver/AbstractNondeterministicLinearEquationSolver.h
  12. 172
      src/solver/GlpkLpSolver.cpp
  13. 113
      src/solver/GlpkLpSolver.h
  14. 47
      src/solver/GmmxxNondeterministicLinearEquationSolver.h
  15. 1
      src/solver/GurobiLpSolver.cpp
  16. 4
      src/storage/BitVector.h
  17. 73
      src/storage/SparseMatrix.cpp
  18. 160
      src/storage/SparseMatrix.h
  19. 25
      src/storm.cpp
  20. 13
      src/utility/StormOptions.cpp
  21. 102
      src/utility/graph.h
  22. 36
      src/utility/solver.cpp
  23. 12
      src/utility/solver.h
  24. 600
      src/utility/vector.h
  25. 3
      storm-config.h.in
  26. 8
      test/functional/modelchecker/GmmxxMdpPrctlModelCheckerTest.cpp
  27. 8
      test/functional/modelchecker/SparseMdpPrctlModelCheckerTest.cpp
  28. 4
      test/performance/modelchecker/GmmxxMdpPrctModelCheckerTest.cpp
  29. 4
      test/performance/modelchecker/SparseMdpPrctlModelCheckerTest.cpp

20
CMakeLists.txt

@ -32,6 +32,7 @@ option(USE_INTELTBB "Sets whether the Intel TBB Extensions should be used." OFF)
option(STORM_USE_COTIRE "Sets whether Cotire should be used (for building precompiled headers)." OFF)
option(LINK_LIBCXXABI "Sets whether libc++abi should be linked." OFF)
option(USE_LIBCXX "Sets whether the standard library is libc++." OFF)
option(ENABLE_GLPK "Sets whether StoRM is built with support for glpk." OFF)
set(GUROBI_ROOT "" CACHE STRING "The root directory of Gurobi (if available).")
set(Z3_ROOT "" CACHE STRING "The root directory of Z3 (if available).")
set(ADDITIONAL_INCLUDE_DIRS "" CACHE STRING "Additional directories added to the include directories.")
@ -184,6 +185,13 @@ else()
set(STORM_CPP_GUROBI_DEF "undef")
endif()
# glpk defines
if (ENABLE_GLPK)
set(STORM_CPP_GLPK_DEF "define")
else()
set(STORM_CPP_GLPK_DEF "undef")
endif()
# Z3 Defines
if (ENABLE_Z3)
set(STORM_CPP_Z3_DEF "define")
@ -359,6 +367,18 @@ if (ENABLE_GUROBI)
target_link_libraries(storm-performance-tests "gurobi56")
endif(ENABLE_GUROBI)
#############################################################
##
## glpk (optional)
##
#############################################################
if (ENABLE_GLPK)
message (STATUS "StoRM - Linking with glpk")
target_link_libraries(storm "glpk")
target_link_libraries(storm-functional-tests "glpk")
target_link_libraries(storm-performance-tests "glpk")
endif(ENABLE_GLPK)
#############################################################
##
## Z3 (optional)

11
src/counterexamples/MILPMinimalLabelSetGenerator.h

@ -90,10 +90,9 @@ namespace storm {
*/
static struct StateInformation determineRelevantAndProblematicStates(storm::models::Mdp<T> const& labeledMdp, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates) {
StateInformation result;
storm::storage::SparseMatrix<T> backwardTransitions = labeledMdp.getBackwardTransitions();
result.relevantStates = storm::utility::graph::performProbGreater0E(labeledMdp, backwardTransitions, phiStates, psiStates);
result.relevantStates = storm::utility::graph::performProbGreater0E(labeledMdp.getTransitionMatrix(), labeledMdp.getNondeterministicChoiceIndices(), labeledMdp.getBackwardTransitions(), phiStates, psiStates);
result.relevantStates &= ~psiStates;
result.problematicStates = storm::utility::graph::performProbGreater0E(labeledMdp, backwardTransitions, phiStates, psiStates);
result.problematicStates = storm::utility::graph::performProbGreater0E(labeledMdp.getTransitionMatrix(), labeledMdp.getNondeterministicChoiceIndices(), labeledMdp.getBackwardTransitions(), phiStates, psiStates);
result.problematicStates.complement();
result.problematicStates &= result.relevantStates;
LOG4CPLUS_DEBUG(logger, "Found " << phiStates.getNumberOfSetBits() << " filter states.");
@ -961,7 +960,7 @@ namespace storm {
// (1) Check whether its possible to exceed the threshold if checkThresholdFeasible is set.
double maximalReachabilityProbability = 0;
storm::modelchecker::prctl::SparseMdpPrctlModelChecker<T> modelchecker(labeledMdp, new storm::solver::GmmxxNondeterministicLinearEquationSolver<T>());
storm::modelchecker::prctl::SparseMdpPrctlModelChecker<T> modelchecker(labeledMdp);
std::vector<T> result = modelchecker.checkUntil(false, phiStates, psiStates, false).first;
for (auto state : labeledMdp.getInitialStates()) {
maximalReachabilityProbability = std::max(maximalReachabilityProbability, result[state]);
@ -977,7 +976,7 @@ namespace storm {
ChoiceInformation choiceInformation = determineRelevantAndProblematicChoices(labeledMdp, stateInformation, psiStates);
// (4) Encode resulting system as MILP problem.
std::unique_ptr<storm::solver::LpSolver> solver = storm::utility::solver::getLpSolver("MinimalCommandSetCounterexample");
std::shared_ptr<storm::solver::LpSolver> solver = storm::utility::solver::getLpSolver("MinimalCommandSetCounterexample");
// (4.1) Create variables.
VariableInformation variableInformation = createVariables(*solver, labeledMdp, stateInformation, choiceInformation);
@ -1025,7 +1024,7 @@ namespace storm {
storm::property::prctl::AbstractPathFormula<double> const& pathFormula = probBoundFormula->getPathFormula();
storm::storage::BitVector phiStates;
storm::storage::BitVector psiStates;
storm::modelchecker::prctl::SparseMdpPrctlModelChecker<T> modelchecker(labeledMdp, new storm::solver::GmmxxNondeterministicLinearEquationSolver<T>());
storm::modelchecker::prctl::SparseMdpPrctlModelChecker<T> modelchecker(labeledMdp);
try {
storm::property::prctl::Until<double> const& untilFormula = dynamic_cast<storm::property::prctl::Until<double> const&>(pathFormula);

2
src/counterexamples/PathBasedSubsystemGenerator.h

@ -570,7 +570,7 @@ public:
storm::property::prctl::Until<T> const* until = dynamic_cast<storm::property::prctl::Until<T> const*>(pathFormulaPtr);
if(eventually != nullptr) {
targetStates = eventually->getChild().check(modelCheck);
allowedStates = storm::storage::BitVector(targetStates.getSize(), true);
allowedStates = storm::storage::BitVector(targetStates.size(), true);
}
else if(globally != nullptr){
//eventually reaching a state without property visiting only states with property

14
src/counterexamples/SMTMinimalCommandSetGenerator.h

@ -103,7 +103,7 @@ namespace storm {
// Compute all relevant states, i.e. states for which there exists a scheduler that has a non-zero
// probabilitiy of satisfying phi until psi.
storm::storage::SparseMatrix<T> backwardTransitions = labeledMdp.getBackwardTransitions();
relevancyInformation.relevantStates = storm::utility::graph::performProbGreater0E(labeledMdp, backwardTransitions, phiStates, psiStates);
relevancyInformation.relevantStates = storm::utility::graph::performProbGreater0E(labeledMdp.getTransitionMatrix(), labeledMdp.getNondeterministicChoiceIndices(), backwardTransitions, phiStates, psiStates);
relevancyInformation.relevantStates &= ~psiStates;
LOG4CPLUS_DEBUG(logger, "Found " << relevancyInformation.relevantStates.getNumberOfSetBits() << " relevant states.");
@ -902,7 +902,7 @@ namespace storm {
// Get some data from the MDP for convenient access.
storm::storage::SparseMatrix<T> const& transitionMatrix = labeledMdp.getTransitionMatrix();
std::vector<storm::storage::VectorSet<uint_fast64_t>> const& choiceLabeling = labeledMdp.getChoiceLabeling();
storm::storage::SparseMatrix<bool> backwardTransitions = labeledMdp.getBackwardTransitions();
storm::storage::SparseMatrix<T> backwardTransitions = labeledMdp.getBackwardTransitions();
// First, we add the formulas that encode
// (1) if an incoming transition is chosen, an outgoing one is chosen as well (for non-initial states)
@ -1427,7 +1427,7 @@ namespace storm {
}
storm::storage::BitVector unreachableRelevantStates = ~reachableStates & relevancyInformation.relevantStates;
storm::storage::BitVector statesThatCanReachTargetStates = storm::utility::graph::performProbGreater0E(subMdp, subMdp.getBackwardTransitions(), phiStates, psiStates);
storm::storage::BitVector statesThatCanReachTargetStates = storm::utility::graph::performProbGreater0E(subMdp.getTransitionMatrix(), subMdp.getNondeterministicChoiceIndices(), subMdp.getBackwardTransitions(), phiStates, psiStates);
std::vector<storm::storage::VectorSet<uint_fast64_t>> guaranteedLabelSets = storm::utility::counterexamples::getGuaranteedLabelSets(originalMdp, statesThatCanReachTargetStates, relevancyInformation.relevantLabels);
LOG4CPLUS_DEBUG(logger, "Found " << reachableLabels.size() << " reachable labels and " << reachableStates.getNumberOfSetBits() << " reachable states.");
@ -1547,7 +1547,7 @@ namespace storm {
LOG4CPLUS_DEBUG(logger, "Successfully determined reachable state space.");
storm::storage::BitVector unreachableRelevantStates = ~reachableStates & relevancyInformation.relevantStates;
storm::storage::BitVector statesThatCanReachTargetStates = storm::utility::graph::performProbGreater0E(subMdp, subMdp.getBackwardTransitions(), phiStates, psiStates);
storm::storage::BitVector statesThatCanReachTargetStates = storm::utility::graph::performProbGreater0E(subMdp.getTransitionMatrix(), subMdp.getNondeterministicChoiceIndices(), subMdp.getBackwardTransitions(), phiStates, psiStates);
std::vector<storm::storage::VectorSet<uint_fast64_t>> guaranteedLabelSets = storm::utility::counterexamples::getGuaranteedLabelSets(originalMdp, statesThatCanReachTargetStates, relevancyInformation.relevantLabels);
// Search for states for which we could enable another option and possibly improve the reachability probability.
@ -1635,7 +1635,7 @@ namespace storm {
// (1) Check whether its possible to exceed the threshold if checkThresholdFeasible is set.
double maximalReachabilityProbability = 0;
storm::modelchecker::prctl::SparseMdpPrctlModelChecker<T> modelchecker(labeledMdp, new storm::solver::GmmxxNondeterministicLinearEquationSolver<T>());
storm::modelchecker::prctl::SparseMdpPrctlModelChecker<T> modelchecker(labeledMdp);
std::vector<T> result = modelchecker.checkUntil(false, phiStates, psiStates, false).first;
for (auto state : labeledMdp.getInitialStates()) {
maximalReachabilityProbability = std::max(maximalReachabilityProbability, result[state]);
@ -1701,7 +1701,7 @@ namespace storm {
modelCheckingClock = std::chrono::high_resolution_clock::now();
commandSet.insert(relevancyInformation.knownLabels);
storm::models::Mdp<T> subMdp = labeledMdp.restrictChoiceLabels(commandSet);
storm::modelchecker::prctl::SparseMdpPrctlModelChecker<T> modelchecker(subMdp, new storm::solver::GmmxxNondeterministicLinearEquationSolver<T>());
storm::modelchecker::prctl::SparseMdpPrctlModelChecker<T> modelchecker(subMdp);
LOG4CPLUS_DEBUG(logger, "Invoking model checker.");
std::vector<T> result = modelchecker.checkUntil(false, phiStates, psiStates, false).first;
LOG4CPLUS_DEBUG(logger, "Computed model checking results.");
@ -1786,7 +1786,7 @@ namespace storm {
storm::property::prctl::AbstractPathFormula<double> const& pathFormula = probBoundFormula->getPathFormula();
storm::storage::BitVector phiStates;
storm::storage::BitVector psiStates;
storm::modelchecker::prctl::SparseMdpPrctlModelChecker<T> modelchecker(labeledMdp, new storm::solver::GmmxxNondeterministicLinearEquationSolver<T>());
storm::modelchecker::prctl::SparseMdpPrctlModelChecker<T> modelchecker(labeledMdp);
try {
storm::property::prctl::Until<double> const& untilFormula = dynamic_cast<storm::property::prctl::Until<double> const&>(pathFormula);

8
src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.cpp

@ -0,0 +1,8 @@
#include "src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.h"
bool SparseMarkovAutomatonCslModelCheckerOptionsRegistered = storm::settings::Settings::registerNewModule([] (storm::settings::Settings* instance) -> bool {
instance->addOption(storm::settings::OptionBuilder("GmmxxLinearEquationSolver", "digiprecision", "", "Precision used for iterative solving of linear equation systems").addArgument(storm::settings::ArgumentBuilder::createDoubleArgument("precision value", "Precision").setDefaultValueDouble(1e-4).addValidationFunctionDouble(storm::settings::ArgumentValidators::doubleRangeValidatorExcluding(0.0, 1.0)).build()).build());
return true;
});

442
src/modelchecker/csl/SparseMarkovAutomatonCslModelChecker.h

@ -4,6 +4,7 @@
#include <stack>
#include "src/modelchecker/csl/AbstractModelChecker.h"
#include "src/modelchecker/prctl/SparseMdpPrctlModelChecker.h"
#include "src/models/MarkovAutomaton.h"
#include "src/storage/BitVector.h"
#include "src/storage/MaximalEndComponentDecomposition.h"
@ -20,8 +21,7 @@ namespace storm {
template<typename ValueType>
class SparseMarkovAutomatonCslModelChecker : public AbstractModelChecker<ValueType> {
public:
explicit SparseMarkovAutomatonCslModelChecker(storm::models::MarkovAutomaton<ValueType> const& model, storm::solver::AbstractNondeterministicLinearEquationSolver<ValueType>* linearEquationSolver) : AbstractModelChecker<ValueType>(model), minimumOperatorStack(), linearEquationSolver(linearEquationSolver) {
explicit SparseMarkovAutomatonCslModelChecker(storm::models::MarkovAutomaton<ValueType> const& model, std::shared_ptr<storm::solver::AbstractNondeterministicLinearEquationSolver<ValueType>> nondeterministicLinearEquationSolver = storm::utility::solver::getNondeterministicLinearEquationSolver<ValueType>()) : AbstractModelChecker<ValueType>(model), minimumOperatorStack(), nondeterministicLinearEquationSolver(nondeterministicLinearEquationSolver) {
// Intentionally left empty.
}
@ -34,7 +34,13 @@ namespace storm {
}
std::vector<ValueType> checkUntil(storm::property::csl::Until<ValueType> const& formula, bool qualitative) const {
throw storm::exceptions::NotImplementedException() << "Model checking Until formulas on Markov automata is not yet implemented.";
storm::storage::BitVector leftStates = formula.getLeft().check(*this);
storm::storage::BitVector rightStates = formula.getRight().check(*this);
return computeUnboundedUntilProbabilities(minimumOperatorStack.top(), leftStates, rightStates, qualitative).first;
}
std::pair<std::vector<ValueType>, storm::storage::TotalScheduler> computeUnboundedUntilProbabilities(bool min, storm::storage::BitVector const& leftStates, storm::storage::BitVector const& rightStates, bool qualitative) const {
return storm::modelchecker::prctl::SparseMdpPrctlModelChecker<ValueType>::computeUnboundedUntilProbabilities(min, this->getModel().getTransitionMatrix(), this->getModel().getNondeterministicChoiceIndices(), this->getModel().getBackwardTransitions(), this->getModel().getInitialStates(), leftStates, rightStates, nondeterministicLinearEquationSolver, qualitative);
}
std::vector<ValueType> checkTimeBoundedUntil(storm::property::csl::TimeBoundedUntil<ValueType> const& formula, bool qualitative) const {
@ -42,7 +48,8 @@ namespace storm {
}
std::vector<ValueType> checkTimeBoundedEventually(storm::property::csl::TimeBoundedEventually<ValueType> const& formula, bool qualitative) const {
throw storm::exceptions::NotImplementedException() << "Model checking time-bounded Until formulas on Markov automata is not yet implemented.";
storm::storage::BitVector goalStates = formula.getChild().check(*this);
return this->checkTimeBoundedEventually(this->minimumOperatorStack.top(), goalStates, formula.getLowerBound(), formula.getUpperBound());
}
std::vector<ValueType> checkGlobally(storm::property::csl::Globally<ValueType> const& formula, bool qualitative) const {
@ -50,7 +57,8 @@ namespace storm {
}
std::vector<ValueType> checkEventually(storm::property::csl::Eventually<ValueType> const& formula, bool qualitative) const {
throw storm::exceptions::NotImplementedException() << "Model checking Eventually formulas on Markov automata is not yet implemented.";
storm::storage::BitVector subFormulaStates = formula.getChild().check(*this);
return computeUnboundedUntilProbabilities(minimumOperatorStack.top(), storm::storage::BitVector(this->getModel().getNumberOfStates(), true), subFormulaStates, qualitative).first;
}
std::vector<ValueType> checkNext(storm::property::csl::Next<ValueType> const& formula, bool qualitative) const {
@ -69,44 +77,182 @@ namespace storm {
return result;
}
std::vector<ValueType> checkTimeBoundedEventually(bool min, storm::storage::BitVector const& goalStates) const {
if (!this->getModel().isClosed()) {
throw storm::exceptions::InvalidArgumentException() << "Unable to compute time-bounded reachability on non-closed Markov automaton.";
static void computeBoundedReachability(bool min, storm::storage::SparseMatrix<ValueType> const& transitionMatrix, std::vector<uint_fast64_t> const& nondeterministicChoiceIndices, std::vector<ValueType> const& exitRates, storm::storage::BitVector const& markovianStates, storm::storage::BitVector const& goalStates, storm::storage::BitVector const& markovianNonGoalStates, storm::storage::BitVector const& probabilisticNonGoalStates, std::vector<ValueType>& markovianNonGoalValues, std::vector<ValueType>& probabilisticNonGoalValues, ValueType delta, uint_fast64_t numberOfSteps) {
// Start by computing four sparse matrices:
// * a matrix aMarkovian with all (discretized) transitions from Markovian non-goal states to all Markovian non-goal states.
// * a matrix aMarkovianToProbabilistic with all (discretized) transitions from Markovian non-goal states to all probabilistic non-goal states.
// * a matrix aProbabilistic with all (non-discretized) transitions from probabilistic non-goal states to other probabilistic non-goal states.
// * a matrix aProbabilisticToMarkovian with all (non-discretized) transitions from probabilistic non-goal states to all Markovian non-goal states.
typename storm::storage::SparseMatrix<ValueType> aMarkovian = transitionMatrix.getSubmatrix(markovianNonGoalStates, nondeterministicChoiceIndices, true);
typename storm::storage::SparseMatrix<ValueType> aMarkovianToProbabilistic = transitionMatrix.getSubmatrix(markovianNonGoalStates, probabilisticNonGoalStates, nondeterministicChoiceIndices);
std::vector<uint_fast64_t> markovianNondeterministicChoiceIndices = storm::utility::vector::getConstrainedOffsetVector(nondeterministicChoiceIndices, markovianNonGoalStates);
typename storm::storage::SparseMatrix<ValueType> aProbabilistic = transitionMatrix.getSubmatrix(probabilisticNonGoalStates, nondeterministicChoiceIndices);
typename storm::storage::SparseMatrix<ValueType> aProbabilisticToMarkovian = transitionMatrix.getSubmatrix(probabilisticNonGoalStates, markovianNonGoalStates, nondeterministicChoiceIndices);
std::vector<uint_fast64_t> probabilisticNondeterministicChoiceIndices = storm::utility::vector::getConstrainedOffsetVector(nondeterministicChoiceIndices, probabilisticNonGoalStates);
// The matrices with transitions from Markovian states need to be digitized.
// Digitize aMarkovian. Based on whether the transition is a self-loop or not, we apply the two digitization rules.
uint_fast64_t rowIndex = 0;
for (auto state : markovianNonGoalStates) {
typename storm::storage::SparseMatrix<ValueType>::MutableRows row = aMarkovian.getMutableRow(rowIndex);
for (auto element : row) {
ValueType eTerm = std::exp(-exitRates[state] * delta);
if (element.column() == rowIndex) {
element.value() = (storm::utility::constGetOne<ValueType>() - eTerm) * element.value() + eTerm;
} else {
element.value() = (storm::utility::constGetOne<ValueType>() - eTerm) * element.value();
}
}
++rowIndex;
}
// (1) Compute the number of steps we need to take.
// Digitize aMarkovianToProbabilistic. As there are no self-loops in this case, we only need to apply the digitization formula for regular successors.
rowIndex = 0;
for (auto state : markovianNonGoalStates) {
typename storm::storage::SparseMatrix<ValueType>::MutableRows row = aMarkovianToProbabilistic.getMutableRow(rowIndex);
for (auto element : row) {
element.value() = (1 - std::exp(-exitRates[state] * delta)) * element.value();
}
++rowIndex;
}
// (2) Compute four sparse matrices:
// * a matrix A_MSwG with all (discretized!) transitions from Markovian non-goal states to *all other* non-goal states. Note: this matrix has more columns than rows.
// * a matrix A_PSwg with all (non-discretized) transitions from probabilistic non-goal states to other probabilistic non-goal states. This matrix has more rows than columns.
// * a matrix A_PStoMS with all (non-discretized) transitions from probabilistic non-goal states to all Markovian non-goal states. This matrix may have any shape.
// Initialize the two vectors that hold the variable one-step probabilities to all target states for probabilistic and Markovian (non-goal) states.
std::vector<ValueType> bProbabilistic(aProbabilistic.getRowCount());
std::vector<ValueType> bMarkovian(markovianNonGoalStates.getNumberOfSetBits());
// (3) Initialize three used vectors:
// * v_PS holds the probability values of probabilistic non-goal states.
// * v_MS holds the probability values of Markovian non-goal states.
// * v_all holds the probability values of all non-goal states. This vector is needed for the Markov step.
// Compute the two fixed right-hand side vectors, one for Markovian states and one for the probabilistic ones.
std::vector<ValueType> bProbabilisticFixed = transitionMatrix.getConstrainedRowSumVector(probabilisticNonGoalStates, nondeterministicChoiceIndices, goalStates, aProbabilistic.getRowCount());
std::vector<ValueType> bMarkovianFixed;
bMarkovianFixed.reserve(markovianNonGoalStates.getNumberOfSetBits());
for (auto state : markovianNonGoalStates) {
bMarkovianFixed.push_back(storm::utility::constGetZero<ValueType>());
typename storm::storage::SparseMatrix<ValueType>::Rows row = transitionMatrix.getRow(nondeterministicChoiceIndices[state]);
for (auto element : row) {
if (goalStates.get(element.column())) {
bMarkovianFixed.back() += (1 - std::exp(-exitRates[state] * delta)) * element.value();
}
}
}
// (3) Perform value iteration
// * initialize the vectors v_PS, v_MS and v_all.
std::shared_ptr<storm::solver::AbstractNondeterministicLinearEquationSolver<ValueType>> nondeterministiclinearEquationSolver = storm::utility::solver::getNondeterministicLinearEquationSolver<ValueType>();
// Perform the actual value iteration
// * loop until the step bound has been reached
// * in the loop:
// * perform value iteration using A_PSwG, v_PS and the vector x where x = x_PS + x_MS, x_PS = (A * 1_G)|PS with x_MS = A_PStoMS * v_MS
// * perform value iteration using A_PSwG, v_PS and the vector b where b = (A * 1_G)|PS + A_PStoMS * v_MS
// and 1_G being the characteristic vector for all goal states.
// * copy the values from v_PS to the correct positions into v_all
// * perform one timed-step using A_MSwG, v_all and x_MS = (A * 1_G)|MS and obtain v_MS
// * copy the values from v_MS to the correct positions into v_all
//
// * perform one timed-step using v_MS := A_MSwG * v_MS + A_MStoPS * v_PS + (A * 1_G)|MS
std::vector<ValueType> markovianNonGoalValuesSwap(markovianNonGoalValues);
std::vector<ValueType> multiplicationResultScratchMemory(aProbabilistic.getRowCount());
std::vector<ValueType> aProbabilisticScratchMemory(probabilisticNonGoalValues.size());
for (uint_fast64_t currentStep = 0; currentStep < numberOfSteps; ++currentStep) {
// Start by (re-)computing bProbabilistic = bProbabilisticFixed + aProbabilisticToMarkovian * vMarkovian.
aProbabilisticToMarkovian.multiplyWithVector(markovianNonGoalValues, bProbabilistic);
storm::utility::vector::addVectorsInPlace(bProbabilistic, bProbabilisticFixed);
// Now perform the inner value iteration for probabilistic states.
nondeterministiclinearEquationSolver->solveEquationSystem(min, aProbabilistic, probabilisticNonGoalValues, bProbabilistic, probabilisticNondeterministicChoiceIndices, &multiplicationResultScratchMemory, &aProbabilisticScratchMemory);
// (Re-)compute bMarkovian = bMarkovianFixed + aMarkovianToProbabilistic * vProbabilistic.
aMarkovianToProbabilistic.multiplyWithVector(probabilisticNonGoalValues, bMarkovian);
storm::utility::vector::addVectorsInPlace(bMarkovian, bMarkovianFixed);
aMarkovian.multiplyWithVector(markovianNonGoalValues, markovianNonGoalValuesSwap);
std::swap(markovianNonGoalValues, markovianNonGoalValuesSwap);
storm::utility::vector::addVectorsInPlace(markovianNonGoalValues, bMarkovian);
}
// After the loop, perform one more step of the value iteration for PS states.
// Finally, create the result vector out of 1_G and v_all.
aProbabilisticToMarkovian.multiplyWithVector(probabilisticNonGoalValues, bProbabilistic);
storm::utility::vector::addVectorsInPlace(bProbabilistic, bProbabilisticFixed);
nondeterministiclinearEquationSolver->solveEquationSystem(min, aProbabilistic, probabilisticNonGoalValues, bProbabilistic, probabilisticNondeterministicChoiceIndices);
}
std::vector<ValueType> checkTimeBoundedEventually(bool min, storm::storage::BitVector const& goalStates, ValueType lowerBound, ValueType upperBound) const {
// Check whether the automaton is closed.
if (!this->getModel().isClosed()) {
throw storm::exceptions::InvalidArgumentException() << "Unable to compute time-bounded reachability on non-closed Markov automaton.";
}
// Return dummy vector for the time being.
return std::vector<ValueType>();
// Check whether the given bounds were valid.
if (lowerBound < storm::utility::constGetZero<ValueType>() || upperBound < storm::utility::constGetZero<ValueType>() || upperBound < lowerBound) {
throw storm::exceptions::InvalidArgumentException() << "Illegal interval [";
}
// Get some data fields for convenient access.
std::vector<uint_fast64_t> const& nondeterministicChoiceIndices = this->getModel().getNondeterministicChoiceIndices();
typename storm::storage::SparseMatrix<ValueType> const& transitionMatrix = this->getModel().getTransitionMatrix();
std::vector<ValueType> const& exitRates = this->getModel().getExitRates();
storm::storage::BitVector const& markovianStates = this->getModel().getMarkovianStates();
// (1) Compute the accuracy we need to achieve the required error bound.
ValueType maxExitRate = this->getModel().getMaximalExitRate();
ValueType delta = (2 * storm::settings::Settings::getInstance()->getOptionByLongName("digiprecision").getArgument(0).getValueAsDouble()) / (upperBound * maxExitRate * maxExitRate);
// (2) Compute the number of steps we need to make for the interval.
uint_fast64_t numberOfSteps = static_cast<uint_fast64_t>(std::ceil((upperBound - lowerBound) / delta));
std::cout << "Performing " << numberOfSteps << " iterations (delta=" << delta << ") for interval [" << lowerBound << ", " << upperBound << "]." << std::endl;
// (3) Compute the non-goal states and initialize two vectors
// * vProbabilistic holds the probability values of probabilistic non-goal states.
// * vMarkovian holds the probability values of Markovian non-goal states.
storm::storage::BitVector const& markovianNonGoalStates = markovianStates & ~goalStates;
storm::storage::BitVector const& probabilisticNonGoalStates = ~markovianStates & ~goalStates;
std::vector<ValueType> vProbabilistic(probabilisticNonGoalStates.getNumberOfSetBits());
std::vector<ValueType> vMarkovian(markovianNonGoalStates.getNumberOfSetBits());
computeBoundedReachability(min, transitionMatrix, nondeterministicChoiceIndices, exitRates, markovianStates, goalStates, markovianNonGoalStates, probabilisticNonGoalStates, vMarkovian, vProbabilistic, delta, numberOfSteps);
// (4) If the lower bound of interval was non-zero, we need to take the current values as the starting values for a subsequent value iteration.
if (lowerBound != storm::utility::constGetZero<ValueType>()) {
std::vector<ValueType> vAllProbabilistic((~markovianStates).getNumberOfSetBits());
std::vector<ValueType> vAllMarkovian(markovianStates.getNumberOfSetBits());
// Create the starting value vectors for the next value iteration based on the results of the previous one.
storm::utility::vector::setVectorValues<ValueType>(vAllProbabilistic, ~markovianStates % goalStates, storm::utility::constGetOne<ValueType>());
storm::utility::vector::setVectorValues<ValueType>(vAllProbabilistic, ~markovianStates % ~goalStates, vProbabilistic);
storm::utility::vector::setVectorValues<ValueType>(vAllMarkovian, markovianStates % goalStates, storm::utility::constGetOne<ValueType>());
storm::utility::vector::setVectorValues<ValueType>(vAllMarkovian, markovianStates % ~goalStates, vMarkovian);
// Compute the number of steps to reach the target interval.
numberOfSteps = static_cast<uint_fast64_t>(std::ceil(lowerBound / delta));
std::cout << "Performing " << numberOfSteps << " iterations (delta=" << delta << ") for interval [0, " << lowerBound << "]." << std::endl;
// FIXME: According to IMCA, the value of delta needs to be recomputed here, but using the equations from the source this doesn't make sense, because they always evaluate to the original value of delta.
// Compute the bounded reachability for interval [0, b-a].
computeBoundedReachability(min, transitionMatrix, nondeterministicChoiceIndices, exitRates, markovianStates, storm::storage::BitVector(this->getModel().getNumberOfStates()), markovianStates, ~markovianStates, vAllMarkovian, vAllProbabilistic, delta, numberOfSteps);
// Create the result vector out of vAllProbabilistic and vAllMarkovian and return it.
std::vector<ValueType> result(this->getModel().getNumberOfStates());
storm::utility::vector::setVectorValues(result, ~markovianStates, vAllProbabilistic);
storm::utility::vector::setVectorValues(result, markovianStates, vAllMarkovian);
return result;
} else {
// Create the result vector out of 1_G, vProbabilistic and vMarkovian and return it.
std::vector<ValueType> result(this->getModel().getNumberOfStates());
storm::utility::vector::setVectorValues<ValueType>(result, goalStates, storm::utility::constGetOne<ValueType>());
storm::utility::vector::setVectorValues(result, probabilisticNonGoalStates, vProbabilistic);
storm::utility::vector::setVectorValues(result, markovianNonGoalStates, vMarkovian);
return result;
}
}
std::vector<ValueType> checkLongRunAverage(bool min, storm::storage::BitVector const& goalStates) const {
// Check whether the automaton is closed.
if (!this->getModel().isClosed()) {
throw storm::exceptions::InvalidArgumentException() << "Unable to compute long-run average on non-closed Markov automaton.";
}
// If there are no goal states, we avoid the computation and directly return zero.
if (goalStates.empty()) {
return std::vector<ValueType>(this->getModel().getNumberOfStates(), storm::utility::constGetZero<ValueType>());
}
// Likewise, if all bits are set, we can avoid the computation and set.
if ((~goalStates).empty()) {
return std::vector<ValueType>(this->getModel().getNumberOfStates(), storm::utility::constGetOne<ValueType>());
}
// Start by decomposing the Markov automaton into its MECs.
storm::storage::MaximalEndComponentDecomposition<double> mecDecomposition(this->getModel());
@ -115,7 +261,7 @@ namespace storm {
typename storm::storage::SparseMatrix<ValueType> const& transitionMatrix = this->getModel().getTransitionMatrix();
std::vector<uint_fast64_t> const& nondeterministicChoiceIndices = this->getModel().getNondeterministicChoiceIndices();
// Now compute the long-run average for all end components in isolation.
// Now start with compute the long-run average for all end components in isolation.
std::vector<ValueType> lraValuesForEndComponents;
// While doing so, we already gather some information for the following steps.
@ -125,69 +271,18 @@ namespace storm {
for (uint_fast64_t currentMecIndex = 0; currentMecIndex < mecDecomposition.size(); ++currentMecIndex) {
storm::storage::MaximalEndComponent const& mec = mecDecomposition[currentMecIndex];
std::unique_ptr<storm::solver::LpSolver> solver = storm::utility::solver::getLpSolver("LRA for MEC " + std::to_string(currentMecIndex));
solver->setModelSense(min ? storm::solver::LpSolver::MAXIMIZE : storm::solver::LpSolver::MINIMIZE);
// First, we need to create the variables for the problem.
std::map<uint_fast64_t, uint_fast64_t> stateToVariableIndexMap;
for (auto const& stateChoicesPair : mec) {
stateToVariableIndexMap[stateChoicesPair.first] = solver->createContinuousVariable("x" + std::to_string(stateChoicesPair.first), storm::solver::LpSolver::UNBOUNDED, 0, 0, 0);
}
uint_fast64_t lraValueVariableIndex = solver->createContinuousVariable("k", storm::solver::LpSolver::UNBOUNDED, 0, 0, 1);
// Now we encode the problem as constraints.
std::vector<uint_fast64_t> variables;
std::vector<double> coefficients;
// Gather information for later use.
for (auto const& stateChoicesPair : mec) {
uint_fast64_t state = stateChoicesPair.first;
// Gather information for later use.
statesInMecs.set(state);
stateToMecIndexMap[state] = currentMecIndex;
// Now, based on the type of the state, create a suitable constraint.
if (this->getModel().isMarkovianState(state)) {
variables.clear();
coefficients.clear();
variables.push_back(stateToVariableIndexMap.at(state));
coefficients.push_back(1);
typename storm::storage::SparseMatrix<ValueType>::Rows row = transitionMatrix.getRow(nondeterministicChoiceIndices[state]);
for (auto element : row) {
variables.push_back(stateToVariableIndexMap.at(element.column()));
coefficients.push_back(-element.value());
}
variables.push_back(lraValueVariableIndex);
coefficients.push_back(storm::utility::constGetOne<ValueType>() / this->getModel().getExitRate(state));
solver->addConstraint("state" + std::to_string(state), variables, coefficients, min ? storm::solver::LpSolver::LESS_EQUAL : storm::solver::LpSolver::GREATER_EQUAL, goalStates.get(state) ? storm::utility::constGetOne<ValueType>() / this->getModel().getExitRate(state) : storm::utility::constGetZero<ValueType>());
} else {
// For probabilistic states, we want to add the constraint x_s <= sum P(s, a, s') * x_s' where a is the current action
// and the sum ranges over all states s'.
for (auto choice : stateChoicesPair.second) {
variables.clear();
coefficients.clear();
variables.push_back(stateToVariableIndexMap.at(state));
coefficients.push_back(1);
typename storm::storage::SparseMatrix<ValueType>::Rows row = transitionMatrix.getRow(choice);
for (auto element : row) {
variables.push_back(stateToVariableIndexMap.at(element.column()));
coefficients.push_back(-element.value());
}
solver->addConstraint("state" + std::to_string(state), variables, coefficients, min ? storm::solver::LpSolver::LESS_EQUAL : storm::solver::LpSolver::GREATER_EQUAL, storm::utility::constGetZero<ValueType>());
}
}
}
solver->optimize();
lraValuesForEndComponents.push_back(solver->getContinuousValue(lraValueVariableIndex));
// Compute the LRA value for the current MEC.
lraValuesForEndComponents.push_back(this->computeLraForMaximalEndComponent(min, transitionMatrix, nondeterministicChoiceIndices, this->getModel().getMarkovianStates(), this->getModel().getExitRates(), goalStates, mec, currentMecIndex));
}
// For fast transition rewriting, we build some auxiliary data structures.
storm::storage::BitVector statesNotContainedInAnyMec = ~statesInMecs;
uint_fast64_t firstAuxiliaryStateIndex = statesNotContainedInAnyMec.getNumberOfSetBits();
@ -223,7 +318,7 @@ namespace storm {
for (auto element : row) {
if (statesNotContainedInAnyMec.get(element.column())) {
// If the target state is not contained in an MEC, we can copy over the entry.
sspMatrix.insertNextValue(currentChoice, statesNotInMecsBeforeIndex[element.column()], element.value());
sspMatrix.insertNextValue(currentChoice, statesNotInMecsBeforeIndex[element.column()], element.value(), true);
} else {
// If the target state is contained in MEC i, we need to add the probability to the corresponding field in the vector
// so that we are able to write the cumulative probability to the MEC into the matrix.
@ -234,7 +329,7 @@ namespace storm {
// Now insert all (cumulative) probability values that target an MEC.
for (uint_fast64_t mecIndex = 0; mecIndex < auxiliaryStateToProbabilityMap.size(); ++mecIndex) {
if (auxiliaryStateToProbabilityMap[mecIndex] != 0) {
sspMatrix.insertNextValue(currentChoice, firstAuxiliaryStateIndex + mecIndex, auxiliaryStateToProbabilityMap[mecIndex]);
sspMatrix.insertNextValue(currentChoice, firstAuxiliaryStateIndex + mecIndex, auxiliaryStateToProbabilityMap[mecIndex], true);
}
}
}
@ -260,7 +355,7 @@ namespace storm {
for (auto element : row) {
if (statesNotContainedInAnyMec.get(element.column())) {
// If the target state is not contained in an MEC, we can copy over the entry.
sspMatrix.insertNextValue(currentChoice, statesNotInMecsBeforeIndex[element.column()], element.value());
sspMatrix.insertNextValue(currentChoice, statesNotInMecsBeforeIndex[element.column()], element.value(), true);
} else {
// If the target state is contained in MEC i, we need to add the probability to the corresponding field in the vector
// so that we are able to write the cumulative probability to the MEC into the matrix.
@ -277,7 +372,7 @@ namespace storm {
b.back() += auxiliaryStateToProbabilityMap[mecIndex] * lraValuesForEndComponents[mecIndex];
} else {
// Otherwise, we add a transition to the auxiliary state that is associated with the target MEC.
sspMatrix.insertNextValue(currentChoice, firstAuxiliaryStateIndex + targetMecIndex, auxiliaryStateToProbabilityMap[targetMecIndex]);
sspMatrix.insertNextValue(currentChoice, firstAuxiliaryStateIndex + targetMecIndex, auxiliaryStateToProbabilityMap[targetMecIndex], true);
}
}
}
@ -288,7 +383,7 @@ namespace storm {
}
// For each auxiliary state, there is the option to achieve the reward value of the LRA associated with the MEC.
sspMatrix.insertEmptyRow();
sspMatrix.insertEmptyRow(true);
++currentChoice;
b.push_back(lraValuesForEndComponents[mecIndex]);
}
@ -299,21 +394,13 @@ namespace storm {
// Finalize the matrix and solve the corresponding system of equations.
sspMatrix.finalize();
std::vector<ValueType> x(numberOfStatesNotInMecs + mecDecomposition.size());
if (linearEquationSolver != nullptr) {
this->linearEquationSolver->solveEquationSystem(min, sspMatrix, x, b, sspNondeterministicChoiceIndices);
} else {
throw storm::exceptions::InvalidStateException() << "No valid linear equation solver available.";
}
nondeterministicLinearEquationSolver->solveEquationSystem(min, sspMatrix, x, b, sspNondeterministicChoiceIndices);
// Prepare result vector.
std::vector<ValueType> result(this->getModel().getNumberOfStates());
// Set the values for states not contained in MECs.
uint_fast64_t stateIndex = 0;
for (auto state : statesNotContainedInAnyMec) {
result[state] = x[stateIndex];
++stateIndex;
}
storm::utility::vector::setVectorValues(result, statesNotContainedInAnyMec, x);
// Set the values for all states in MECs.
for (auto state : statesInMecs) {
@ -324,10 +411,103 @@ namespace storm {
}
std::vector<ValueType> checkExpectedTime(bool min, storm::storage::BitVector const& goalStates) const {
// Reduce the problem of computing the expected time to computing expected rewards where the rewards
// for all probabilistic states are zero and the reward values of Markovian states is 1.
std::vector<ValueType> rewardValues(this->getModel().getNumberOfStates(), storm::utility::constGetZero<ValueType>());
storm::utility::vector::setVectorValues(rewardValues, this->getModel().getMarkovianStates(), storm::utility::constGetOne<ValueType>());
return this->computeExpectedRewards(min, goalStates, rewardValues);
}
protected:
/*!
* Computes the long-run average value for the given maximal end component of a Markov automaton.
*
* @param min Sets whether the long-run average is to be minimized or maximized.
* @param transitionMatrix The transition matrix of the underlying Markov automaton.
* @param nondeterministicChoiceIndices A vector indicating at which row the choice of a given state begins.
* @param markovianStates A bit vector storing all markovian states.
* @param exitRates A vector with exit rates for all states. Exit rates of probabilistic states are assumed to be zero.
* @param goalStates A bit vector indicating which states are to be considered as goal states.
* @param mec The maximal end component to consider for computing the long-run average.
* @param mecIndex The index of the MEC.
* @return The long-run average of being in a goal state for the given MEC.
*/
static ValueType computeLraForMaximalEndComponent(bool min, storm::storage::SparseMatrix<ValueType> const& transitionMatrix, std::vector<uint_fast64_t> const& nondeterministicChoiceIndices, storm::storage::BitVector const& markovianStates, std::vector<ValueType> const& exitRates, storm::storage::BitVector const& goalStates, storm::storage::MaximalEndComponent const& mec, uint_fast64_t mecIndex = 0) {
std::shared_ptr<storm::solver::LpSolver> solver = storm::utility::solver::getLpSolver("LRA for MEC");
solver->setModelSense(min ? storm::solver::LpSolver::MAXIMIZE : storm::solver::LpSolver::MINIMIZE);
// First, we need to create the variables for the problem.
std::map<uint_fast64_t, uint_fast64_t> stateToVariableIndexMap;
for (auto const& stateChoicesPair : mec) {
stateToVariableIndexMap[stateChoicesPair.first] = solver->createContinuousVariable("x" + std::to_string(stateChoicesPair.first), storm::solver::LpSolver::UNBOUNDED, 0, 0, 0);
}
uint_fast64_t lraValueVariableIndex = solver->createContinuousVariable("k", storm::solver::LpSolver::UNBOUNDED, 0, 0, 1);
// Now we encode the problem as constraints.
std::vector<uint_fast64_t> variables;
std::vector<double> coefficients;
for (auto const& stateChoicesPair : mec) {
uint_fast64_t state = stateChoicesPair.first;
// Now, based on the type of the state, create a suitable constraint.
if (markovianStates.get(state)) {
variables.clear();
coefficients.clear();
variables.push_back(stateToVariableIndexMap.at(state));
coefficients.push_back(1);
typename storm::storage::SparseMatrix<ValueType>::Rows row = transitionMatrix.getRow(nondeterministicChoiceIndices[state]);
for (auto element : row) {
variables.push_back(stateToVariableIndexMap.at(element.column()));
coefficients.push_back(-element.value());
}
variables.push_back(lraValueVariableIndex);
coefficients.push_back(storm::utility::constGetOne<ValueType>() / exitRates[state]);
solver->addConstraint("state" + std::to_string(state), variables, coefficients, min ? storm::solver::LpSolver::LESS_EQUAL : storm::solver::LpSolver::GREATER_EQUAL, goalStates.get(state) ? storm::utility::constGetOne<ValueType>() / exitRates[state] : storm::utility::constGetZero<ValueType>());
} else {
// For probabilistic states, we want to add the constraint x_s <= sum P(s, a, s') * x_s' where a is the current action
// and the sum ranges over all states s'.
for (auto choice : stateChoicesPair.second) {
variables.clear();
coefficients.clear();
variables.push_back(stateToVariableIndexMap.at(state));
coefficients.push_back(1);
typename storm::storage::SparseMatrix<ValueType>::Rows row = transitionMatrix.getRow(choice);
for (auto element : row) {
variables.push_back(stateToVariableIndexMap.at(element.column()));
coefficients.push_back(-element.value());
}
solver->addConstraint("state" + std::to_string(state), variables, coefficients, min ? storm::solver::LpSolver::LESS_EQUAL : storm::solver::LpSolver::GREATER_EQUAL, storm::utility::constGetZero<ValueType>());
}
}
}
solver->optimize();
return solver->getContinuousValue(lraValueVariableIndex);
}
/*!
* Computes the expected reward that is gained from each state before entering any of the goal states.
*
* @param min Indicates whether minimal or maximal rewards are to be computed.
* @param goalStates The goal states that define until which point rewards are gained.
* @param stateRewards A vector that defines the reward gained in each state. For probabilistic states, this is an instantaneous reward
* that is fully gained and for Markovian states the actually gained reward is dependent on the expected time to stay in the
* state, i.e. it is gouverned by the exit rate of the state.
* @return A vector that contains the expected reward for each state of the model.
*/
std::vector<ValueType> computeExpectedRewards(bool min, storm::storage::BitVector const& goalStates, std::vector<ValueType> const& stateRewards) const {
// Check whether the automaton is closed.
if (!this->getModel().isClosed()) {
throw storm::exceptions::InvalidArgumentException() << "Unable to compute expected time on non-closed Markov automaton.";
}
// First, we need to check which states have infinite expected time (by definition).
storm::storage::BitVector infinityStates;
if (min) {
@ -374,86 +554,50 @@ namespace storm {
infinityStates = storm::storage::BitVector(this->getModel().getNumberOfStates());
}
}
// Now we identify the states for which values need to be computed.
storm::storage::BitVector maybeStates = ~(goalStates | infinityStates);
// Then, we can eliminate the rows and columns for all states whose values are already known to be 0.
std::vector<ValueType> x(maybeStates.getNumberOfSetBits());
std::vector<uint_fast64_t> subNondeterministicChoiceIndices = this->computeNondeterministicChoiceIndicesForConstraint(maybeStates);
std::vector<uint_fast64_t> subNondeterministicChoiceIndices = storm::utility::vector::getConstrainedOffsetVector(this->getModel().getNondeterministicChoiceIndices(), maybeStates);
storm::storage::SparseMatrix<ValueType> submatrix = this->getModel().getTransitionMatrix().getSubmatrix(maybeStates, this->getModel().getNondeterministicChoiceIndices());
// Now prepare the mean sojourn times for all states so they can be used as the right-hand side of the equation system.
std::vector<ValueType> meanSojournTimes(this->getModel().getExitRates());
// Now prepare the expected reward values for all states so they can be used as the right-hand side of the equation system.
std::vector<ValueType> rewardValues(stateRewards);
for (auto state : this->getModel().getMarkovianStates()) {
meanSojournTimes[state] = storm::utility::constGetOne<ValueType>() / meanSojournTimes[state];
rewardValues[state] = rewardValues[state] / this->getModel().getExitRates()[state];
}
// Finally, prepare the actual right-hand side.
std::vector<ValueType> b(submatrix.getRowCount());
storm::utility::vector::selectVectorValuesRepeatedly(b, maybeStates, this->getModel().getNondeterministicChoiceIndices(), meanSojournTimes);
storm::utility::vector::selectVectorValuesRepeatedly(b, maybeStates, this->getModel().getNondeterministicChoiceIndices(), rewardValues);
// Solve the corresponding system of equations.
if (linearEquationSolver != nullptr) {
this->linearEquationSolver->solveEquationSystem(min, submatrix, x, b, subNondeterministicChoiceIndices);
} else {
throw storm::exceptions::InvalidStateException() << "No valid linear equation solver available.";
}
std::shared_ptr<storm::solver::AbstractNondeterministicLinearEquationSolver<ValueType>> nondeterministiclinearEquationSolver = storm::utility::solver::getNondeterministicLinearEquationSolver<ValueType>();
nondeterministiclinearEquationSolver->solveEquationSystem(min, submatrix, x, b, subNondeterministicChoiceIndices);
// Create resulting vector.
std::vector<ValueType> result(this->getModel().getNumberOfStates());
// Set values of resulting vector according to previous result and return the result.
storm::utility::vector::setVectorValues<ValueType>(result, maybeStates, x);
storm::utility::vector::setVectorValues(result, goalStates, storm::utility::constGetZero<ValueType>());
storm::utility::vector::setVectorValues(result, infinityStates, storm::utility::constGetInfinity<ValueType>());
return result;
}
protected:
/*!
* A stack used for storing whether we are currently computing min or max probabilities or rewards, respectively.
* The topmost element is true if and only if we are currently computing minimum probabilities or rewards.
*/
mutable std::stack<bool> minimumOperatorStack;
private:
/*!
* Computes the nondeterministic choice indices vector resulting from reducing the full system to the states given
* by the parameter constraint.
*
* @param constraint A bit vector specifying which states are kept.
* @returns A vector of the nondeterministic choice indices of the subsystem induced by the given constraint.
* A solver that is used for solving systems of linear equations that are the result of nondeterministic choices.
*/
std::vector<uint_fast64_t> computeNondeterministicChoiceIndicesForConstraint(storm::storage::BitVector const& constraint) const {
// First, get a reference to the full nondeterministic choice indices.
std::vector<uint_fast64_t> const& nondeterministicChoiceIndices = this->getModel().getNondeterministicChoiceIndices();
// Reserve the known amount of slots for the resulting vector.
std::vector<uint_fast64_t> subNondeterministicChoiceIndices(constraint.getNumberOfSetBits() + 1);
uint_fast64_t currentRowCount = 0;
uint_fast64_t currentIndexCount = 1;
// Set the first element as this will clearly begin at offset 0.
subNondeterministicChoiceIndices[0] = 0;
// Loop over all states that need to be kept and copy the relative indices of the nondeterministic choices over
// to the resulting vector.
for (auto index : constraint) {
subNondeterministicChoiceIndices[currentIndexCount] = currentRowCount + nondeterministicChoiceIndices[index + 1] - nondeterministicChoiceIndices[index];
currentRowCount += nondeterministicChoiceIndices[index + 1] - nondeterministicChoiceIndices[index];
++currentIndexCount;
}
// Put a sentinel element at the end.
subNondeterministicChoiceIndices[constraint.getNumberOfSetBits()] = currentRowCount;
return subNondeterministicChoiceIndices;
}
// An object that is used for solving linear equations and performing matrix-vector multiplication.
std::unique_ptr<storm::solver::AbstractNondeterministicLinearEquationSolver<ValueType>> linearEquationSolver;
std::shared_ptr<storm::solver::AbstractNondeterministicLinearEquationSolver<ValueType>> nondeterministicLinearEquationSolver;
};
}
}

148
src/modelchecker/prctl/SparseMdpPrctlModelChecker.h

@ -18,6 +18,7 @@
#include "src/models/Mdp.h"
#include "src/utility/vector.h"
#include "src/utility/graph.h"
#include "src/utility/solver.h"
#include "src/settings/Settings.h"
#include "src/storage/TotalScheduler.h"
@ -37,7 +38,7 @@ namespace storm {
*
* @param model The MDP to be checked.
*/
explicit SparseMdpPrctlModelChecker(storm::models::Mdp<Type> const& model, storm::solver::AbstractNondeterministicLinearEquationSolver<Type>* linearEquationSolver) : AbstractModelChecker<Type>(model), minimumOperatorStack(), linearEquationSolver(linearEquationSolver) {
explicit SparseMdpPrctlModelChecker(storm::models::Mdp<Type> const& model, std::shared_ptr<storm::solver::AbstractNondeterministicLinearEquationSolver<Type>> nondeterministicLinearEquationSolver = storm::utility::solver::getNondeterministicLinearEquationSolver<Type>()) : AbstractModelChecker<Type>(model), minimumOperatorStack(), nondeterministicLinearEquationSolver(nondeterministicLinearEquationSolver) {
// Intentionally left empty.
}
@ -46,7 +47,7 @@ namespace storm {
* constructed model checker will have the model of the given model checker as its associated model.
*/
explicit SparseMdpPrctlModelChecker(storm::modelchecker::prctl::SparseMdpPrctlModelChecker<Type> const& modelchecker)
: AbstractModelChecker<Type>(modelchecker), minimumOperatorStack(), linearEquationSolver(new storm::solver::AbstractNondeterministicLinearEquationSolver<Type>()) {
: AbstractModelChecker<Type>(modelchecker), minimumOperatorStack(), nondeterministicLinearEquationSolver(storm::utility::solver::getNondeterministicLinearEquationSolver<Type>()) {
// Intentionally left empty.
}
@ -95,9 +96,9 @@ namespace storm {
// Determine the states that have 0 probability of reaching the target states.
storm::storage::BitVector statesWithProbabilityGreater0;
if (this->minimumOperatorStack.top()) {
statesWithProbabilityGreater0 = storm::utility::graph::performProbGreater0A(this->getModel(), this->getModel().getBackwardTransitions(), phiStates, psiStates, true, stepBound);
statesWithProbabilityGreater0 = storm::utility::graph::performProbGreater0A(this->getModel().getTransitionMatrix(), this->getModel().getNondeterministicChoiceIndices(), this->getModel().getBackwardTransitions(), phiStates, psiStates, true, stepBound);
} else {
statesWithProbabilityGreater0 = storm::utility::graph::performProbGreater0E(this->getModel(), this->getModel().getBackwardTransitions(), phiStates, psiStates, true, stepBound);
statesWithProbabilityGreater0 = storm::utility::graph::performProbGreater0E(this->getModel().getTransitionMatrix(), this->getModel().getNondeterministicChoiceIndices(), this->getModel().getBackwardTransitions(), phiStates, psiStates, true, stepBound);
}
// Check if we already know the result (i.e. probability 0) for all initial states and
@ -115,7 +116,7 @@ namespace storm {
storm::storage::SparseMatrix<Type> submatrix = this->getModel().getTransitionMatrix().getSubmatrix(statesWithProbabilityGreater0, this->getModel().getNondeterministicChoiceIndices());
// Get the "new" nondeterministic choice indices for the submatrix.
std::vector<uint_fast64_t> subNondeterministicChoiceIndices = this->computeNondeterministicChoiceIndicesForConstraint(statesWithProbabilityGreater0);
std::vector<uint_fast64_t> subNondeterministicChoiceIndices = storm::utility::vector::getConstrainedOffsetVector(this->getModel().getNondeterministicChoiceIndices(), statesWithProbabilityGreater0);
// Compute the new set of target states in the reduced system.
storm::storage::BitVector rightStatesInReducedSystem = statesWithProbabilityGreater0 % psiStates;
@ -127,11 +128,7 @@ namespace storm {
std::vector<Type> subresult(statesWithProbabilityGreater0.getNumberOfSetBits());
storm::utility::vector::setVectorValues(subresult, rightStatesInReducedSystem, storm::utility::constGetOne<Type>());
if (linearEquationSolver != nullptr) {
this->linearEquationSolver->performMatrixVectorMultiplication(this->minimumOperatorStack.top(), submatrix, subresult, subNondeterministicChoiceIndices, nullptr, stepBound);
} else {
throw storm::exceptions::InvalidStateException() << "No valid linear equation solver available.";
}
this->nondeterministicLinearEquationSolver->performMatrixVectorMultiplication(this->minimumOperatorStack.top(), submatrix, subresult, subNondeterministicChoiceIndices, nullptr, stepBound);
// Set the values of the resulting vector accordingly.
storm::utility::vector::setVectorValues(result, statesWithProbabilityGreater0, subresult);
@ -172,11 +169,7 @@ namespace storm {
std::vector<Type> result(this->getModel().getNumberOfStates());
storm::utility::vector::setVectorValues(result, nextStates, storm::utility::constGetOne<Type>());
if (linearEquationSolver != nullptr) {
this->linearEquationSolver->performMatrixVectorMultiplication(this->minimumOperatorStack.top(), this->getModel().getTransitionMatrix(), result, this->getModel().getNondeterministicChoiceIndices());
} else {
throw storm::exceptions::InvalidStateException() << "No valid linear equation solver available.";
}
this->nondeterministicLinearEquationSolver->performMatrixVectorMultiplication(this->minimumOperatorStack.top(), this->getModel().getTransitionMatrix(), result, this->getModel().getNondeterministicChoiceIndices());
return result;
}
@ -285,29 +278,30 @@ namespace storm {
* @return The probabilities for the satisfying phi until psi for each state of the model. If the
* qualitative flag is set, exact probabilities might not be computed.
*/
std::pair<std::vector<Type>, storm::storage::TotalScheduler> checkUntil(bool minimize, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, bool qualitative) const {
static std::pair<std::vector<Type>, storm::storage::TotalScheduler> computeUnboundedUntilProbabilities(bool minimize, storm::storage::SparseMatrix<Type> const& transitionMatrix, std::vector<uint_fast64_t> nondeterministicChoiceIndices, storm::storage::SparseMatrix<Type> const& backwardTransitions, storm::storage::BitVector const& initialStates, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, std::shared_ptr<storm::solver::AbstractNondeterministicLinearEquationSolver<Type>> nondeterministicLinearEquationSolver, bool qualitative) {
size_t numberOfStates = phiStates.size();
// We need to identify the states which have to be taken out of the matrix, i.e.
// all states that have probability 0 and 1 of satisfying the until-formula.
std::pair<storm::storage::BitVector, storm::storage::BitVector> statesWithProbability01;
if (minimize) {
statesWithProbability01 = storm::utility::graph::performProb01Min(this->getModel(), phiStates, psiStates);
statesWithProbability01 = storm::utility::graph::performProb01Min(transitionMatrix, nondeterministicChoiceIndices, backwardTransitions, phiStates, psiStates);
} else {
statesWithProbability01 = storm::utility::graph::performProb01Max(this->getModel(), phiStates, psiStates);
statesWithProbability01 = storm::utility::graph::performProb01Max(transitionMatrix, nondeterministicChoiceIndices, backwardTransitions, phiStates, psiStates);
}
storm::storage::BitVector statesWithProbability0 = std::move(statesWithProbability01.first);
storm::storage::BitVector statesWithProbability1 = std::move(statesWithProbability01.second);
storm::storage::BitVector maybeStates = ~(statesWithProbability0 | statesWithProbability1);
LOG4CPLUS_INFO(logger, "Found " << statesWithProbability0.getNumberOfSetBits() << " 'no' states.");
LOG4CPLUS_INFO(logger, "Found " << statesWithProbability1.getNumberOfSetBits() << " 'yes' states.");
LOG4CPLUS_INFO(logger, "Found " << maybeStates.getNumberOfSetBits() << " 'maybe' states.");
// Create resulting vector.
std::vector<Type> result(this->getModel().getNumberOfStates());
std::vector<Type> result(numberOfStates);
// Check whether we need to compute exact probabilities for some states.
if (this->getModel().getInitialStates().isDisjointFrom(maybeStates) || qualitative) {
if (initialStates.isDisjointFrom(maybeStates) || qualitative) {
if (qualitative) {
LOG4CPLUS_INFO(logger, "The formula was checked qualitatively. No exact probabilities were computed.");
} else {
@ -322,24 +316,20 @@ namespace storm {
// First, we can eliminate the rows and columns from the original transition probability matrix for states
// whose probabilities are already known.
storm::storage::SparseMatrix<Type> submatrix = this->getModel().getTransitionMatrix().getSubmatrix(maybeStates, this->getModel().getNondeterministicChoiceIndices());
storm::storage::SparseMatrix<Type> submatrix = transitionMatrix.getSubmatrix(maybeStates, nondeterministicChoiceIndices);
// Get the "new" nondeterministic choice indices for the submatrix.
std::vector<uint_fast64_t> subNondeterministicChoiceIndices = this->computeNondeterministicChoiceIndicesForConstraint(maybeStates);
std::vector<uint_fast64_t> subNondeterministicChoiceIndices = storm::utility::vector::getConstrainedOffsetVector(nondeterministicChoiceIndices, maybeStates);
// Prepare the right-hand side of the equation system. For entry i this corresponds to
// the accumulated probability of going from state i to some 'yes' state.
std::vector<Type> b = this->getModel().getTransitionMatrix().getConstrainedRowSumVector(maybeStates, this->getModel().getNondeterministicChoiceIndices(), statesWithProbability1, submatrix.getRowCount());
std::vector<Type> b = transitionMatrix.getConstrainedRowSumVector(maybeStates, nondeterministicChoiceIndices, statesWithProbability1, submatrix.getRowCount());
// Create vector for results for maybe states.
std::vector<Type> x(maybeStates.getNumberOfSetBits());
// Solve the corresponding system of equations.
if (linearEquationSolver != nullptr) {
this->linearEquationSolver->solveEquationSystem(minimize, submatrix, x, b, subNondeterministicChoiceIndices);
} else {
throw storm::exceptions::InvalidStateException() << "No valid linear equation solver available.";
}
nondeterministicLinearEquationSolver->solveEquationSystem(minimize, submatrix, x, b, subNondeterministicChoiceIndices);
// Set values of resulting vector according to result.
storm::utility::vector::setVectorValues<Type>(result, maybeStates, x);
@ -350,11 +340,15 @@ namespace storm {
storm::utility::vector::setVectorValues<Type>(result, statesWithProbability1, storm::utility::constGetOne<Type>());
// Finally, compute a scheduler that achieves the extramal value.
storm::storage::TotalScheduler scheduler = this->computeExtremalScheduler(minimize, false, result);
storm::storage::TotalScheduler scheduler = computeExtremalScheduler(minimize, transitionMatrix, nondeterministicChoiceIndices, result);
return std::make_pair(result, scheduler);
}
std::pair<std::vector<Type>, storm::storage::TotalScheduler> checkUntil(bool minimize, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, bool qualitative) const {
return computeUnboundedUntilProbabilities(minimize, this->getModel().getTransitionMatrix(), this->getModel().getNondeterministicChoiceIndices(), this->getModel().getBackwardTransitions(), this->getModel().getInitialStates(), phiStates, psiStates, this->nondeterministicLinearEquationSolver, qualitative);
}
/*!
* Checks the given formula that is an instantaneous reward formula.
*
@ -376,11 +370,7 @@ namespace storm {
// Initialize result to state rewards of the model.
std::vector<Type> result(this->getModel().getStateRewardVector());
if (linearEquationSolver != nullptr) {
this->linearEquationSolver->performMatrixVectorMultiplication(this->minimumOperatorStack.top(), this->getModel().getTransitionMatrix(), result, this->getModel().getNondeterministicChoiceIndices(), nullptr, formula.getBound());
} else {
throw storm::exceptions::InvalidStateException() << "No valid linear equation solver available.";
}
this->nondeterministicLinearEquationSolver->performMatrixVectorMultiplication(this->minimumOperatorStack.top(), this->getModel().getTransitionMatrix(), result, this->getModel().getNondeterministicChoiceIndices(), nullptr, formula.getBound());
return result;
}
@ -422,11 +412,7 @@ namespace storm {
result.resize(this->getModel().getNumberOfStates());
}
if (linearEquationSolver != nullptr) {
this->linearEquationSolver->performMatrixVectorMultiplication(this->minimumOperatorStack.top(), this->getModel().getTransitionMatrix(), result, this->getModel().getNondeterministicChoiceIndices(), &totalRewardVector, formula.getBound());
} else {
throw storm::exceptions::InvalidStateException() << "No valid linear equation solver available.";
}
this->nondeterministicLinearEquationSolver->performMatrixVectorMultiplication(this->minimumOperatorStack.top(), this->getModel().getTransitionMatrix(), result, this->getModel().getNondeterministicChoiceIndices(), &totalRewardVector, formula.getBound());
return result;
}
@ -472,9 +458,9 @@ namespace storm {
storm::storage::BitVector infinityStates;
storm::storage::BitVector trueStates(this->getModel().getNumberOfStates(), true);
if (minimize) {
infinityStates = std::move(storm::utility::graph::performProb1A(this->getModel(), this->getModel().getBackwardTransitions(), trueStates, targetStates));
infinityStates = std::move(storm::utility::graph::performProb1A(this->getModel().getTransitionMatrix(), this->getModel().getNondeterministicChoiceIndices(), this->getModel().getBackwardTransitions(), trueStates, targetStates));
} else {
infinityStates = std::move(storm::utility::graph::performProb1E(this->getModel(), this->getModel().getBackwardTransitions(), trueStates, targetStates));
infinityStates = std::move(storm::utility::graph::performProb1E(this->getModel().getTransitionMatrix(), this->getModel().getNondeterministicChoiceIndices(), this->getModel().getBackwardTransitions(), trueStates, targetStates));
}
infinityStates.complement();
storm::storage::BitVector maybeStates = ~targetStates & ~infinityStates;
@ -500,7 +486,7 @@ namespace storm {
storm::storage::SparseMatrix<Type> submatrix = this->getModel().getTransitionMatrix().getSubmatrix(maybeStates, this->getModel().getNondeterministicChoiceIndices());
// Get the "new" nondeterministic choice indices for the submatrix.
std::vector<uint_fast64_t> subNondeterministicChoiceIndices = this->computeNondeterministicChoiceIndicesForConstraint(maybeStates);
std::vector<uint_fast64_t> subNondeterministicChoiceIndices = storm::utility::vector::getConstrainedOffsetVector(this->getModel().getNondeterministicChoiceIndices(), maybeStates);
// Prepare the right-hand side of the equation system. For entry i this corresponds to
// the accumulated probability of going from state i to some 'yes' state.
@ -534,11 +520,7 @@ namespace storm {
std::vector<Type> x(maybeStates.getNumberOfSetBits());
// Solve the corresponding system of equations.
if (linearEquationSolver != nullptr) {
this->linearEquationSolver->solveEquationSystem(minimize, submatrix, x, b, subNondeterministicChoiceIndices);
} else {
throw storm::exceptions::InvalidStateException() << "No valid linear equation solver available.";
}
this->nondeterministicLinearEquationSolver->solveEquationSystem(minimize, submatrix, x, b, subNondeterministicChoiceIndices);
// Set values of resulting vector according to result.
storm::utility::vector::setVectorValues<Type>(result, maybeStates, x);
@ -549,8 +531,8 @@ namespace storm {
storm::utility::vector::setVectorValues(result, infinityStates, storm::utility::constGetInfinity<Type>());
// Finally, compute a scheduler that achieves the extramal value.
storm::storage::TotalScheduler scheduler = this->computeExtremalScheduler(this->minimumOperatorStack.top(), false, result);
storm::storage::TotalScheduler scheduler = computeExtremalScheduler(this->minimumOperatorStack.top(), this->getModel().getTransitionMatrix(), this->getModel().getNondeterministicChoiceIndices(), result, this->getModel().hasStateRewards() ? &this->getModel().getStateRewardVector() : nullptr, this->getModel().hasTransitionRewards() ? &this->getModel().getTransitionRewardMatrix() : nullptr);
return std::make_pair(result, scheduler);
}
@ -563,33 +545,33 @@ namespace storm {
* @param takenChoices The output vector that is to store the taken choices.
* @param nondeterministicChoiceIndices The assignment of states to their nondeterministic choices in the matrix.
*/
storm::storage::TotalScheduler computeExtremalScheduler(bool minimize, bool addRewards, std::vector<Type> const& result) const {
std::vector<Type> temporaryResult(this->getModel().getNondeterministicChoiceIndices().size() - 1);
static storm::storage::TotalScheduler computeExtremalScheduler(bool minimize, storm::storage::SparseMatrix<Type> const& transitionMatrix, std::vector<uint_fast64_t> const& nondeterministicChoiceIndices, std::vector<Type> const& result, std::vector<Type> const* stateRewardVector = nullptr, storm::storage::SparseMatrix<Type> const* transitionRewardMatrix = nullptr) {
std::vector<Type> temporaryResult(nondeterministicChoiceIndices.size() - 1);
std::vector<Type> nondeterministicResult(result);
storm::solver::GmmxxLinearEquationSolver<Type> solver;
solver.performMatrixVectorMultiplication(this->getModel().getTransitionMatrix(), nondeterministicResult, nullptr, 1);
if (addRewards) {
solver.performMatrixVectorMultiplication(transitionMatrix, nondeterministicResult, nullptr, 1);
if (stateRewardVector != nullptr || transitionRewardMatrix != nullptr) {
std::vector<Type> totalRewardVector;
if (this->getModel().hasTransitionRewards()) {
std::vector<Type> totalRewardVector = this->getModel().getTransitionMatrix().getPointwiseProductRowSumVector(this->getModel().getTransitionRewardMatrix());
if (this->getModel().hasStateRewards()) {
if (transitionRewardMatrix != nullptr) {
totalRewardVector = transitionMatrix.getPointwiseProductRowSumVector(*transitionRewardMatrix);
if (stateRewardVector != nullptr) {
std::vector<Type> stateRewards(totalRewardVector.size());
storm::utility::vector::selectVectorValuesRepeatedly(stateRewards, storm::storage::BitVector(this->getModel().getStateRewardVector().size(), true), this->getModel().getNondeterministicChoiceIndices(), this->getModel().getStateRewardVector());
storm::utility::vector::selectVectorValuesRepeatedly(stateRewards, storm::storage::BitVector(stateRewardVector->size(), true), nondeterministicChoiceIndices, *stateRewardVector);
storm::utility::vector::addVectorsInPlace(totalRewardVector, stateRewards);
}
} else {
totalRewardVector.resize(nondeterministicResult.size());
storm::utility::vector::selectVectorValuesRepeatedly(totalRewardVector, storm::storage::BitVector(this->getModel().getStateRewardVector().size(), true), this->getModel().getNondeterministicChoiceIndices(), this->getModel().getStateRewardVector());
storm::utility::vector::selectVectorValuesRepeatedly(totalRewardVector, storm::storage::BitVector(stateRewardVector->size(), true), nondeterministicChoiceIndices, *stateRewardVector);
}
storm::utility::vector::addVectorsInPlace(nondeterministicResult, totalRewardVector);
}
std::vector<uint_fast64_t> choices(this->getModel().getNumberOfStates());
std::vector<uint_fast64_t> choices(result.size());
if (minimize) {
storm::utility::vector::reduceVectorMin(nondeterministicResult, temporaryResult, this->getModel().getNondeterministicChoiceIndices(), &choices);
storm::utility::vector::reduceVectorMin(nondeterministicResult, temporaryResult, nondeterministicChoiceIndices, &choices);
} else {
storm::utility::vector::reduceVectorMax(nondeterministicResult, temporaryResult, this->getModel().getNondeterministicChoiceIndices(), &choices);
storm::utility::vector::reduceVectorMax(nondeterministicResult, temporaryResult, nondeterministicChoiceIndices, &choices);
}
return storm::storage::TotalScheduler(choices);
@ -601,45 +583,11 @@ namespace storm {
*/
mutable std::stack<bool> minimumOperatorStack;
private:
/*!
* Computes the nondeterministic choice indices vector resulting from reducing the full system to the states given
* by the parameter constraint.
*
* @param constraint A bit vector specifying which states are kept.
* @returns A vector of the nondeterministic choice indices of the subsystem induced by the given constraint.
* A solver that is used for solving systems of linear equations that are the result of nondeterministic choices.
*/
std::vector<uint_fast64_t> computeNondeterministicChoiceIndicesForConstraint(storm::storage::BitVector const& constraint) const {
// First, get a reference to the full nondeterministic choice indices.
std::vector<uint_fast64_t> const& nondeterministicChoiceIndices = this->getModel().getNondeterministicChoiceIndices();
// Reserve the known amount of slots for the resulting vector.
std::vector<uint_fast64_t> subNondeterministicChoiceIndices(constraint.getNumberOfSetBits() + 1);
uint_fast64_t currentRowCount = 0;
uint_fast64_t currentIndexCount = 1;
// Set the first element as this will clearly begin at offset 0.
subNondeterministicChoiceIndices[0] = 0;
// Loop over all states that need to be kept and copy the relative indices of the nondeterministic choices over
// to the resulting vector.
for (auto index : constraint) {
subNondeterministicChoiceIndices[currentIndexCount] = currentRowCount + nondeterministicChoiceIndices[index + 1] - nondeterministicChoiceIndices[index];
currentRowCount += nondeterministicChoiceIndices[index + 1] - nondeterministicChoiceIndices[index];
++currentIndexCount;
}
// Put a sentinel element at the end.
subNondeterministicChoiceIndices[constraint.getNumberOfSetBits()] = currentRowCount;
return subNondeterministicChoiceIndices;
}
// An object that is used for solving linear equations and performing matrix-vector multiplication.
std::unique_ptr<storm::solver::AbstractNondeterministicLinearEquationSolver<Type>> linearEquationSolver;
std::shared_ptr<storm::solver::AbstractNondeterministicLinearEquationSolver<Type>> nondeterministicLinearEquationSolver;
};
} // namespace prctl
} // namespace modelchecker
} // namespace storm

2
src/models/AtomicPropositionsLabeling.h

@ -289,7 +289,7 @@ public:
*/
void addState() {
for(uint_fast64_t i = 0; i < apsCurrent; i++) {
singleLabelings[i].resize(singleLabelings[i].getSize() + 1);
singleLabelings[i].resize(singleLabelings[i].size() + 1);
}
stateCount++;
}

4
src/models/Dtmc.h

@ -147,13 +147,13 @@ public:
}
// Does the vector have the right size?
if(subSysStates.getSize() != this->getNumberOfStates()) {
if(subSysStates.size() != this->getNumberOfStates()) {
LOG4CPLUS_INFO(logger, "BitVector has wrong size. Resizing it...");
subSysStates.resize(this->getNumberOfStates());
}
// Test if it is a proper subsystem of this Dtmc, i.e. if there is at least one state to be left out.
if(subSysStates.getNumberOfSetBits() == subSysStates.getSize()) {
if(subSysStates.getNumberOfSetBits() == subSysStates.size()) {
LOG4CPLUS_INFO(logger, "All states are kept. This is no proper subsystem.");
return storm::models::Dtmc<T>(*this);
}

8
src/models/MarkovAutomaton.h

@ -100,6 +100,14 @@ namespace storm {
return this->exitRates[state];
}
T getMaximalExitRate() const {
T result = storm::utility::constGetZero<T>();
for (auto markovianState : this->markovianStates) {
result = std::max(result, this->exitRates[markovianState]);
}
return result;
}
storm::storage::BitVector const& getMarkovianStates() const {
return this->markovianStates;
}

76
src/solver/AbstractNondeterministicLinearEquationSolver.h

@ -13,9 +13,19 @@ namespace storm {
template<class Type>
class AbstractNondeterministicLinearEquationSolver {
public:
AbstractNondeterministicLinearEquationSolver() {
storm::settings::Settings* s = storm::settings::Settings::getInstance();
precision = s->getOptionByLongName("precision").getArgument(0).getValueAsDouble();
maxIterations = s->getOptionByLongName("maxIterations").getArgument(0).getValueAsUnsignedInteger();
relative = s->getOptionByLongName("relative").getArgument(0).getValueAsBoolean();
}
AbstractNondeterministicLinearEquationSolver(double precision, uint_fast64_t maxIterations, bool relative) : precision(precision), maxIterations(maxIterations), relative(relative) {
// Intentionally left empty.
}
virtual AbstractNondeterministicLinearEquationSolver<Type>* clone() const {
return new AbstractNondeterministicLinearEquationSolver<Type>();
return new AbstractNondeterministicLinearEquationSolver<Type>(this->precision, this->maxIterations, this->relative);
}
/*!
@ -69,38 +79,38 @@ namespace storm {
* as there are states in the MDP.
* @returns The solution vector x of the system of linear equations as the content of the parameter x.
*/
virtual void solveEquationSystem(bool minimize, storm::storage::SparseMatrix<Type> const& A, std::vector<Type>& x, std::vector<Type> const& b, std::vector<uint_fast64_t> const& nondeterministicChoiceIndices) const {
LOG4CPLUS_INFO(logger, "Starting iterative solver.");
// Get the settings object to customize solving.
storm::settings::Settings* s = storm::settings::Settings::getInstance();
// Get relevant user-defined settings for solving the equations.
double precision = s->getOptionByLongName("precision").getArgument(0).getValueAsDouble();
uint_fast64_t maxIterations = s->getOptionByLongName("maxIterations").getArgument(0).getValueAsUnsignedInteger();
bool relative = s->getOptionByLongName("relative").getArgument(0).getValueAsBoolean();
virtual void solveEquationSystem(bool minimize, storm::storage::SparseMatrix<Type> const& A, std::vector<Type>& x, std::vector<Type> const& b, std::vector<uint_fast64_t> const& nondeterministicChoiceIndices, std::vector<Type>* multiplyResult = nullptr, std::vector<Type>* newX = nullptr) const {
// LOG4CPLUS_INFO(logger, "Starting iterative solver.");
// Set up the environment for the power method.
std::vector<Type> multiplyResult(A.getRowCount());
bool multiplyResultMemoryProvided = true;
if (multiplyResult == nullptr) {
multiplyResult = new std::vector<Type>(A.getRowCount());
multiplyResultMemoryProvided = false;
}
std::vector<Type>* currentX = &x;
std::vector<Type>* newX = new std::vector<Type>(x.size());
bool xMemoryProvided = true;
if (newX == nullptr) {
newX = new std::vector<Type>(x.size());
xMemoryProvided = false;
}
std::vector<Type>* swap = nullptr;
uint_fast64_t iterations = 0;
bool converged = false;
// Proceed with the iterations as long as the method did not converge or reach the
// user-specified maximum number of iterations.
while (!converged && iterations < maxIterations) {
// Compute x' = A*x + b.
A.multiplyWithVector(*currentX, multiplyResult);
storm::utility::vector::addVectorsInPlace(multiplyResult, b);
A.multiplyWithVector(*currentX, *multiplyResult);
storm::utility::vector::addVectorsInPlace(*multiplyResult, b);
// Reduce the vector x' by applying min/max for all non-deterministic choices as given by the topmost
// element of the min/max operator stack.
if (minimize) {
storm::utility::vector::reduceVectorMin(multiplyResult, *newX, nondeterministicChoiceIndices);
storm::utility::vector::reduceVectorMin(*multiplyResult, *newX, nondeterministicChoiceIndices);
} else {
storm::utility::vector::reduceVectorMax(multiplyResult, *newX, nondeterministicChoiceIndices);
storm::utility::vector::reduceVectorMax(*multiplyResult, *newX, nondeterministicChoiceIndices);
}
// Determine whether the method converged.
@ -112,24 +122,36 @@ namespace storm {
newX = swap;
++iterations;
}
// If we performed an odd number of iterations, we need to swap the x and currentX, because the newest result
// is currently stored in currentX, but x is the output vector.
if (iterations % 2 == 1) {
std::swap(x, *currentX);
delete currentX;
} else {
if (!xMemoryProvided) {
delete currentX;
}
} else if (!xMemoryProvided) {
delete newX;
}
// Check if the solver converged and issue a warning otherwise.
if (converged) {
LOG4CPLUS_INFO(logger, "Iterative solver converged after " << iterations << " iterations.");
} else {
LOG4CPLUS_WARN(logger, "Iterative solver did not converge.");
if (!multiplyResultMemoryProvided) {
delete multiplyResult;
}
// // Check if the solver converged and issue a warning otherwise.
// if (converged) {
// LOG4CPLUS_INFO(logger, "Iterative solver converged after " << iterations << " iterations.");
// } else {
// LOG4CPLUS_WARN(logger, "Iterative solver did not converge.");
// }
}
protected:
double precision;
uint_fast64_t maxIterations;
bool relative;
};
} // namespace solver

172
src/solver/GlpkLpSolver.cpp

@ -0,0 +1,172 @@
#include "src/solver/GlpkLpSolver.h"
#ifdef STORM_HAVE_GLPK
#include <iostream>
#include "src/exceptions/InvalidStateException.h"
#include "src/settings/Settings.h"
#include "log4cplus/logger.h"
#include "log4cplus/loggingmacros.h"
extern log4cplus::Logger logger;
namespace storm {
namespace solver {
GlpkLpSolver::GlpkLpSolver(std::string const& name, ModelSense const& modelSense) : LpSolver(modelSense), lp(nullptr), nextVariableIndex(1), nextConstraintIndex(1), isOptimal(false), rowIndices(), columnIndices(), coefficientValues() {
// Create the LP problem for glpk.
lp = glp_create_prob();
// Set its name and model sense.
glp_set_prob_name(lp, name.c_str());
this->setModelSense(modelSense);
// Because glpk uses 1-based indexing (wtf!?), we need to put dummy elements into the matrix vectors.
rowIndices.push_back(0);
columnIndices.push_back(0);
coefficientValues.push_back(0);
}
GlpkLpSolver::GlpkLpSolver(std::string const& name) : GlpkLpSolver(name, MINIMIZE) {
// Intentionally left empty.
}
GlpkLpSolver::~GlpkLpSolver() {
glp_delete_prob(this->lp);
glp_free_env();
}
uint_fast64_t GlpkLpSolver::createContinuousVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) {
glp_add_cols(this->lp, 1);
glp_set_col_name(this->lp, nextVariableIndex, name.c_str());
switch (variableType) {
case LpSolver::BOUNDED:
glp_set_col_bnds(lp, nextVariableIndex, GLP_DB, lowerBound, upperBound);
break;
case LpSolver::UNBOUNDED:
glp_set_col_bnds(lp, nextVariableIndex, GLP_FR, 0, 0);
break;
case LpSolver::UPPER_BOUND:
glp_set_col_bnds(lp, nextVariableIndex, GLP_UP, 0, upperBound);
break;
case LpSolver::LOWER_BOUND:
glp_set_col_bnds(lp, nextVariableIndex, GLP_LO, lowerBound, 0);
break;
}
glp_set_col_kind(this->lp, nextVariableIndex, GLP_CV);
glp_set_obj_coef(this->lp, nextVariableIndex, objectiveFunctionCoefficient);
++nextVariableIndex;
this->isOptimal = false;
return nextVariableIndex - 1;
}
uint_fast64_t GlpkLpSolver::createIntegerVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) {
uint_fast64_t index = this->createContinuousVariable(name, variableType, lowerBound, upperBound, objectiveFunctionCoefficient);
glp_set_col_kind(this->lp, index, GLP_IV);
return index;
}
uint_fast64_t GlpkLpSolver::createBinaryVariable(std::string const& name, double objectiveFunctionCoefficient) {
uint_fast64_t index = this->createContinuousVariable(name, UNBOUNDED, 0, 1, objectiveFunctionCoefficient);
glp_set_col_kind(this->lp, index, GLP_BV);
return index;
}
void GlpkLpSolver::addConstraint(std::string const& name, std::vector<uint_fast64_t> const& variables, std::vector<double> const& coefficients, BoundType const& boundType, double rightHandSideValue) {
if (variables.size() != coefficients.size()) {
LOG4CPLUS_ERROR(logger, "Sizes of variable indices vector and coefficients vector do not match.");
throw storm::exceptions::InvalidStateException() << "Sizes of variable indices vector and coefficients vector do not match.";
}
// Add the row that will represent this constraint.
glp_add_rows(this->lp, 1);
glp_set_row_name(this->lp, nextConstraintIndex, name.c_str());
// Determine the type of the constraint and add it properly.
bool isUpperBound = boundType == LESS || boundType == LESS_EQUAL;
bool isStrict = boundType == LESS || boundType == GREATER;
switch (boundType) {
case LESS:
glp_set_row_bnds(this->lp, nextConstraintIndex, GLP_UP, 0, rightHandSideValue - storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble());
break;
case LESS_EQUAL:
glp_set_row_bnds(this->lp, nextConstraintIndex, GLP_UP, 0, rightHandSideValue);
break;
case GREATER:
glp_set_row_bnds(this->lp, nextConstraintIndex, GLP_LO, rightHandSideValue + storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble(), 0);
break;
case GREATER_EQUAL:
glp_set_row_bnds(this->lp, nextConstraintIndex, GLP_LO, rightHandSideValue, 0);
break;
case EQUAL:
glp_set_row_bnds(this->lp, nextConstraintIndex, GLP_FX, rightHandSideValue, rightHandSideValue);
break;
}
// Record the variables and coefficients in the coefficient matrix.
rowIndices.insert(rowIndices.end(), variables.size(), nextConstraintIndex);
columnIndices.insert(columnIndices.end(), variables.begin(), variables.end());
coefficientValues.insert(coefficientValues.end(), coefficients.begin(), coefficients.end());
++nextConstraintIndex;
this->isOptimal = false;
}
void GlpkLpSolver::setModelSense(ModelSense const& newModelSense) {
glp_set_obj_dir(this->lp, newModelSense == MINIMIZE ? GLP_MIN : GLP_MAX);
this->isOptimal = false;
}
void GlpkLpSolver::optimize() const {
glp_load_matrix(this->lp, rowIndices.size() - 1, rowIndices.data(), columnIndices.data(), coefficientValues.data());
int error = glp_simplex(this->lp, nullptr);
if (error != 0) {
LOG4CPLUS_ERROR(logger, "Unable to optimize glpk model (" << error << ").");
throw storm::exceptions::InvalidStateException() << "Unable to optimize glpk model (" << error << ").";
}
this->isOptimal = true;
}
int_fast64_t GlpkLpSolver::getIntegerValue(uint_fast64_t variableIndex) const {
double value = glp_get_col_prim(this->lp, static_cast<int>(variableIndex));
if (std::abs(value - static_cast<int>(value)) <= storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()) {
// Nothing to do in this case.
} else if (std::abs(value) > storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()) {
LOG4CPLUS_ERROR(logger, "Illegal value for integer variable in glpk solution (" << value << ").");
throw storm::exceptions::InvalidStateException() << "Illegal value for integer variable in glpk solution (" << value << ").";
}
return static_cast<int_fast64_t>(value);
}
bool GlpkLpSolver::getBinaryValue(uint_fast64_t variableIndex) const {
double value = glp_get_col_prim(this->lp, static_cast<int>(variableIndex));
if (std::abs(value - 1) <= storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()) {
// Nothing to do in this case.
} else if (std::abs(value) > storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()) {
LOG4CPLUS_ERROR(logger, "Illegal value for binary variable in Gurobi solution (" << value << ").");
throw storm::exceptions::InvalidStateException() << "Illegal value for binary variable in Gurobi solution (" << value << ").";
}
return static_cast<bool>(value);
}
double GlpkLpSolver::getContinuousValue(uint_fast64_t variableIndex) const {
return glp_get_col_prim(this->lp, static_cast<int>(variableIndex));
}
void GlpkLpSolver::writeModelToFile(std::string const& filename) const {
glp_load_matrix(this->lp, rowIndices.size() - 1, rowIndices.data(), columnIndices.data(), coefficientValues.data());
glp_write_lp(this->lp, 0, filename.c_str());
}
}
}
#endif

113
src/solver/GlpkLpSolver.h

@ -0,0 +1,113 @@
#ifndef STORM_SOLVER_GLPKLPSOLVER_H_
#define STORM_SOLVER_GLPKLPSOLVER_H_
#include "src/solver/LpSolver.h"
#include "src/exceptions/NotImplementedException.h"
// To detect whether the usage of glpk is possible, this include is neccessary.
#include "storm-config.h"
#ifdef STORM_HAVE_GLPK
#include <glpk.h>
#endif
namespace storm {
namespace solver {
#ifdef STORM_HAVE_GLPK
class GlpkLpSolver : public LpSolver {
public:
GlpkLpSolver(std::string const& name, ModelSense const& modelSense);
GlpkLpSolver(std::string const& name);
virtual ~GlpkLpSolver();
virtual uint_fast64_t createContinuousVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) override;
virtual uint_fast64_t createIntegerVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) override;
virtual uint_fast64_t createBinaryVariable(std::string const& name, double objectiveFunctionCoefficient) override;
virtual void addConstraint(std::string const& name, std::vector<uint_fast64_t> const& variables, std::vector<double> const& coefficients, BoundType const& boundType, double rightHandSideValue) override;
virtual void setModelSense(ModelSense const& newModelSense) override;
virtual void optimize() const override;
virtual int_fast64_t getIntegerValue(uint_fast64_t variableIndex) const override;
virtual bool getBinaryValue(uint_fast64_t variableIndex) const override;
virtual double getContinuousValue(uint_fast64_t variableIndex) const override;
virtual void writeModelToFile(std::string const& filename) const override;
private:
// The glpk LP problem.
glp_prob* lp;
// A counter that keeps track of the next free variable index.
uint_fast64_t nextVariableIndex;
// A counter that keeps track of the next free constraint index.
uint_fast64_t nextConstraintIndex;
// A flag that stores whether the model was optimized properly.
mutable bool isOptimal;
// The arrays that store the coefficient matrix of the problem.
std::vector<int> rowIndices;
std::vector<int> columnIndices;
std::vector<double> coefficientValues;
};
#else
// If glpk is not available, we provide a stub implementation that emits an error if any of its methods is called.
class GlpkLpSolver : public LpSolver {
public:
GlpkLpSolver(std::string const& name) : LpSolver(MINIMIZE) {
throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support.";
}
virtual ~GlpkLpSolver() {
throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support.";
}
virtual uint_fast64_t createContinuousVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) override {
throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support.";
}
virtual uint_fast64_t createIntegerVariable(std::string const& name, VariableType const& variableType, double lowerBound, double upperBound, double objectiveFunctionCoefficient) override {
throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support.";
}
virtual uint_fast64_t createBinaryVariable(std::string const& name, double objectiveFunctionCoefficient) override {
throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support.";
}
virtual void addConstraint(std::string const& name, std::vector<uint_fast64_t> const& variables, std::vector<double> const& coefficients, BoundType const& boundType, double rightHandSideValue) override {
throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support.";
}
virtual void setModelSense(ModelSense const& newModelSense) {
throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support.";
}
virtual void optimize() const override {
throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support.";
}
virtual int_fast64_t getIntegerValue(uint_fast64_t variableIndex) const override {
throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support.";
}
virtual bool getBinaryValue(uint_fast64_t variableIndex) const override {
throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support.";
}
virtual double getContinuousValue(uint_fast64_t variableIndex) const override {
throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support.";
}
virtual void writeModelToFile(std::string const& filename) const override {
throw storm::exceptions::NotImplementedException() << "This version of StoRM was compiled without support for glpk. Yet, a method was called that requires this support. Please choose a version of support with glpk support.";
}
};
#endif
}
}
#endif /* STORM_SOLVER_GLPKLPSOLVER_H_ */

47
src/solver/GmmxxNondeterministicLinearEquationSolver.h

@ -47,42 +47,43 @@ namespace storm {
delete gmmxxMatrix;
}
virtual void solveEquationSystem(bool minimize, storm::storage::SparseMatrix<Type> const& A, std::vector<Type>& x, std::vector<Type> const& b, std::vector<uint_fast64_t> const& nondeterministicChoiceIndices) const override {
// Get the settings object to customize solving.
storm::settings::Settings* s = storm::settings::Settings::getInstance();
// Get relevant user-defined settings for solving the equations.
double precision = s->getOptionByLongName("precision").getArgument(0).getValueAsDouble();
uint_fast64_t maxIterations = s->getOptionByLongName("maxIterations").getArgument(0).getValueAsUnsignedInteger();
bool relative = s->getOptionByLongName("relative").getArgument(0).getValueAsBoolean();
virtual void solveEquationSystem(bool minimize, storm::storage::SparseMatrix<Type> const& A, std::vector<Type>& x, std::vector<Type> const& b, std::vector<uint_fast64_t> const& nondeterministicChoiceIndices, std::vector<Type>* multiplyResult = nullptr, std::vector<Type>* newX = nullptr) const override {
// Transform the transition probability matrix to the gmm++ format to use its arithmetic.
gmm::csr_matrix<Type>* gmmxxMatrix = storm::adapters::GmmxxAdapter::toGmmxxSparseMatrix<Type>(A);
// Set up the environment for the power method.
std::vector<Type> multiplyResult(A.getRowCount());
bool multiplyResultMemoryProvided = true;
if (multiplyResult == nullptr) {
multiplyResult = new std::vector<Type>(A.getRowCount());
multiplyResultMemoryProvided = false;
}
std::vector<Type>* currentX = &x;
std::vector<Type>* newX = new std::vector<Type>(x.size());
bool xMemoryProvided = true;
if (newX == nullptr) {
newX = new std::vector<Type>(x.size());
xMemoryProvided = false;
}
std::vector<Type>* swap = nullptr;
uint_fast64_t iterations = 0;
bool converged = false;
// Proceed with the iterations as long as the method did not converge or reach the user-specified maximum number
// of iterations.
while (!converged && iterations < maxIterations) {
while (!converged && iterations < this->maxIterations) {
// Compute x' = A*x + b.
gmm::mult(*gmmxxMatrix, *currentX, multiplyResult);
gmm::add(b, multiplyResult);
gmm::mult(*gmmxxMatrix, *currentX, *multiplyResult);
gmm::add(b, *multiplyResult);
// Reduce the vector x' by applying min/max for all non-deterministic choices.
if (minimize) {
storm::utility::vector::reduceVectorMin(multiplyResult, *newX, nondeterministicChoiceIndices);
storm::utility::vector::reduceVectorMin(*multiplyResult, *newX, nondeterministicChoiceIndices);
} else {
storm::utility::vector::reduceVectorMax(multiplyResult, *newX, nondeterministicChoiceIndices);
storm::utility::vector::reduceVectorMax(*multiplyResult, *newX, nondeterministicChoiceIndices);
}
// Determine whether the method converged.
converged = storm::utility::vector::equalModuloPrecision(*currentX, *newX, precision, relative);
converged = storm::utility::vector::equalModuloPrecision(*currentX, *newX, this->precision, this->relative);
// Update environment variables.
swap = currentX;
@ -95,12 +96,18 @@ namespace storm {
// is currently stored in currentX, but x is the output vector.
if (iterations % 2 == 1) {
std::swap(x, *currentX);
delete currentX;
} else {
if (!xMemoryProvided) {
delete currentX;
}
} else if (!xMemoryProvided) {
delete newX;
}
delete gmmxxMatrix;
if (!multiplyResultMemoryProvided) {
delete multiplyResult;
}
// Check if the solver converged and issue a warning otherwise.
if (converged) {
LOG4CPLUS_INFO(logger, "Iterative solver converged after " << iterations << " iterations.");

1
src/solver/GurobiLpSolver.cpp

@ -233,7 +233,6 @@ namespace storm {
throw storm::exceptions::InvalidStateException() << "Unable to get Gurobi solution (" << GRBgeterrormsg(env) << ").";
}
bool returnValue = false;
if (std::abs(value - 1) <= storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()) {
// Nothing to do in this case.
} else if (std::abs(value) > storm::settings::Settings::getInstance()->getOptionByLongName("precision").getArgument(0).getValueAsDouble()) {

4
src/storage/BitVector.h

@ -675,8 +675,8 @@ public:
/*!
* Retrieves the number of bits this bit vector can store.
*/
uint_fast64_t getSize() const {
return bitCount;
size_t size() const {
return static_cast<size_t>(bitCount);
}
/*!

73
src/storage/SparseMatrix.cpp

@ -5,6 +5,8 @@
* Author: Manuel Sascha Weiand
*/
#include <iomanip>
#include "src/storage/SparseMatrix.h"
namespace storm {
@ -540,64 +542,97 @@ namespace storage {
}
template<typename T>
SparseMatrix<T> SparseMatrix<T>::getSubmatrix(storm::storage::BitVector const& rowGroupConstraint, std::vector<uint_fast64_t> const& rowGroupIndices) const {
SparseMatrix<T> SparseMatrix<T>::getSubmatrix(storm::storage::BitVector const& rowGroupConstraint, std::vector<uint_fast64_t> const& rowGroupIndices, bool insertDiagonalEntries) const {
return getSubmatrix(rowGroupConstraint, rowGroupConstraint, rowGroupIndices, insertDiagonalEntries);
}
template<typename T>
SparseMatrix<T> SparseMatrix<T>::getSubmatrix(storm::storage::BitVector const& rowGroupConstraint, storm::storage::BitVector const& columnConstraint, std::vector<uint_fast64_t> const& rowGroupIndices, bool insertDiagonalEntries) const {
LOG4CPLUS_DEBUG(logger, "Creating a sub-matrix (of unknown size).");
// First, we need to determine the number of non-zero entries and the number of rows of the sub-matrix.
uint_fast64_t subNonZeroEntries = 0;
uint_fast64_t subRowCount = 0;
for (auto index : rowGroupConstraint) {
subRowCount += rowGroupIndices[index + 1] - rowGroupIndices[index];
for (uint_fast64_t i = rowGroupIndices[index]; i < rowGroupIndices[index + 1]; ++i) {
bool foundDiagonalElement = false;
for (uint_fast64_t j = rowIndications[i]; j < rowIndications[i + 1]; ++j) {
if (rowGroupConstraint.get(columnIndications[j])) {
if (columnConstraint.get(columnIndications[j])) {
++subNonZeroEntries;
if (index == columnIndications[j]) {
foundDiagonalElement = true;
}
}
}
if (insertDiagonalEntries && !foundDiagonalElement) {
++subNonZeroEntries;
}
}
}
LOG4CPLUS_DEBUG(logger, "Determined size of submatrix to be " << subRowCount << "x" << rowGroupConstraint.getNumberOfSetBits() << ".");
// Create and initialize resulting matrix.
SparseMatrix result(subRowCount, rowGroupConstraint.getNumberOfSetBits());
SparseMatrix result(subRowCount, columnConstraint.getNumberOfSetBits());
result.initialize(subNonZeroEntries);
// Create 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<uint_fast64_t> bitsSetBeforeIndex;
bitsSetBeforeIndex.reserve(colCount);
// Compute the information to fill this vector.
uint_fast64_t lastIndex = 0;
uint_fast64_t currentNumberOfSetBits = 0;
for (auto index : rowGroupConstraint) {
// If we are requested to add missing diagonal entries, we need to make sure the corresponding rows
storm::storage::BitVector columnBitCountConstraint = columnConstraint;
if (insertDiagonalEntries) {
columnBitCountConstraint |= rowGroupConstraint;
}
for (auto index : columnBitCountConstraint) {
while (lastIndex <= index) {
bitsSetBeforeIndex.push_back(currentNumberOfSetBits);
++lastIndex;
}
++currentNumberOfSetBits;
}
// Copy over selected entries.
uint_fast64_t rowCount = 0;
for (auto index : rowGroupConstraint) {
for (uint_fast64_t i = rowGroupIndices[index]; i < rowGroupIndices[index + 1]; ++i) {
bool insertedDiagonalElement = false;
for (uint_fast64_t j = rowIndications[i]; j < rowIndications[i + 1]; ++j) {
if (rowGroupConstraint.get(columnIndications[j])) {
if (columnConstraint.get(columnIndications[j])) {
if (index == columnIndications[j]) {
insertedDiagonalElement = true;
} else if (insertDiagonalEntries && !insertedDiagonalElement && columnIndications[j] > index) {
result.addNextValue(rowCount, bitsSetBeforeIndex[index], storm::utility::constGetZero<T>());
insertedDiagonalElement = true;
}
result.addNextValue(rowCount, bitsSetBeforeIndex[columnIndications[j]], valueStorage[j]);
}
}
if (insertDiagonalEntries && !insertedDiagonalElement) {
result.addNextValue(rowCount, bitsSetBeforeIndex[index], storm::utility::constGetZero<T>());
}
++rowCount;
}
}
// Finalize sub-matrix and return result.
result.finalize();
LOG4CPLUS_DEBUG(logger, "Done creating sub-matrix.");
return result;
}
template<typename T>
SparseMatrix<T> SparseMatrix<T>::getSubmatrix(std::vector<uint_fast64_t> const& rowGroupToRowIndexMapping, std::vector<uint_fast64_t> const& rowGroupIndices, bool insertDiagonalEntries) const {
LOG4CPLUS_DEBUG(logger, "Creating a sub-matrix (of unknown size).");
@ -850,6 +885,16 @@ namespace storage {
typename SparseMatrix<T>::Rows SparseMatrix<T>::getRows(uint_fast64_t startRow, uint_fast64_t endRow) const {
return Rows(this->valueStorage.data() + this->rowIndications[startRow], this->columnIndications.data() + this->rowIndications[startRow], this->rowIndications[endRow + 1] - this->rowIndications[startRow]);
}
template<typename T>
typename SparseMatrix<T>::MutableRows SparseMatrix<T>::getMutableRows(uint_fast64_t startRow, uint_fast64_t endRow) {
return MutableRows(this->valueStorage.data() + this->rowIndications[startRow], this->columnIndications.data() + this->rowIndications[startRow], this->rowIndications[endRow + 1] - this->rowIndications[startRow]);
}
template<typename T>
typename SparseMatrix<T>::MutableRows SparseMatrix<T>::getMutableRow(uint_fast64_t row) {
return getMutableRows(row, row);
}
template<typename T>
typename SparseMatrix<T>::Rows SparseMatrix<T>::getRow(uint_fast64_t row) const {
@ -989,7 +1034,7 @@ namespace storage {
uint_fast64_t currentRealIndex = 0;
while (currentRealIndex < colCount) {
if (nextIndex < rowIndications[i + 1] && currentRealIndex == columnIndications[nextIndex]) {
result << valueStorage[nextIndex] << "\t";
result << std::setprecision(8) << valueStorage[nextIndex] << "\t";
++nextIndex;
} else {
result << "0\t";

160
src/storage/SparseMatrix.h

@ -15,6 +15,7 @@
#include <new>
#include <algorithm>
#include <iostream>
#include <iomanip>
#include <iterator>
#include <set>
#include <cstdint>
@ -154,6 +155,89 @@ public:
uint_fast64_t const* columnPtr;
};
/*!
* A class representing an iterator over a continuous number of rows of the matrix.
*/
class Iterator {
public:
/*!
* Constructs an iterator from the given parameters.
*
* @param valuePtr A pointer to the value of the first element that is to be iterated over.
* @param columnPtr A pointer to the column of the first element that is to be iterated over.
*/
Iterator(T* valuePtr, uint_fast64_t* columnPtr) : valuePtr(valuePtr), columnPtr(columnPtr) {
// Intentionally left empty.
}
/*!
* Moves the iterator to the next non-zero element.
*
* @return A reference to itself.
*/
Iterator& operator++() {
++valuePtr;
++columnPtr;
return *this;
}
/*!
* Dereferences the iterator by returning a reference to itself. This is needed for making use of the range-based
* for loop over transitions.
*
* @return A reference to itself.
*/
Iterator& operator*() {
return *this;
}
/*!
* Compares the two iterators for inequality.
*
* @return True iff the given iterator points to a different index as the current iterator.
*/
bool operator!=(Iterator const& other) const {
return this->valuePtr != other.valuePtr;
}
/*!
* Assigns the position of the given iterator to the current iterator.
*
* @return A reference to itself.
*/
Iterator& operator=(Iterator const& other) {
this->valuePtr = other.valuePtr;
this->columnPtr = other.columnPtr;
return *this;
}
/*!
* Retrieves the column that is associated with the current non-zero element to which this iterator
* points.
*
* @return The column of the current non-zero element to which this iterator points.
*/
uint_fast64_t column() {
return *columnPtr;
}
/*!
* Retrieves the value of the current non-zero element to which this iterator points.
*
* @return The value of the current non-zero element to which this iterator points.
*/
T& value() {
return *valuePtr;
}
private:
// A pointer to the value of the current non-zero element.
T* valuePtr;
// A pointer to the column of the current non-zero element.
uint_fast64_t* columnPtr;
};
/*!
* This class represents a number of consecutive rows of the matrix.
*/
@ -192,6 +276,51 @@ public:
// The number of non-zero entries in the rows.
uint_fast64_t entryCount;
};
/*!
* This class represents a number of consecutive rows of the matrix.
*/
class MutableRows {
public:
/*!
* Constructs a row from the given parameters.
*
* @param valuePtr A pointer to the value of the first non-zero element of the rows.
* @param columnPtr A pointer to the column of the first non-zero element of the rows.
* @param entryCount The number of non-zero elements of the rows.
*/
MutableRows(T* valuePtr, uint_fast64_t* columnPtr, uint_fast64_t entryCount) : valuePtr(valuePtr), columnPtr(columnPtr), entryCount(entryCount) {
// Intentionally left empty.
}
/*!
* Retrieves an iterator that points to the beginning of the rows.
*
* @return An iterator that points to the beginning of the rows.
*/
Iterator begin() {
return Iterator(valuePtr, columnPtr);
}
/*!
* Retrieves an iterator that points past the last element of the rows.
*
* @return An iterator that points past the last element of the rows.
*/
Iterator end() {
return Iterator(valuePtr + entryCount, columnPtr + entryCount);
}
private:
// The pointer to the value of the first element.
T* valuePtr;
// The pointer to the column of the first element.
uint_fast64_t* columnPtr;
// The number of non-zero entries in the rows.
uint_fast64_t entryCount;
};
/*!
* This class represents an iterator to all rows of the matrix.
@ -528,7 +657,19 @@ public:
* @returns A matrix corresponding to a submatrix of the current matrix in which only row groups
* and columns given by the row group constraint are kept and all others are dropped.
*/
SparseMatrix getSubmatrix(storm::storage::BitVector const& rowGroupConstraint, std::vector<uint_fast64_t> const& rowGroupIndices) const;
SparseMatrix getSubmatrix(storm::storage::BitVector const& rowGroupConstraint, std::vector<uint_fast64_t> const& rowGroupIndices, bool insertDiagonalEntries = false) const;
/*!
* Creates a submatrix of the current matrix by keeping only row groups and columns in the given
* row group and column constraint, respectively.
*
* @param rowGroupConstraint A bit vector indicating which row groups to keep.
* @param columnConstraint A bit vector indicating which columns to keep.
* @param rowGroupIndices A vector indicating which rows belong to a given row group.
* @returns A matrix corresponding to a submatrix of the current matrix in which only row groups
* and columns given by the row group constraint are kept and all others are dropped.
*/
SparseMatrix getSubmatrix(storm::storage::BitVector const& rowGroupConstraint, storm::storage::BitVector const& columnConstraint, std::vector<uint_fast64_t> const& rowGroupIndices, bool insertDiagonalEntries = false) const;
SparseMatrix getSubmatrix(std::vector<uint_fast64_t> const& rowGroupToRowIndexMapping, std::vector<uint_fast64_t> const& rowGroupIndices, bool insertDiagonalEntries = true) const;
@ -611,6 +752,23 @@ public:
*/
Rows getRow(uint_fast64_t row) const;
/*!
* Returns an object representing the consecutive rows given by the parameters.
*
* @param startRow The starting row.
* @param endRow The ending row (which is included in the result).
* @return An object representing the consecutive rows given by the parameters.
*/
MutableRows getMutableRows(uint_fast64_t startRow, uint_fast64_t endRow);
/*!
* Returns an object representing the given row.
*
* @param row The chosen row.
* @return An object representing the given row.
*/
MutableRows getMutableRow(uint_fast64_t row);
/*!
* Returns a const iterator to the rows of the matrix.
*

25
src/storm.cpp

@ -231,18 +231,7 @@ storm::modelchecker::prctl::AbstractModelChecker<double>* createPrctlModelChecke
*/
storm::modelchecker::prctl::AbstractModelChecker<double>* createPrctlModelChecker(storm::models::Mdp<double>& mdp) {
// Create the appropriate model checker.
storm::settings::Settings* s = storm::settings::Settings::getInstance();
std::string const chosenMatrixLibrary = s->getOptionByLongName("matrixLibrary").getArgument(0).getValueAsString();
if (chosenMatrixLibrary == "gmm++") {
return new storm::modelchecker::prctl::SparseMdpPrctlModelChecker<double>(mdp, new storm::solver::GmmxxNondeterministicLinearEquationSolver<double>());
} else if (chosenMatrixLibrary == "native") {
return new storm::modelchecker::prctl::SparseMdpPrctlModelChecker<double>(mdp, new storm::solver::AbstractNondeterministicLinearEquationSolver<double>());
}
// The control flow should never reach this point, as there is a default setting for matrixlib.
std::string message = "No matrix library suitable for MDP model checking has been set.";
throw storm::exceptions::InvalidSettingsException() << message;
return nullptr;
return new storm::modelchecker::prctl::SparseMdpPrctlModelChecker<double>(mdp);
}
/*!
@ -473,11 +462,13 @@ int main(const int argc, const char* argv[]) {
LOG4CPLUS_INFO(logger, "Model is a Markov automaton.");
std::shared_ptr<storm::models::MarkovAutomaton<double>> markovAutomaton = parser.getModel<storm::models::MarkovAutomaton<double>>();
markovAutomaton->close();
storm::modelchecker::csl::SparseMarkovAutomatonCslModelChecker<double> mc(*markovAutomaton, new storm::solver::AbstractNondeterministicLinearEquationSolver<double>());
std::cout << mc.checkExpectedTime(true, markovAutomaton->getLabeledStates("goal")) << std::endl;
std::cout << mc.checkExpectedTime(false, markovAutomaton->getLabeledStates("goal")) << std::endl;
std::cout << mc.checkLongRunAverage(true, markovAutomaton->getLabeledStates("goal")) << std::endl;
std::cout << mc.checkLongRunAverage(false, markovAutomaton->getLabeledStates("goal")) << std::endl;
storm::modelchecker::csl::SparseMarkovAutomatonCslModelChecker<double> mc(*markovAutomaton, std::shared_ptr<storm::solver::AbstractNondeterministicLinearEquationSolver<double>>(new storm::solver::AbstractNondeterministicLinearEquationSolver<double>()));
// std::cout << mc.checkExpectedTime(true, markovAutomaton->getLabeledStates("goal")) << std::endl;
// std::cout << mc.checkExpectedTime(false, markovAutomaton->getLabeledStates("goal")) << std::endl;
// std::cout << mc.checkLongRunAverage(true, markovAutomaton->getLabeledStates("goal")) << std::endl;
// std::cout << mc.checkLongRunAverage(false, markovAutomaton->getLabeledStates("goal")) << std::endl;
// std::cout << mc.checkTimeBoundedEventually(true, markovAutomaton->getLabeledStates("goal"), 0, 1) << std::endl;
std::cout << mc.checkTimeBoundedEventually(true, markovAutomaton->getLabeledStates("goal"), 1, 2) << std::endl;
break;
}
case storm::models::Unknown:

13
src/utility/StormOptions.cpp

@ -34,10 +34,15 @@ bool storm::utility::StormOptions::optionsRegistered = storm::settings::Settings
settings->addOption(storm::settings::OptionBuilder("StoRM Main", "fixDeadlocks", "", "If the model contains deadlock states, setting this option will insert self-loops for these states.").build());
std::vector<std::string> matrixLibrarys;
matrixLibrarys.push_back("gmm++");
matrixLibrarys.push_back("native");
settings->addOption(storm::settings::OptionBuilder("StoRM Main", "matrixLibrary", "m", "Sets which matrix library is preferred for numerical operations.").addArgument(storm::settings::ArgumentBuilder::createStringArgument("matrixLibraryName", "The name of a matrix library. Valid values are gmm++ and native.").addValidationFunctionString(storm::settings::ArgumentValidators::stringInListValidator(matrixLibrarys)).setDefaultValueString("gmm++").build()).build());
std::vector<std::string> matrixLibraries;
matrixLibraries.push_back("gmm++");
matrixLibraries.push_back("native");
settings->addOption(storm::settings::OptionBuilder("StoRM Main", "matrixLibrary", "m", "Sets which matrix library is preferred for numerical operations.").addArgument(storm::settings::ArgumentBuilder::createStringArgument("matrixLibraryName", "The name of a matrix library. Valid values are gmm++ and native.").addValidationFunctionString(storm::settings::ArgumentValidators::stringInListValidator(matrixLibraries)).setDefaultValueString("gmm++").build()).build());
std::vector<std::string> lpSolvers;
lpSolvers.push_back("gurobi");
lpSolvers.push_back("glpk");
settings->addOption(storm::settings::OptionBuilder("StoRM Main", "lpSolver", "", "Sets which LP solver is preferred.").addArgument(storm::settings::ArgumentBuilder::createStringArgument("LP solver name", "The name of an available LP solver. Valid values are gurobi and glpk.").addValidationFunctionString(storm::settings::ArgumentValidators::stringInListValidator(lpSolvers)).setDefaultValueString("glpk").build()).build());
return true;
});

102
src/utility/graph.h

@ -177,9 +177,11 @@ namespace storm {
* @return A bit vector that represents all states with probability 0.
*/
template <typename T>
storm::storage::BitVector performProbGreater0E(storm::models::AbstractNondeterministicModel<T> const& model, storm::storage::SparseMatrix<T> const& backwardTransitions, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, bool useStepBound = false, uint_fast64_t maximalSteps = 0) {
storm::storage::BitVector performProbGreater0E(storm::storage::SparseMatrix<T> const& transitionMatrix, std::vector<uint_fast64_t> const& nondeterministicChoiceIndices, storm::storage::SparseMatrix<T> const& backwardTransitions, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, bool useStepBound = false, uint_fast64_t maximalSteps = 0) {
size_t numberOfStates = phiStates.size();
// Prepare resulting bit vector.
storm::storage::BitVector statesWithProbabilityGreater0(model.getNumberOfStates());
storm::storage::BitVector statesWithProbabilityGreater0(numberOfStates);
// Add all psi states as the already satisfy the condition.
statesWithProbabilityGreater0 |= psiStates;
@ -191,9 +193,9 @@ namespace storm {
std::vector<uint_fast64_t> stepStack;
std::vector<uint_fast64_t> remainingSteps;
if (useStepBound) {
stepStack.reserve(model.getNumberOfStates());
stepStack.reserve(numberOfStates);
stepStack.insert(stepStack.begin(), psiStates.getNumberOfSetBits(), maximalSteps);
remainingSteps.resize(model.getNumberOfStates());
remainingSteps.resize(numberOfStates);
for (auto state : psiStates) {
remainingSteps[state] = maximalSteps;
}
@ -230,6 +232,13 @@ namespace storm {
return statesWithProbabilityGreater0;
}
template <typename T>
storm::storage::BitVector performProb0A(storm::storage::SparseMatrix<T> const& transitionMatrix, std::vector<uint_fast64_t> const& nondeterministicChoiceIndices, storm::storage::SparseMatrix<T> const& backwardTransitions, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates) {
storm::storage::BitVector statesWithProbability0 = performProbGreater0E(transitionMatrix, nondeterministicChoiceIndices, backwardTransitions, phiStates, psiStates);
statesWithProbability0.complement();
return statesWithProbability0;
}
/*!
* Computes the sets of states that have probability 0 of satisfying phi until psi under all
* possible resolutions of non-determinism in a non-deterministic model. Stated differently,
@ -246,11 +255,9 @@ namespace storm {
*/
template <typename T>
storm::storage::BitVector performProb0A(storm::models::AbstractNondeterministicModel<T> const& model, storm::storage::SparseMatrix<T> const& backwardTransitions, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates) {
storm::storage::BitVector statesWithProbability0 = performProbGreater0E(model, backwardTransitions, phiStates, psiStates);
statesWithProbability0.complement();
return statesWithProbability0;
return performProb0A(model.getTransitionMatrix(), model.getNondeterministicChoiceIndices(), backwardTransitions, phiStates, psiStates);
}
/*!
* Computes the sets of states that have probability 1 of satisfying phi until psi under at least
* one possible resolution of non-determinism in a non-deterministic model. Stated differently,
@ -264,15 +271,13 @@ namespace storm {
* @return A bit vector that represents all states with probability 1.
*/
template <typename T>
storm::storage::BitVector performProb1E(storm::models::AbstractNondeterministicModel<T> const& model, storm::storage::SparseMatrix<T> const& backwardTransitions, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates) {
// Get some temporaries for convenience.
storm::storage::SparseMatrix<T> const& transitionMatrix = model.getTransitionMatrix();
std::vector<uint_fast64_t> const& nondeterministicChoiceIndices = model.getNondeterministicChoiceIndices();
storm::storage::BitVector performProb1E(storm::storage::SparseMatrix<T> const& transitionMatrix, std::vector<uint_fast64_t> const& nondeterministicChoiceIndices, storm::storage::SparseMatrix<T> const& backwardTransitions, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates) {
size_t numberOfStates = phiStates.size();
// Initialize the environment for the iterative algorithm.
storm::storage::BitVector currentStates(model.getNumberOfStates(), true);
storm::storage::BitVector currentStates(numberOfStates, true);
std::vector<uint_fast64_t> stack;
stack.reserve(model.getNumberOfStates());
stack.reserve(numberOfStates);
// Perform the loop as long as the set of states gets larger.
bool done = false;
@ -323,6 +328,15 @@ namespace storm {
return currentStates;
}
template <typename T>
std::pair<storm::storage::BitVector, storm::storage::BitVector> performProb01Max(storm::storage::SparseMatrix<T> const& transitionMatrix, std::vector<uint_fast64_t> const& nondeterministicChoiceIndices, storm::storage::SparseMatrix<T> const& backwardTransitions, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates) {
std::pair<storm::storage::BitVector, storm::storage::BitVector> result;
result.first = performProb0A(transitionMatrix, nondeterministicChoiceIndices, backwardTransitions, phiStates, psiStates);
result.second = performProb1E(transitionMatrix, nondeterministicChoiceIndices, backwardTransitions, phiStates, psiStates);
return result;
}
/*!
* Computes the sets of states that have probability 0 or 1, respectively, of satisfying phi
* until psi in a non-deterministic model in which all non-deterministic choices are resolved
@ -335,14 +349,7 @@ namespace storm {
*/
template <typename T>
std::pair<storm::storage::BitVector, storm::storage::BitVector> performProb01Max(storm::models::AbstractNondeterministicModel<T> const& model, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates) {
std::pair<storm::storage::BitVector, storm::storage::BitVector> result;
// Get the backwards transition relation from the model to ease the search.
storm::storage::SparseMatrix<T> backwardTransitions = model.getBackwardTransitions();
result.first = performProb0A(model, backwardTransitions, phiStates, psiStates);
result.second = performProb1E(model, backwardTransitions, phiStates, psiStates);
return result;
return performProb01Max(model.getTransitionMatrix(), model.getNondeterministicChoiceIndices(), model.getBackwardTransitions(), phiStates, psiStates);
}
/*!
@ -360,13 +367,11 @@ namespace storm {
* @return A bit vector that represents all states with probability 0.
*/
template <typename T>
storm::storage::BitVector performProbGreater0A(storm::models::AbstractNondeterministicModel<T> const& model, storm::storage::SparseMatrix<T> const& backwardTransitions, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, bool useStepBound = false, uint_fast64_t maximalSteps = 0) {
// Prepare resulting bit vector.
storm::storage::BitVector statesWithProbabilityGreater0(model.getNumberOfStates());
storm::storage::BitVector performProbGreater0A(storm::storage::SparseMatrix<T> const& transitionMatrix, std::vector<uint_fast64_t> const& nondeterministicChoiceIndices, storm::storage::SparseMatrix<T> const& backwardTransitions, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates, bool useStepBound = false, uint_fast64_t maximalSteps = 0) {
size_t numberOfStates = phiStates.size();
// Get some temporaries for convenience.
storm::storage::SparseMatrix<T> const& transitionMatrix = model.getTransitionMatrix();
std::vector<uint_fast64_t> const& nondeterministicChoiceIndices = model.getNondeterministicChoiceIndices();
// Prepare resulting bit vector.
storm::storage::BitVector statesWithProbabilityGreater0(numberOfStates);
// Add all psi states as the already satisfy the condition.
statesWithProbabilityGreater0 |= psiStates;
@ -378,9 +383,9 @@ namespace storm {
std::vector<uint_fast64_t> stepStack;
std::vector<uint_fast64_t> remainingSteps;
if (useStepBound) {
stepStack.reserve(model.getNumberOfStates());
stepStack.reserve(numberOfStates);
stepStack.insert(stepStack.begin(), psiStates.getNumberOfSetBits(), maximalSteps);
remainingSteps.resize(model.getNumberOfStates());
remainingSteps.resize(numberOfStates);
for (auto state : psiStates) {
remainingSteps[state] = maximalSteps;
}
@ -452,7 +457,14 @@ namespace storm {
*/
template <typename T>
storm::storage::BitVector performProb0E(storm::models::AbstractNondeterministicModel<T> const& model, storm::storage::SparseMatrix<T> const& backwardTransitions, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates) {
storm::storage::BitVector statesWithProbability0 = performProbGreater0A(model, backwardTransitions, phiStates, psiStates);
storm::storage::BitVector statesWithProbability0 = performProbGreater0A(model.getTransitionMatrix(), model.getNondeterministicChoiceIndices(), backwardTransitions, phiStates, psiStates);
statesWithProbability0.complement();
return statesWithProbability0;
}
template <typename T>
storm::storage::BitVector performProb0E(storm::storage::SparseMatrix<T> const& transitionMatrix, std::vector<uint_fast64_t> const& nondeterministicChoiceIndices, storm::storage::SparseMatrix<T> const& backwardTransitions, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates) {
storm::storage::BitVector statesWithProbability0 = performProbGreater0A(transitionMatrix, nondeterministicChoiceIndices, backwardTransitions, phiStates, psiStates);
statesWithProbability0.complement();
return statesWithProbability0;
}
@ -470,15 +482,13 @@ namespace storm {
* @return A bit vector that represents all states with probability 0.
*/
template <typename T>
storm::storage::BitVector performProb1A(storm::models::AbstractNondeterministicModel<T> const& model, storm::storage::SparseMatrix<T> const& backwardTransitions, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates) {
// Get some temporaries for convenience.
storm::storage::SparseMatrix<T> const& transitionMatrix = model.getTransitionMatrix();
std::vector<uint_fast64_t> const& nondeterministicChoiceIndices = model.getNondeterministicChoiceIndices();
storm::storage::BitVector performProb1A( storm::storage::SparseMatrix<T> const& transitionMatrix, std::vector<uint_fast64_t> const& nondeterministicChoiceIndices, storm::storage::SparseMatrix<T> const& backwardTransitions, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates) {
size_t numberOfStates = phiStates.size();
// Initialize the environment for the iterative algorithm.
storm::storage::BitVector currentStates(model.getNumberOfStates(), true);
storm::storage::BitVector currentStates(numberOfStates, true);
std::vector<uint_fast64_t> stack;
stack.reserve(model.getNumberOfStates());
stack.reserve(numberOfStates);
// Perform the loop as long as the set of states gets smaller.
bool done = false;
@ -528,6 +538,15 @@ namespace storm {
return currentStates;
}
template <typename T>
std::pair<storm::storage::BitVector, storm::storage::BitVector> performProb01Min(storm::storage::SparseMatrix<T> const& transitionMatrix, std::vector<uint_fast64_t> const& nondeterministicChoiceIndices, storm::storage::SparseMatrix<T> const& backwardTransitions, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates) {
std::pair<storm::storage::BitVector, storm::storage::BitVector> result;
result.first = performProb0E(transitionMatrix, nondeterministicChoiceIndices, backwardTransitions, phiStates, psiStates);
result.second = performProb1A(transitionMatrix, nondeterministicChoiceIndices, backwardTransitions, phiStates, psiStates);
return result;
}
/*!
* Computes the sets of states that have probability 0 or 1, respectively, of satisfying phi
* until psi in a non-deterministic model in which all non-deterministic choices are resolved
@ -540,14 +559,7 @@ namespace storm {
*/
template <typename T>
std::pair<storm::storage::BitVector, storm::storage::BitVector> performProb01Min(storm::models::AbstractNondeterministicModel<T> const& model, storm::storage::BitVector const& phiStates, storm::storage::BitVector const& psiStates) {
std::pair<storm::storage::BitVector, storm::storage::BitVector> result;
// Get the backwards transition relation from the model to ease the search.
storm::storage::SparseMatrix<T> backwardTransitions = model.getBackwardTransitions();
result.first = performProb0E(model, backwardTransitions, phiStates, psiStates);
result.second = performProb1A(model, backwardTransitions, phiStates, psiStates);
return result;
return performProb01Min(model.getTransitionMatrix(), model.getNondeterministicChoiceIndices(), model.getBackwardTransitions(), phiStates, psiStates);
}
/*!

36
src/utility/solver.cpp

@ -0,0 +1,36 @@
#include "src/utility/solver.h"
#include "src/solver/GmmxxNondeterministicLinearEquationSolver.h"
#include "src/solver/GurobiLpSolver.h"
#include "src/solver/GlpkLpSolver.h"
namespace storm {
namespace utility {
namespace solver {
std::shared_ptr<storm::solver::LpSolver> getLpSolver(std::string const& name) {
std::string const& lpSolver = storm::settings::Settings::getInstance()->getOptionByLongName("lpSolver").getArgument(0).getValueAsString();
if (lpSolver == "gurobi") {
return std::shared_ptr<storm::solver::LpSolver>(new storm::solver::GurobiLpSolver(name));
} else if (lpSolver == "glpk") {
return std::shared_ptr<storm::solver::LpSolver>(new storm::solver::GlpkLpSolver(name));
}
throw storm::exceptions::InvalidSettingsException() << "No suitable LP-solver selected.";
}
template<typename ValueType>
std::shared_ptr<storm::solver::AbstractNondeterministicLinearEquationSolver<ValueType>> getNondeterministicLinearEquationSolver() {
std::string const& matrixLibrary = storm::settings::Settings::getInstance()->getOptionByLongName("matrixLibrary").getArgument(0).getValueAsString();
if (matrixLibrary == "gmm++") {
return std::shared_ptr<storm::solver::AbstractNondeterministicLinearEquationSolver<ValueType>>(new storm::solver::GmmxxNondeterministicLinearEquationSolver<ValueType>());
} else if (matrixLibrary == "native") {
return std::shared_ptr<storm::solver::AbstractNondeterministicLinearEquationSolver<ValueType>>(new storm::solver::AbstractNondeterministicLinearEquationSolver<ValueType>());
}
throw storm::exceptions::InvalidSettingsException() << "No suitable nondeterministic linear equation solver selected.";
}
template std::shared_ptr<storm::solver::AbstractNondeterministicLinearEquationSolver<double>> getNondeterministicLinearEquationSolver();
}
}
}

12
src/utility/solver.h

@ -1,15 +1,19 @@
#ifndef STORM_UTILITY_SOLVER_H_
#define STORM_UTILITY_SOLVER_H_
#include "src/solver/GurobiLpSolver.h"
#include "src/solver/AbstractNondeterministicLinearEquationSolver.h"
#include "src/solver/LpSolver.h"
#include "src/exceptions/InvalidSettingsException.h"
namespace storm {
namespace utility {
namespace solver {
std::unique_ptr<storm::solver::LpSolver> getLpSolver(std::string const& name) {
return std::unique_ptr<storm::solver::LpSolver>(new storm::solver::GurobiLpSolver(name));
}
std::shared_ptr<storm::solver::LpSolver> getLpSolver(std::string const& name);
template<typename ValueType>
std::shared_ptr<storm::solver::AbstractNondeterministicLinearEquationSolver<ValueType>> getNondeterministicLinearEquationSolver();
}
}
}

600
src/utility/vector.h

@ -1,10 +1,3 @@
/*
* vector.h
*
* Created on: 06.12.2012
* Author: Christian Dehnert
*/
#ifndef STORM_UTILITY_VECTOR_H_
#define STORM_UTILITY_VECTOR_H_
@ -14,295 +7,318 @@
#include <algorithm>
#include <functional>
#include "log4cplus/logger.h"
#include "log4cplus/loggingmacros.h"
extern log4cplus::Logger logger;
namespace storm {
namespace utility {
namespace vector {
/*!
* Sets the provided values at the provided positions in the given vector.
*
* @param vector The vector in which the values are to be set.
* @param positions The positions at which the values are to be set.
* @param values The values that are to be set.
*/
template<class T>
void setVectorValues(std::vector<T>& vector, storm::storage::BitVector const& positions, std::vector<T> const& values) {
uint_fast64_t oldPosition = 0;
for (auto position : positions) {
vector[position] = values[oldPosition++];
}
}
/*!
* Sets the provided value at the provided positions in the given vector.
*
* @param vector The vector in which the value is to be set.
* @param positions The positions at which the value is to be set.
* @param value The value that is to be set.
*/
template<class T>
void setVectorValues(std::vector<T>& vector, storm::storage::BitVector const& positions, T value) {
for (auto position : positions) {
vector[position] = value;
}
}
/*!
* Sets the provided value at the provided positions in the given vector.
*
* @param vector The vector in which the value is to be set.
* @param positions The positions at which the value is to be set.
* @param value The value that is to be set.
*/
template<class T>
void setVectorValues(Eigen::Matrix<T, -1, 1, 0, -1, 1>& eigenVector, storm::storage::BitVector const& positions, T value) {
for (auto position : positions) {
eigenVector(position, 0) = value;
}
}
/*!
* Selects the elements from a vector at the specified positions and writes them consecutively into another vector.
* @param vector The vector into which the selected elements are to be written.
* @param positions The positions at which to select the elements from the values vector.
* @param values The vector from which to select the elements.
*/
template<class T>
void selectVectorValues(std::vector<T>& vector, storm::storage::BitVector const& positions, std::vector<T> const& values) {
uint_fast64_t oldPosition = 0;
for (auto position : positions) {
vector[oldPosition++] = values[position];
}
}
/*!
* Selects groups of elements from a vector at the specified positions and writes them consecutively into another vector.
*
* @param vector The vector into which the selected elements are to be written.
* @param positions The positions of the groups of elements that are to be selected.
* @param rowGrouping A vector that specifies the begin and end of each group of elements in the values vector.
* @param values The vector from which to select groups of elements.
*/
template<class T>
void selectVectorValues(std::vector<T>& vector, storm::storage::BitVector const& positions, std::vector<uint_fast64_t> const& rowGrouping, std::vector<T> const& values) {
uint_fast64_t oldPosition = 0;
for (auto position : positions) {
for (uint_fast64_t i = rowGrouping[position]; i < rowGrouping[position + 1]; ++i) {
vector[oldPosition++] = values[i];
}
}
}
/*!
* Selects one element out of each row group and writes it to the target vector.
*
* @param vector The target vector to which the values are written.
* @param rowGroupToRowIndexMapping A mapping from row group indices to an offset that specifies which of the values to
* take from the row group.
* @param rowGrouping A vector that specifies the begin and end of each group of elements in the values vector.
* @param values The vector from which to select the values.
*/
template<class T>
void selectVectorValues(std::vector<T>& vector, std::vector<uint_fast64_t> const& rowGroupToRowIndexMapping, std::vector<uint_fast64_t> const& rowGrouping, std::vector<T> const& values) {
uint_fast64_t oldPosition = 0;
for (uint_fast64_t i = 0; i < vector.size(); ++i) {
vector[i] = values[rowGrouping[i] + rowGroupToRowIndexMapping[i]];
}
}
/*!
* Selects values from a vector at the specified positions and writes them into another vector as often as given by
* the size of the corresponding group of elements.
*
* @param vector The vector into which the selected elements are written.
* @param positions The positions at which to select the values.
* @param rowGrouping A vector that specifies the begin and end of each group of elements in the values vector. This
* implicitly defines the number of times any element is written to the output vector.
*/
template<class T>
void selectVectorValuesRepeatedly(std::vector<T>& vector, storm::storage::BitVector const& positions, std::vector<uint_fast64_t> const& rowGrouping, std::vector<T> const& values) {
uint_fast64_t oldPosition = 0;
for (auto position : positions) {
for (uint_fast64_t i = rowGrouping[position]; i < rowGrouping[position + 1]; ++i) {
vector[oldPosition++] = values[position];
}
}
}
/*!
* Subtracts the given vector from the constant one-vector and writes the result to the input vector.
*
* @param vector The vector that is to be subtracted from the constant one-vector.
*/
template<class T>
void subtractFromConstantOneVector(std::vector<T>& vector) {
for (auto& element : vector) {
element = storm::utility::constGetOne<T>() - element;
}
}
/*!
* Adds the two given vectors and writes the result into the first operand.
*
* @param target The first summand and target vector.
* @param summand The second summand.
*/
template<class T>
void addVectorsInPlace(std::vector<T>& target, std::vector<T> const& summand) {
if (target.size() != summand.size()) {
LOG4CPLUS_ERROR(logger, "Lengths of vectors do not match, which makes operation impossible.");
throw storm::exceptions::InvalidArgumentException() << "Length of vectors do not match, which makes operation impossible.";
}
std::transform(target.begin(), target.end(), summand.begin(), target.begin(), std::plus<T>());
}
/*!
* Reduces the given source vector by selecting an element according to the given filter out of each row group.
*
* @param source The source vector which is to be reduced.
* @param target The target vector into which a single element from each row group is written.
* @param rowGrouping A vector that specifies the begin and end of each group of elements in the values vector.
* @param filter A function that compares two elements v1 and v2 according to some filter criterion. This function must
* return true iff v1 is supposed to be taken instead of v2.
* @param choices If non-null, this vector is used to store the choices made during the selection.
*/
template<class T>
void reduceVector(std::vector<T> const& source, std::vector<T>& target, std::vector<uint_fast64_t> const& rowGrouping, std::function<bool (T const&, T const&)> filter, std::vector<uint_fast64_t>* choices = nullptr) {
uint_fast64_t currentSourceRow = 0;
uint_fast64_t currentTargetRow = -1;
uint_fast64_t currentLocalRow = 0;
for (auto it = source.cbegin(), ite = source.cend(); it != ite; ++it, ++currentSourceRow, ++currentLocalRow) {
// Check whether we have considered all source rows for the current target row.
if (rowGrouping[currentTargetRow + 1] <= currentSourceRow || currentSourceRow == 0) {
currentLocalRow = 0;
++currentTargetRow;
target[currentTargetRow] = source[currentSourceRow];
if (choices != nullptr) {
(*choices)[currentTargetRow] = 0;
namespace utility {
namespace vector {
/*!
* Sets the provided values at the provided positions in the given vector.
*
* @param vector The vector in which the values are to be set.
* @param positions The positions at which the values are to be set.
* @param values The values that are to be set.
*/
template<class T>
void setVectorValues(std::vector<T>& vector, storm::storage::BitVector const& positions, std::vector<T> const& values) {
uint_fast64_t oldPosition = 0;
for (auto position : positions) {
vector[position] = values[oldPosition++];
}
}
continue;
}
// We have to upate the value, so only overwrite the current value if the value passes the filter.
if (filter(*it, target[currentTargetRow])) {
target[currentTargetRow] = *it;
if (choices != nullptr) {
(*choices)[currentTargetRow] = currentLocalRow;
/*!
* Sets the provided value at the provided positions in the given vector.
*
* @param vector The vector in which the value is to be set.
* @param positions The positions at which the value is to be set.
* @param value The value that is to be set.
*/
template<class T>
void setVectorValues(std::vector<T>& vector, storm::storage::BitVector const& positions, T value) {
for (auto position : positions) {
vector[position] = value;
}
}
}
}
}
/*!
* Reduces the given source vector by selecting the smallest element out of each row group.
*
* @param source The source vector which is to be reduced.
* @param target The target vector into which a single element from each row group is written.
* @param rowGrouping A vector that specifies the begin and end of each group of elements in the source vector.
* @param choices If non-null, this vector is used to store the choices made during the selection.
*/
template<class T>
void reduceVectorMin(std::vector<T> const& source, std::vector<T>& target, std::vector<uint_fast64_t> const& rowGrouping, std::vector<uint_fast64_t>* choices = nullptr) {
reduceVector<T>(source, target, rowGrouping, std::less<T>(), choices);
}
/*!
* Reduces the given source vector by selecting the largest element out of each row group.
*
* @param source The source vector which is to be reduced.
* @param target The target vector into which a single element from each row group is written.
* @param rowGrouping A vector that specifies the begin and end of each group of elements in the source vector.
* @param choices If non-null, this vector is used to store the choices made during the selection.
*/
template<class T>
void reduceVectorMax(std::vector<T> const& source, std::vector<T>& target, std::vector<uint_fast64_t> const& rowGrouping, std::vector<uint_fast64_t>* choices = nullptr) {
reduceVector<T>(source, target, rowGrouping, std::greater<T>(), choices);
}
/*!
* Compares the given elements and determines whether they are equal modulo the given precision. The provided flag
* additionaly specifies whether the error is computed in relative or absolute terms.
*
* @param val1 The first value to compare.
* @param val2 The second value to compare.
* @param precision The precision up to which the elements are compared.
* @param relativeError If set, the error is computed relative to the second value.
* @return True iff the elements are considered equal.
*/
template<class T>
bool equalModuloPrecision(T const& val1, T const& val2, T precision, bool relativeError = true) {
if (relativeError) {
if (std::abs(val1 - val2)/val2 > precision) return false;
} else {
if (std::abs(val1 - val2) > precision) return false;
}
return true;
}
/*!
* Compares the two vectors and determines whether they are equal modulo the provided precision. Depending on whether the
* flag is set, the difference between the vectors is computed relative to the value or in absolute terms.
*
* @param vectorLeft The first vector of the comparison.
* @param vectorRight The second vector of the comparison.
* @param precision The precision up to which the vectors are to be checked for equality.
* @param relativeError If set, the difference between the vectors is computed relative to the value or in absolute terms.
*/
template<class T>
bool equalModuloPrecision(std::vector<T> const& vectorLeft, std::vector<T> const& vectorRight, T precision, bool relativeError) {
if (vectorLeft.size() != vectorRight.size()) {
LOG4CPLUS_ERROR(logger, "Lengths of vectors do not match, which makes comparison impossible.");
throw storm::exceptions::InvalidArgumentException() << "Lengths of vectors do not match, which makes comparison impossible.";
}
for (uint_fast64_t i = 0; i < vectorLeft.size(); ++i) {
if (!equalModuloPrecision(vectorLeft[i], vectorRight[i], precision, relativeError)) {
return false;
}
}
return true;
}
/*!
* Compares the two vectors at the specified positions and determines whether they are equal modulo the provided
* precision. Depending on whether the flag is set, the difference between the vectors is computed relative to the value
* or in absolute terms.
*
* @param vectorLeft The first vector of the comparison.
* @param vectorRight The second vector of the comparison.
* @param precision The precision up to which the vectors are to be checked for equality.
* @param positions A vector representing a set of positions at which the vectors are compared.
* @param relativeError If set, the difference between the vectors is computed relative to the value or in absolute terms.
*/
template<class T>
bool equalModuloPrecision(std::vector<T> const& vectorLeft, std::vector<T> const& vectorRight, std::vector<uint_fast64_t> const& positions, T precision, bool relativeError) {
if (vectorLeft.size() != vectorRight.size()) {
LOG4CPLUS_ERROR(logger, "Lengths of vectors do not match, which makes comparison impossible.");
throw storm::exceptions::InvalidArgumentException() << "Lengths of vectors do not match, which makes comparison impossible.";
}
for (uint_fast64_t position : positions) {
if (!equalModuloPrecision(vectorLeft[position], vectorRight[position], precision, relativeError)) {
return false;
}
}
return true;
}
} // namespace vector
} // namespace utility
/*!
* Sets the provided value at the provided positions in the given vector.
*
* @param vector The vector in which the value is to be set.
* @param positions The positions at which the value is to be set.
* @param value The value that is to be set.
*/
template<class T>
void setVectorValues(Eigen::Matrix<T, -1, 1, 0, -1, 1>& eigenVector, storm::storage::BitVector const& positions, T value) {
for (auto position : positions) {
eigenVector(position, 0) = value;
}
}
/*!
* Selects the elements from a vector at the specified positions and writes them consecutively into another vector.
* @param vector The vector into which the selected elements are to be written.
* @param positions The positions at which to select the elements from the values vector.
* @param values The vector from which to select the elements.
*/
template<class T>
void selectVectorValues(std::vector<T>& vector, storm::storage::BitVector const& positions, std::vector<T> const& values) {
uint_fast64_t oldPosition = 0;
for (auto position : positions) {
vector[oldPosition++] = values[position];
}
}
/*!
* Selects groups of elements from a vector at the specified positions and writes them consecutively into another vector.
*
* @param vector The vector into which the selected elements are to be written.
* @param positions The positions of the groups of elements that are to be selected.
* @param rowGrouping A vector that specifies the begin and end of each group of elements in the values vector.
* @param values The vector from which to select groups of elements.
*/
template<class T>
void selectVectorValues(std::vector<T>& vector, storm::storage::BitVector const& positions, std::vector<uint_fast64_t> const& rowGrouping, std::vector<T> const& values) {
uint_fast64_t oldPosition = 0;
for (auto position : positions) {
for (uint_fast64_t i = rowGrouping[position]; i < rowGrouping[position + 1]; ++i) {
vector[oldPosition++] = values[i];
}
}
}
/*!
* Selects one element out of each row group and writes it to the target vector.
*
* @param vector The target vector to which the values are written.
* @param rowGroupToRowIndexMapping A mapping from row group indices to an offset that specifies which of the values to
* take from the row group.
* @param rowGrouping A vector that specifies the begin and end of each group of elements in the values vector.
* @param values The vector from which to select the values.
*/
template<class T>
void selectVectorValues(std::vector<T>& vector, std::vector<uint_fast64_t> const& rowGroupToRowIndexMapping, std::vector<uint_fast64_t> const& rowGrouping, std::vector<T> const& values) {
uint_fast64_t oldPosition = 0;
for (uint_fast64_t i = 0; i < vector.size(); ++i) {
vector[i] = values[rowGrouping[i] + rowGroupToRowIndexMapping[i]];
}
}
/*!
* Selects values from a vector at the specified positions and writes them into another vector as often as given by
* the size of the corresponding group of elements.
*
* @param vector The vector into which the selected elements are written.
* @param positions The positions at which to select the values.
* @param rowGrouping A vector that specifies the begin and end of each group of elements in the values vector. This
* implicitly defines the number of times any element is written to the output vector.
*/
template<class T>
void selectVectorValuesRepeatedly(std::vector<T>& vector, storm::storage::BitVector const& positions, std::vector<uint_fast64_t> const& rowGrouping, std::vector<T> const& values) {
uint_fast64_t oldPosition = 0;
for (auto position : positions) {
for (uint_fast64_t i = rowGrouping[position]; i < rowGrouping[position + 1]; ++i) {
vector[oldPosition++] = values[position];
}
}
}
/*!
* Subtracts the given vector from the constant one-vector and writes the result to the input vector.
*
* @param vector The vector that is to be subtracted from the constant one-vector.
*/
template<class T>
void subtractFromConstantOneVector(std::vector<T>& vector) {
for (auto& element : vector) {
element = storm::utility::constGetOne<T>() - element;
}
}
/*!
* Adds the two given vectors and writes the result into the first operand.
*
* @param target The first summand and target vector.
* @param summand The second summand.
*/
template<class T>
void addVectorsInPlace(std::vector<T>& target, std::vector<T> const& summand) {
if (target.size() != summand.size()) {
LOG4CPLUS_ERROR(logger, "Lengths of vectors do not match, which makes operation impossible.");
throw storm::exceptions::InvalidArgumentException() << "Length of vectors do not match, which makes operation impossible.";
}
std::transform(target.begin(), target.end(), summand.begin(), target.begin(), std::plus<T>());
}
/*!
* Reduces the given source vector by selecting an element according to the given filter out of each row group.
*
* @param source The source vector which is to be reduced.
* @param target The target vector into which a single element from each row group is written.
* @param rowGrouping A vector that specifies the begin and end of each group of elements in the values vector.
* @param filter A function that compares two elements v1 and v2 according to some filter criterion. This function must
* return true iff v1 is supposed to be taken instead of v2.
* @param choices If non-null, this vector is used to store the choices made during the selection.
*/
template<class T>
void reduceVector(std::vector<T> const& source, std::vector<T>& target, std::vector<uint_fast64_t> const& rowGrouping, std::function<bool (T const&, T const&)> filter, std::vector<uint_fast64_t>* choices = nullptr) {
uint_fast64_t currentSourceRow = 0;
uint_fast64_t currentTargetRow = -1;
uint_fast64_t currentLocalRow = 0;
for (auto it = source.cbegin(), ite = source.cend(); it != ite; ++it, ++currentSourceRow, ++currentLocalRow) {
// Check whether we have considered all source rows for the current target row.
if (rowGrouping[currentTargetRow + 1] <= currentSourceRow || currentSourceRow == 0) {
currentLocalRow = 0;
++currentTargetRow;
target[currentTargetRow] = source[currentSourceRow];
if (choices != nullptr) {
(*choices)[currentTargetRow] = 0;
}
continue;
}
// We have to upate the value, so only overwrite the current value if the value passes the filter.
if (filter(*it, target[currentTargetRow])) {
target[currentTargetRow] = *it;
if (choices != nullptr) {
(*choices)[currentTargetRow] = currentLocalRow;
}
}
}
}
/*!
* Reduces the given source vector by selecting the smallest element out of each row group.
*
* @param source The source vector which is to be reduced.
* @param target The target vector into which a single element from each row group is written.
* @param rowGrouping A vector that specifies the begin and end of each group of elements in the source vector.
* @param choices If non-null, this vector is used to store the choices made during the selection.
*/
template<class T>
void reduceVectorMin(std::vector<T> const& source, std::vector<T>& target, std::vector<uint_fast64_t> const& rowGrouping, std::vector<uint_fast64_t>* choices = nullptr) {
reduceVector<T>(source, target, rowGrouping, std::less<T>(), choices);
}
/*!
* Reduces the given source vector by selecting the largest element out of each row group.
*
* @param source The source vector which is to be reduced.
* @param target The target vector into which a single element from each row group is written.
* @param rowGrouping A vector that specifies the begin and end of each group of elements in the source vector.
* @param choices If non-null, this vector is used to store the choices made during the selection.
*/
template<class T>
void reduceVectorMax(std::vector<T> const& source, std::vector<T>& target, std::vector<uint_fast64_t> const& rowGrouping, std::vector<uint_fast64_t>* choices = nullptr) {
reduceVector<T>(source, target, rowGrouping, std::greater<T>(), choices);
}
/*!
* Compares the given elements and determines whether they are equal modulo the given precision. The provided flag
* additionaly specifies whether the error is computed in relative or absolute terms.
*
* @param val1 The first value to compare.
* @param val2 The second value to compare.
* @param precision The precision up to which the elements are compared.
* @param relativeError If set, the error is computed relative to the second value.
* @return True iff the elements are considered equal.
*/
template<class T>
bool equalModuloPrecision(T const& val1, T const& val2, T precision, bool relativeError = true) {
if (relativeError) {
if (std::abs(val1 - val2)/val2 > precision) return false;
} else {
if (std::abs(val1 - val2) > precision) return false;
}
return true;
}
/*!
* Compares the two vectors and determines whether they are equal modulo the provided precision. Depending on whether the
* flag is set, the difference between the vectors is computed relative to the value or in absolute terms.
*
* @param vectorLeft The first vector of the comparison.
* @param vectorRight The second vector of the comparison.
* @param precision The precision up to which the vectors are to be checked for equality.
* @param relativeError If set, the difference between the vectors is computed relative to the value or in absolute terms.
*/
template<class T>
bool equalModuloPrecision(std::vector<T> const& vectorLeft, std::vector<T> const& vectorRight, T precision, bool relativeError) {
if (vectorLeft.size() != vectorRight.size()) {
LOG4CPLUS_ERROR(logger, "Lengths of vectors do not match, which makes comparison impossible.");
throw storm::exceptions::InvalidArgumentException() << "Lengths of vectors do not match, which makes comparison impossible.";
}
for (uint_fast64_t i = 0; i < vectorLeft.size(); ++i) {
if (!equalModuloPrecision(vectorLeft[i], vectorRight[i], precision, relativeError)) {
return false;
}
}
return true;
}
/*!
* Compares the two vectors at the specified positions and determines whether they are equal modulo the provided
* precision. Depending on whether the flag is set, the difference between the vectors is computed relative to the value
* or in absolute terms.
*
* @param vectorLeft The first vector of the comparison.
* @param vectorRight The second vector of the comparison.
* @param precision The precision up to which the vectors are to be checked for equality.
* @param positions A vector representing a set of positions at which the vectors are compared.
* @param relativeError If set, the difference between the vectors is computed relative to the value or in absolute terms.
*/
template<class T>
bool equalModuloPrecision(std::vector<T> const& vectorLeft, std::vector<T> const& vectorRight, std::vector<uint_fast64_t> const& positions, T precision, bool relativeError) {
if (vectorLeft.size() != vectorRight.size()) {
LOG4CPLUS_ERROR(logger, "Lengths of vectors do not match, which makes comparison impossible.");
throw storm::exceptions::InvalidArgumentException() << "Lengths of vectors do not match, which makes comparison impossible.";
}
for (uint_fast64_t position : positions) {
if (!equalModuloPrecision(vectorLeft[position], vectorRight[position], precision, relativeError)) {
return false;
}
}
return true;
}
/*!
* Takes the given offset vector and applies the given contraint. That is, it produces another offset vector that contains
* the relative offsets of the entries given by the constraint.
*
* @param offsetVector The offset vector to constrain.
* @param constraint The constraint to apply to the offset vector.
* @return An offset vector that contains all selected relative offsets.
*/
template<class T>
std::vector<T> getConstrainedOffsetVector(std::vector<T> const& offsetVector, storm::storage::BitVector const& constraint) {
// Reserve the known amount of slots for the resulting vector.
std::vector<uint_fast64_t> subVector(constraint.getNumberOfSetBits() + 1);
uint_fast64_t currentRowCount = 0;
uint_fast64_t currentIndexCount = 1;
// Set the first element as this will clearly begin at offset 0.
subVector[0] = 0;
// Loop over all states that need to be kept and copy the relative indices of the nondeterministic choices over
// to the resulting vector.
for (auto index : constraint) {
subVector[currentIndexCount] = currentRowCount + offsetVector[index + 1] - offsetVector[index];
currentRowCount += offsetVector[index + 1] - offsetVector[index];
++currentIndexCount;
}
// Put a sentinel element at the end.
subVector[constraint.getNumberOfSetBits()] = currentRowCount;
return subVector;
}
} // namespace vector
} // namespace utility
} // namespace storm
#endif /* STORM_UTILITY_VECTOR_H_ */

3
storm-config.h.in

@ -19,6 +19,9 @@
// Whether Gurobi is available and to be used (define/undef)
#@STORM_CPP_GUROBI_DEF@ STORM_HAVE_GUROBI
// Whether GLPK is available and to be used (define/undef)
#@STORM_CPP_GLPK_DEF@ STORM_HAVE_GLPK
// Whether Z3 is available and to be used (define/undef)
#@STORM_CPP_Z3_DEF@ STORM_HAVE_Z3

8
test/functional/modelchecker/GmmxxMdpPrctlModelCheckerTest.cpp

@ -17,7 +17,7 @@ TEST(GmmxxMdpPrctlModelCheckerTest, Dice) {
ASSERT_EQ(mdp->getNumberOfStates(), 169ull);
ASSERT_EQ(mdp->getNumberOfTransitions(), 436ull);
storm::modelchecker::prctl::SparseMdpPrctlModelChecker<double> mc(*mdp, new storm::solver::GmmxxNondeterministicLinearEquationSolver<double>());
storm::modelchecker::prctl::SparseMdpPrctlModelChecker<double> mc(*mdp, std::shared_ptr<storm::solver::AbstractNondeterministicLinearEquationSolver<double>>(new storm::solver::GmmxxNondeterministicLinearEquationSolver<double>()));
storm::property::prctl::Ap<double>* apFormula = new storm::property::prctl::Ap<double>("two");
storm::property::prctl::Eventually<double>* eventuallyFormula = new storm::property::prctl::Eventually<double>(apFormula);
@ -105,7 +105,7 @@ TEST(GmmxxMdpPrctlModelCheckerTest, Dice) {
std::shared_ptr<storm::models::Mdp<double>> stateRewardMdp = stateRewardParser.getModel<storm::models::Mdp<double>>();
storm::modelchecker::prctl::SparseMdpPrctlModelChecker<double> stateRewardModelChecker(*stateRewardMdp, new storm::solver::GmmxxNondeterministicLinearEquationSolver<double>());
storm::modelchecker::prctl::SparseMdpPrctlModelChecker<double> stateRewardModelChecker(*stateRewardMdp, std::shared_ptr<storm::solver::AbstractNondeterministicLinearEquationSolver<double>>(new storm::solver::GmmxxNondeterministicLinearEquationSolver<double>()));
apFormula = new storm::property::prctl::Ap<double>("done");
reachabilityRewardFormula = new storm::property::prctl::ReachabilityReward<double>(apFormula);
@ -133,7 +133,7 @@ TEST(GmmxxMdpPrctlModelCheckerTest, Dice) {
std::shared_ptr<storm::models::Mdp<double>> stateAndTransitionRewardMdp = stateAndTransitionRewardParser.getModel<storm::models::Mdp<double>>();
storm::modelchecker::prctl::SparseMdpPrctlModelChecker<double> stateAndTransitionRewardModelChecker(*stateAndTransitionRewardMdp, new storm::solver::GmmxxNondeterministicLinearEquationSolver<double>());
storm::modelchecker::prctl::SparseMdpPrctlModelChecker<double> stateAndTransitionRewardModelChecker(*stateAndTransitionRewardMdp, std::shared_ptr<storm::solver::AbstractNondeterministicLinearEquationSolver<double>>(new storm::solver::GmmxxNondeterministicLinearEquationSolver<double>()));
apFormula = new storm::property::prctl::Ap<double>("done");
reachabilityRewardFormula = new storm::property::prctl::ReachabilityReward<double>(apFormula);
@ -167,7 +167,7 @@ TEST(GmmxxMdpPrctlModelCheckerTest, AsynchronousLeader) {
ASSERT_EQ(mdp->getNumberOfStates(), 3172ull);
ASSERT_EQ(mdp->getNumberOfTransitions(), 7144ull);
storm::modelchecker::prctl::SparseMdpPrctlModelChecker<double> mc(*mdp, new storm::solver::GmmxxNondeterministicLinearEquationSolver<double>());
storm::modelchecker::prctl::SparseMdpPrctlModelChecker<double> mc(*mdp, std::shared_ptr<storm::solver::AbstractNondeterministicLinearEquationSolver<double>>(new storm::solver::GmmxxNondeterministicLinearEquationSolver<double>()));
storm::property::prctl::Ap<double>* apFormula = new storm::property::prctl::Ap<double>("elected");
storm::property::prctl::Eventually<double>* eventuallyFormula = new storm::property::prctl::Eventually<double>(apFormula);

8
test/functional/modelchecker/SparseMdpPrctlModelCheckerTest.cpp

@ -16,7 +16,7 @@ TEST(SparseMdpPrctlModelCheckerTest, Dice) {
ASSERT_EQ(mdp->getNumberOfStates(), 169ull);
ASSERT_EQ(mdp->getNumberOfTransitions(), 436ull);
storm::modelchecker::prctl::SparseMdpPrctlModelChecker<double> mc(*mdp, new storm::solver::AbstractNondeterministicLinearEquationSolver<double>());
storm::modelchecker::prctl::SparseMdpPrctlModelChecker<double> mc(*mdp, std::shared_ptr<storm::solver::AbstractNondeterministicLinearEquationSolver<double>>(new storm::solver::AbstractNondeterministicLinearEquationSolver<double>()));
storm::property::prctl::Ap<double>* apFormula = new storm::property::prctl::Ap<double>("two");
storm::property::prctl::Eventually<double>* eventuallyFormula = new storm::property::prctl::Eventually<double>(apFormula);
@ -104,7 +104,7 @@ TEST(SparseMdpPrctlModelCheckerTest, Dice) {
std::shared_ptr<storm::models::Mdp<double>> stateRewardMdp = stateRewardParser.getModel<storm::models::Mdp<double>>();
storm::modelchecker::prctl::SparseMdpPrctlModelChecker<double> stateRewardModelChecker(*stateRewardMdp, new storm::solver::AbstractNondeterministicLinearEquationSolver<double>());
storm::modelchecker::prctl::SparseMdpPrctlModelChecker<double> stateRewardModelChecker(*stateRewardMdp, std::shared_ptr<storm::solver::AbstractNondeterministicLinearEquationSolver<double>>(new storm::solver::AbstractNondeterministicLinearEquationSolver<double>()));
apFormula = new storm::property::prctl::Ap<double>("done");
reachabilityRewardFormula = new storm::property::prctl::ReachabilityReward<double>(apFormula);
@ -132,7 +132,7 @@ TEST(SparseMdpPrctlModelCheckerTest, Dice) {
std::shared_ptr<storm::models::Mdp<double>> stateAndTransitionRewardMdp = stateAndTransitionRewardParser.getModel<storm::models::Mdp<double>>();
storm::modelchecker::prctl::SparseMdpPrctlModelChecker<double> stateAndTransitionRewardModelChecker(*stateAndTransitionRewardMdp, new storm::solver::AbstractNondeterministicLinearEquationSolver<double>());
storm::modelchecker::prctl::SparseMdpPrctlModelChecker<double> stateAndTransitionRewardModelChecker(*stateAndTransitionRewardMdp, std::shared_ptr<storm::solver::AbstractNondeterministicLinearEquationSolver<double>>(new storm::solver::AbstractNondeterministicLinearEquationSolver<double>()));
apFormula = new storm::property::prctl::Ap<double>("done");
reachabilityRewardFormula = new storm::property::prctl::ReachabilityReward<double>(apFormula);
@ -166,7 +166,7 @@ TEST(SparseMdpPrctlModelCheckerTest, AsynchronousLeader) {
ASSERT_EQ(mdp->getNumberOfStates(), 3172ull);
ASSERT_EQ(mdp->getNumberOfTransitions(), 7144ull);
storm::modelchecker::prctl::SparseMdpPrctlModelChecker<double> mc(*mdp, new storm::solver::AbstractNondeterministicLinearEquationSolver<double>());
storm::modelchecker::prctl::SparseMdpPrctlModelChecker<double> mc(*mdp, std::shared_ptr<storm::solver::AbstractNondeterministicLinearEquationSolver<double>>(new storm::solver::AbstractNondeterministicLinearEquationSolver<double>()));
storm::property::prctl::Ap<double>* apFormula = new storm::property::prctl::Ap<double>("elected");
storm::property::prctl::Eventually<double>* eventuallyFormula = new storm::property::prctl::Eventually<double>(apFormula);

4
test/performance/modelchecker/GmmxxMdpPrctModelCheckerTest.cpp

@ -17,7 +17,7 @@ TEST(GmmxxMdpPrctlModelCheckerTest, AsynchronousLeader) {
ASSERT_EQ(mdp->getNumberOfStates(), 2095783ull);
ASSERT_EQ(mdp->getNumberOfTransitions(), 7714385ull);
storm::modelchecker::prctl::SparseMdpPrctlModelChecker<double> mc(*mdp, new storm::solver::GmmxxNondeterministicLinearEquationSolver<double>());
storm::modelchecker::prctl::SparseMdpPrctlModelChecker<double> mc(*mdp, std::shared_ptr<storm::solver::AbstractNondeterministicLinearEquationSolver<double>>(new storm::solver::GmmxxNondeterministicLinearEquationSolver<double>()));
storm::property::prctl::Ap<double>* apFormula = new storm::property::prctl::Ap<double>("elected");
storm::property::prctl::Eventually<double>* eventuallyFormula = new storm::property::prctl::Eventually<double>(apFormula);
@ -104,7 +104,7 @@ TEST(GmmxxMdpPrctlModelCheckerTest, Consensus) {
ASSERT_EQ(mdp->getNumberOfStates(), 63616ull);
ASSERT_EQ(mdp->getNumberOfTransitions(), 213472ull);
storm::modelchecker::prctl::SparseMdpPrctlModelChecker<double> mc(*mdp, new storm::solver::GmmxxNondeterministicLinearEquationSolver<double>());
storm::modelchecker::prctl::SparseMdpPrctlModelChecker<double> mc(*mdp, std::shared_ptr<storm::solver::AbstractNondeterministicLinearEquationSolver<double>>(new storm::solver::GmmxxNondeterministicLinearEquationSolver<double>()));
storm::property::prctl::Ap<double>* apFormula = new storm::property::prctl::Ap<double>("finished");
storm::property::prctl::Eventually<double>* eventuallyFormula = new storm::property::prctl::Eventually<double>(apFormula);

4
test/performance/modelchecker/SparseMdpPrctlModelCheckerTest.cpp

@ -17,7 +17,7 @@ TEST(SparseMdpPrctlModelCheckerTest, AsynchronousLeader) {
ASSERT_EQ(mdp->getNumberOfStates(), 2095783ull);
ASSERT_EQ(mdp->getNumberOfTransitions(), 7714385ull);
storm::modelchecker::prctl::SparseMdpPrctlModelChecker<double> mc(*mdp, new storm::solver::AbstractNondeterministicLinearEquationSolver<double>());
storm::modelchecker::prctl::SparseMdpPrctlModelChecker<double> mc(*mdp, std::shared_ptr<storm::solver::AbstractNondeterministicLinearEquationSolver<double>>(new storm::solver::AbstractNondeterministicLinearEquationSolver<double>()));
storm::property::prctl::Ap<double>* apFormula = new storm::property::prctl::Ap<double>("elected");
storm::property::prctl::Eventually<double>* eventuallyFormula = new storm::property::prctl::Eventually<double>(apFormula);
@ -106,7 +106,7 @@ TEST(SparseMdpPrctlModelCheckerTest, Consensus) {
ASSERT_EQ(mdp->getNumberOfStates(), 63616ull);
ASSERT_EQ(mdp->getNumberOfTransitions(), 213472ull);
storm::modelchecker::prctl::SparseMdpPrctlModelChecker<double> mc(*mdp, new storm::solver::AbstractNondeterministicLinearEquationSolver<double>());
storm::modelchecker::prctl::SparseMdpPrctlModelChecker<double> mc(*mdp, std::shared_ptr<storm::solver::AbstractNondeterministicLinearEquationSolver<double>>(new storm::solver::AbstractNondeterministicLinearEquationSolver<double>()));
storm::property::prctl::Ap<double>* apFormula = new storm::property::prctl::Ap<double>("finished");
storm::property::prctl::Eventually<double>* eventuallyFormula = new storm::property::prctl::Eventually<double>(apFormula);

Loading…
Cancel
Save