Browse Source

working towards EC detection

Former-commit-id: 78bbe54f81
main
dehnert 9 years ago
parent
commit
b06419afe0
  1. 2
      src/cli/entrypoints.h
  2. 285
      src/modelchecker/reachability/SparseMdpLearningModelChecker.cpp
  3. 26
      src/modelchecker/reachability/SparseMdpLearningModelChecker.h
  4. 2
      src/parser/DeterministicModelParser.cpp
  5. 2
      src/parser/NondeterministicModelParser.cpp
  6. 32
      src/storage/SparseMatrix.cpp
  7. 2
      src/storage/SparseMatrix.h
  8. 2
      src/storage/expressions/ToExprtkStringVisitor.cpp

2
src/cli/entrypoints.h

@ -64,7 +64,7 @@ namespace storm {
storm::modelchecker::SparseMdpLearningModelChecker<ValueType> checker(program);
std::unique_ptr<storm::modelchecker::CheckResult> result;
if (checker.canHandle(task)) {
std::unique_ptr<storm::modelchecker::CheckResult> result = checker.check(task);
result = checker.check(task);
} else {
std::cout << " skipped, because the formula cannot be handled by the selected engine/method." << std::endl;
}

285
src/modelchecker/reachability/SparseMdpLearningModelChecker.cpp

@ -2,6 +2,7 @@
#include "src/storage/SparseMatrix.h"
#include "src/storage/sparse/StateStorage.h"
#include "src/storage/MaximalEndComponentDecomposition.h"
#include "src/generator/PrismNextStateGenerator.h"
@ -11,13 +12,16 @@
#include "src/modelchecker/results/ExplicitQuantitativeCheckResult.h"
#include "src/settings/SettingsManager.h"
#include "src/settings/modules/GeneralSettings.h"
#include "src/utility/macros.h"
#include "src/exceptions/NotSupportedException.h"
namespace storm {
namespace modelchecker {
template<typename ValueType>
SparseMdpLearningModelChecker<ValueType>::SparseMdpLearningModelChecker(storm::prism::Program const& program) : program(storm::utility::prism::preprocessProgram<ValueType>(program)), variableInformation(this->program) {
SparseMdpLearningModelChecker<ValueType>::SparseMdpLearningModelChecker(storm::prism::Program const& program) : program(storm::utility::prism::preprocessProgram<ValueType>(program)), variableInformation(this->program), randomGenerator(std::chrono::system_clock::now().time_since_epoch().count()), comparator(1e-9) {
// Intentionally left empty.
}
@ -104,7 +108,6 @@ namespace storm {
max = std::max(max, current);
}
// STORM_LOG_TRACE("Looking for action with value " << upperBoundsPerState[stateToRowGroupMapping[currentStateId]] << ".");
STORM_LOG_TRACE("Looking for action with value " << max << ".");
for (uint32_t row = rowGroupIndices[rowGroup]; row < rowGroupIndices[rowGroup + 1]; ++row) {
@ -115,9 +118,7 @@ namespace storm {
STORM_LOG_TRACE("Computed (upper) bound " << current << " for row " << row << ".");
// If the action is one of the maximizing ones, insert it into our list.
// TODO: should this need to be an approximate check?
// if (current == upperBoundsPerState[stateToRowGroupMapping[currentStateId]]) {
if (current == max) {
if (comparator.isEqual(current, max)) {
allMaxActions.push_back(row);
}
}
@ -126,99 +127,128 @@ namespace storm {
// Now sample from all maximizing actions.
std::uniform_int_distribution<uint32_t> distribution(0, allMaxActions.size() - 1);
return allMaxActions[distribution(generator)] - rowGroupIndices[rowGroup];
return allMaxActions[distribution(randomGenerator)] - rowGroupIndices[rowGroup];
}
template<typename ValueType>
typename SparseMdpLearningModelChecker<ValueType>::StateType SparseMdpLearningModelChecker<ValueType>::sampleSuccessorFromAction(StateType currentStateId, std::vector<std::vector<storm::storage::MatrixEntry<StateType, ValueType>>> const& transitionMatrix, std::vector<StateType> const& rowGroupIndices, std::vector<StateType> const& stateToRowGroupMapping) {
uint32_t row = rowGroupIndices[stateToRowGroupMapping[currentStateId]];
// TODO: this can be precomputed.
// TODO: precompute this?
std::vector<ValueType> probabilities(transitionMatrix[row].size());
std::transform(transitionMatrix[row].begin(), transitionMatrix[row].end(), probabilities.begin(), [] (storm::storage::MatrixEntry<StateType, ValueType> const& entry) { return entry.getValue(); } );
// Now sample according to the probabilities.
std::discrete_distribution<StateType> distribution(probabilities.begin(), probabilities.end());
StateType offset = distribution(generator);
StateType offset = distribution(randomGenerator);
STORM_LOG_TRACE("Sampled " << offset << " from " << probabilities.size() << " elements.");
return transitionMatrix[row][offset].getColumn();
}
template<typename ValueType>
std::unique_ptr<CheckResult> SparseMdpLearningModelChecker<ValueType>::computeReachabilityProbabilities(CheckTask<storm::logic::EventuallyFormula> const& checkTask) {
storm::logic::EventuallyFormula const& eventuallyFormula = checkTask.getFormula();
storm::logic::Formula const& subformula = eventuallyFormula.getSubformula();
STORM_LOG_THROW(subformula.isAtomicExpressionFormula() || subformula.isAtomicLabelFormula(), storm::exceptions::NotSupportedException, "Learning engine can only deal with formulas of the form 'F \"label\"' or 'F expression'.");
storm::expressions::Expression targetStateExpression;
storm::expressions::Expression SparseMdpLearningModelChecker<ValueType>::getTargetStateExpression(storm::logic::Formula const& subformula) {
storm::expressions::Expression result;
if (subformula.isAtomicExpressionFormula()) {
targetStateExpression = subformula.asAtomicExpressionFormula().getExpression();
result = subformula.asAtomicExpressionFormula().getExpression();
} else {
targetStateExpression = program.getLabelExpression(subformula.asAtomicLabelFormula().getLabel());
result = program.getLabelExpression(subformula.asAtomicLabelFormula().getLabel());
}
return result;
}
template<typename ValueType>
void SparseMdpLearningModelChecker<ValueType>::detectEndComponents(std::vector<std::pair<StateType, uint32_t>> const& stateActionStack, boost::container::flat_set<StateType> const& targetStates, std::vector<std::vector<storm::storage::MatrixEntry<StateType, ValueType>>> const& transitionMatrix, std::vector<StateType> const& rowGroupIndices, std::vector<StateType> const& stateToRowGroupMapping, std::vector<ValueType>& lowerBoundsPerAction, std::vector<ValueType>& upperBoundsPerAction, std::vector<ValueType>& lowerBoundsPerState, std::vector<ValueType>& upperBoundsPerState, std::unordered_map<StateType, storm::generator::CompressedState>& unexploredStates, StateType const& unexploredMarker) const {
// A container for the encountered states.
storm::storage::sparse::StateStorage<StateType> stateStorage(variableInformation.getTotalBitOffset(true));
// Outline:
// 1. construct a sparse transition matrix of the relevant part of the state space.
// 2. use this matrix to compute an MEC decomposition.
// 3. if non-empty analyze the decomposition for accepting/rejecting MECs.
// 4. modify matrix to account for the findings of 3.
// A generator used to explore the model.
storm::generator::PrismNextStateGenerator<ValueType, StateType> generator(program, variableInformation, false);
// Start with 1.
storm::storage::SparseMatrixBuilder<ValueType> builder(0, 0, 0, false, true, 0);
std::vector<StateType> relevantStates;
for (StateType state = 0; state < stateToRowGroupMapping.size(); ++state) {
if (stateToRowGroupMapping[state] != unexploredMarker) {
relevantStates.push_back(state);
}
}
StateType unexploredState = relevantStates.size();
// A container that stores the transitions found so far.
std::vector<std::vector<storm::storage::MatrixEntry<StateType, ValueType>>> matrix;
std::unordered_map<StateType, StateType> relevantStateToNewRowGroupMapping;
for (StateType index = 0; index < relevantStates.size(); ++index) {
relevantStateToNewRowGroupMapping.emplace(relevantStates[index], index);
}
// A vector storing where the row group of each state starts.
std::vector<StateType> rowGroupIndices;
rowGroupIndices.push_back(0);
StateType currentRow = 0;
for (auto const& state : relevantStates) {
builder.newRowGroup(currentRow);
StateType rowGroup = stateToRowGroupMapping[state];
for (auto row = rowGroupIndices[rowGroup]; row < rowGroupIndices[rowGroup + 1]; ++row) {
ValueType unexpandedProbability = storm::utility::zero<ValueType>();
for (auto const& entry : transitionMatrix[row]) {
auto it = relevantStateToNewRowGroupMapping.find(entry.getColumn());
if (it != relevantStateToNewRowGroupMapping.end()) {
// If the entry is a relevant state, we copy it over (and compensate for the offset change).
builder.addNextValue(currentRow, it->second, entry.getValue());
} else {
// If the entry is an unexpanded state, we gather the probability to later redirect it to an unexpanded sink.
unexpandedProbability += entry.getValue();
}
}
builder.addNextValue(currentRow, unexploredState, unexpandedProbability);
++currentRow;
}
}
builder.newRowGroup(currentRow);
// A vector storing the mapping from state ids to row groups.
std::vector<StateType> stateToRowGroupMapping;
StateType unexploredMarker = std::numeric_limits<StateType>::max();
// Vectors to store the lower/upper bounds for each action (in each state).
std::vector<ValueType> lowerBoundsPerAction;
std::vector<ValueType> upperBoundsPerAction;
std::vector<ValueType> lowerBoundsPerState;
std::vector<ValueType> upperBoundsPerState;
// Go on to step 2.
storm::storage::MaximalEndComponentDecomposition<ValueType> mecDecomposition;
// A mapping of unexplored IDs to their actual compressed states.
std::unordered_map<StateType, storm::generator::CompressedState> unexploredStates;
// 3. Analyze the MEC decomposition.
// Create a callback for the next-state generator to enable it to request the index of states.
std::function<StateType (storm::generator::CompressedState const&)> stateToIdCallback = [&stateStorage, &stateToRowGroupMapping, &unexploredStates, &unexploredMarker] (storm::generator::CompressedState const& state) -> StateType {
StateType newIndex = stateStorage.numberOfStates;
// Check, if the state was already registered.
std::pair<uint32_t, std::size_t> actualIndexBucketPair = stateStorage.stateToId.findOrAddAndGetBucket(state, newIndex);
if (actualIndexBucketPair.first == newIndex) {
++stateStorage.numberOfStates;
stateToRowGroupMapping.push_back(unexploredMarker);
unexploredStates[newIndex] = state;
}
return actualIndexBucketPair.first;
};
// 4. Finally modify the system
}
template<typename ValueType>
std::tuple<typename SparseMdpLearningModelChecker<ValueType>::StateType, ValueType, ValueType> SparseMdpLearningModelChecker<ValueType>::performLearningProcedure(storm::expressions::Expression const& targetStateExpression, storm::storage::sparse::StateStorage<StateType>& stateStorage, storm::generator::PrismNextStateGenerator<ValueType, StateType>& generator, std::function<StateType (storm::generator::CompressedState const&)> const& stateToIdCallback, std::vector<std::vector<storm::storage::MatrixEntry<StateType, ValueType>>>& matrix, std::vector<StateType>& rowGroupIndices, std::vector<StateType>& stateToRowGroupMapping, std::unordered_map<StateType, storm::generator::CompressedState>& unexploredStates, StateType const& unexploredMarker) {
// Generate the initial state so we know where to start the simulation.
stateStorage.initialStateIndices = generator.getInitialStates(stateToIdCallback);
STORM_LOG_THROW(stateStorage.initialStateIndices.size() == 1, storm::exceptions::NotSupportedException, "Currently only models with one initial state are supported by the learning engine.");
StateType initialStateIndex = stateStorage.initialStateIndices.front();
// A set storing all target states.
boost::container::flat_set<StateType> targetStates;
// Vectors to store the lower/upper bounds for each action (in each state).
std::vector<ValueType> lowerBoundsPerAction;
std::vector<ValueType> upperBoundsPerAction;
std::vector<ValueType> lowerBoundsPerState;
std::vector<ValueType> upperBoundsPerState;
// Now perform the actual sampling.
std::vector<std::pair<StateType, uint32_t>> stateActionStack;
uint_fast64_t iteration = 0;
std::size_t iterations = 0;
std::size_t maxPathLength = 0;
std::size_t pathLengthUntilEndComponentDetection = 27;
bool convergenceCriterionMet = false;
while (!convergenceCriterionMet) {
// Start the search from the initial state.
stateActionStack.push_back(std::make_pair(stateStorage.initialStateIndices.front(), 0));
stateActionStack.push_back(std::make_pair(initialStateIndex, 0));
bool foundTerminalState = false;
bool foundTargetState = false;
while (!foundTargetState) {
while (!foundTerminalState) {
StateType const& currentStateId = stateActionStack.back().first;
STORM_LOG_TRACE("State on top of stack is: " << currentStateId << ".");
// If the state is not yet expanded, we need to retrieve its behaviors.
auto unexploredIt = unexploredStates.find(currentStateId);
if (unexploredIt != unexploredStates.end()) {
STORM_LOG_TRACE("State was not yet expanded.");
STORM_LOG_TRACE("State was not yet explored.");
// Map the unexplored state to a row group.
stateToRowGroupMapping[currentStateId] = rowGroupIndices.size() - 1;
@ -235,60 +265,32 @@ namespace storm {
generator.load(currentState);
if (generator.satisfies(targetStateExpression)) {
STORM_LOG_TRACE("State does not need to be expanded, because it is a target state.");
// If it's in fact a goal state, we need to go backwards in the stack and update the probabilities.
targetStates.insert(currentStateId);
foundTargetState = true;
lowerBoundsPerState.back() = storm::utility::one<ValueType>();
// Set the lower/upper bounds for the only (dummy) action.
lowerBoundsPerAction.push_back(storm::utility::one<ValueType>());
upperBoundsPerAction.push_back(storm::utility::one<ValueType>());
// Increase the size of the matrix, but leave the row empty.
matrix.resize(matrix.size() + 1);
STORM_LOG_TRACE("Updating probabilities along states in stack.");
updateProbabilitiesUsingStack(stateActionStack, matrix, rowGroupIndices, stateToRowGroupMapping, lowerBoundsPerAction, upperBoundsPerAction, lowerBoundsPerState, upperBoundsPerState, unexploredMarker);
foundTerminalState = true;
} else {
STORM_LOG_TRACE("Expanding state.");
STORM_LOG_TRACE("Exploring state.");
// If it needs to be expanded, we use the generator to retrieve the behavior of the new state.
storm::generator::StateBehavior<ValueType, StateType> behavior = generator.expand(stateToIdCallback);
STORM_LOG_TRACE("State has " << behavior.getNumberOfChoices() << " choices.");
// Clumsily check whether we have found a state that forms a trivial BMEC.
bool isNonacceptingTerminalState = false;
if (behavior.getNumberOfChoices() == 0) {
isNonacceptingTerminalState = true;
foundTerminalState = true;
} else if (behavior.getNumberOfChoices() == 1) {
auto const& onlyChoice = *behavior.begin();
if (onlyChoice.size() == 1) {
auto const& onlyEntry = *onlyChoice.begin();
if (onlyEntry.first == currentStateId) {
isNonacceptingTerminalState = true;
foundTerminalState = true;
}
}
}
if (isNonacceptingTerminalState) {
STORM_LOG_TRACE("State does not need to be expanded, because it forms a trivial BMEC.");
foundTargetState = true;
upperBoundsPerState.back() = storm::utility::zero<ValueType>();
// Set the lower/upper bounds for the only (dummy) action.
lowerBoundsPerAction.push_back(storm::utility::zero<ValueType>());
upperBoundsPerAction.push_back(storm::utility::zero<ValueType>());
// Increase the size of the matrix, but leave the row empty.
matrix.resize(matrix.size() + 1);
STORM_LOG_TRACE("Updating probabilities along states in stack.");
updateProbabilitiesUsingStack(stateActionStack, matrix, rowGroupIndices, stateToRowGroupMapping, lowerBoundsPerAction, upperBoundsPerAction, lowerBoundsPerState, upperBoundsPerState, unexploredMarker);
}
// If the state was neither a trivial (non-accepting) BMEC nor a target state, we need to store
// its behavior.
if (!foundTargetState) {
// If the state was neither a trivial (non-accepting) terminal state nor a target state, we
// need to store its behavior.
if (!foundTerminalState) {
// Next, we insert the behavior into our matrix structure.
StateType startRow = matrix.size();
matrix.resize(startRow + behavior.getNumberOfChoices());
@ -297,10 +299,10 @@ namespace storm {
for (auto const& entry : choice) {
matrix[startRow + currentAction].emplace_back(entry.first, entry.second);
}
lowerBoundsPerAction.push_back(storm::utility::zero<ValueType>());
upperBoundsPerAction.push_back(storm::utility::one<ValueType>());
// Update the bounds for the explored states to initially let them have the correct value.
updateProbabilities(currentStateId, currentAction, matrix, rowGroupIndices, stateToRowGroupMapping, lowerBoundsPerAction, upperBoundsPerAction, lowerBoundsPerState, upperBoundsPerState, unexploredMarker);
@ -309,6 +311,26 @@ namespace storm {
}
}
if (foundTerminalState) {
STORM_LOG_TRACE("State does not need to be explored, because it is " << (foundTargetState ? "a target state" : "a rejecting terminal state") << ".");
if (foundTargetState) {
lowerBoundsPerState.back() = storm::utility::one<ValueType>();
lowerBoundsPerAction.push_back(storm::utility::one<ValueType>());
upperBoundsPerAction.push_back(storm::utility::one<ValueType>());
} else {
upperBoundsPerState.back() = storm::utility::zero<ValueType>();
lowerBoundsPerAction.push_back(storm::utility::zero<ValueType>());
upperBoundsPerAction.push_back(storm::utility::zero<ValueType>());
}
// Increase the size of the matrix, but leave the row empty.
matrix.resize(matrix.size() + 1);
STORM_LOG_TRACE("Updating probabilities along states in stack.");
updateProbabilitiesUsingStack(stateActionStack, matrix, rowGroupIndices, stateToRowGroupMapping, lowerBoundsPerAction, upperBoundsPerAction, lowerBoundsPerState, upperBoundsPerState, unexploredMarker);
}
// Now that we have explored the state, we can dispose of it.
unexploredStates.erase(unexploredIt);
@ -318,14 +340,14 @@ namespace storm {
// If the state was explored before, but determined to be a terminal state of the exploration,
// we need to determine this now.
if (matrix[rowGroupIndices[stateToRowGroupMapping[currentStateId]]].empty()) {
foundTargetState = true;
foundTerminalState = true;
// Update the bounds along the path to the terminal state.
updateProbabilitiesUsingStack(stateActionStack, matrix, rowGroupIndices, stateToRowGroupMapping, lowerBoundsPerAction, upperBoundsPerAction, lowerBoundsPerState, upperBoundsPerState, unexploredMarker);
}
}
if (!foundTargetState) {
if (!foundTerminalState) {
// At this point, we can be sure that the state was expanded and that we can sample according to the
// probabilities in the matrix.
uint32_t chosenAction = sampleFromMaxActions(currentStateId, matrix, rowGroupIndices, stateToRowGroupMapping, upperBoundsPerAction, unexploredMarker);
@ -337,20 +359,83 @@ namespace storm {
// Put the successor state and a dummy action on top of the stack.
stateActionStack.emplace_back(successor, 0);
maxPathLength = std::max(maxPathLength, stateActionStack.size());
// If the current path length exceeds the threshold, we perform an EC detection.
if (stateActionStack.size() > pathLengthUntilEndComponentDetection) {
detectEndComponents(stateActionStack, targetStates, matrix, rowGroupIndices, stateToRowGroupMapping, lowerBoundsPerAction, upperBoundsPerAction, lowerBoundsPerState, upperBoundsPerState, unexploredStates, unexploredMarker);
}
}
}
STORM_LOG_DEBUG("Discovered states: " << stateStorage.numberOfStates << " (" << unexploredStates.size() << " unexplored).");
STORM_LOG_DEBUG("Lower bound is " << lowerBoundsPerState[0] << ".");
STORM_LOG_DEBUG("Upper bound is " << upperBoundsPerState[0] << ".");
ValueType difference = upperBoundsPerState[0] - lowerBoundsPerState[0];
STORM_LOG_DEBUG("Difference after iteration " << iteration << " is " << difference << ".");
STORM_LOG_DEBUG("Lower bound is " << lowerBoundsPerState[initialStateIndex] << ".");
STORM_LOG_DEBUG("Upper bound is " << upperBoundsPerState[initialStateIndex] << ".");
ValueType difference = upperBoundsPerState[initialStateIndex] - lowerBoundsPerState[initialStateIndex];
STORM_LOG_DEBUG("Difference after iteration " << iterations << " is " << difference << ".");
convergenceCriterionMet = difference < 1e-6;
++iteration;
++iterations;
}
return nullptr;
if (storm::settings::generalSettings().isShowStatisticsSet()) {
std::cout << std::endl << "Learning summary -------------------------" << std::endl;
std::cout << "Discovered states: " << stateStorage.numberOfStates << " (" << unexploredStates.size() << " unexplored)" << std::endl;
std::cout << "Sampling iterations: " << iterations << std::endl;
std::cout << "Maximal path length: " << maxPathLength << std::endl;
}
return std::make_tuple(initialStateIndex, lowerBoundsPerState[initialStateIndex], upperBoundsPerState[initialStateIndex]);
}
template<typename ValueType>
std::unique_ptr<CheckResult> SparseMdpLearningModelChecker<ValueType>::computeReachabilityProbabilities(CheckTask<storm::logic::EventuallyFormula> const& checkTask) {
storm::logic::EventuallyFormula const& eventuallyFormula = checkTask.getFormula();
storm::logic::Formula const& subformula = eventuallyFormula.getSubformula();
STORM_LOG_THROW(subformula.isAtomicExpressionFormula() || subformula.isAtomicLabelFormula(), storm::exceptions::NotSupportedException, "Learning engine can only deal with formulas of the form 'F \"label\"' or 'F expression'.");
// Derive the expression for the target states, so we know when to abort the learning run.
storm::expressions::Expression targetStateExpression = getTargetStateExpression(subformula);
// A container for the encountered states.
storm::storage::sparse::StateStorage<StateType> stateStorage(variableInformation.getTotalBitOffset(true));
// A generator used to explore the model.
storm::generator::PrismNextStateGenerator<ValueType, StateType> generator(program, variableInformation, false);
// A container that stores the transitions found so far.
std::vector<std::vector<storm::storage::MatrixEntry<StateType, ValueType>>> matrix;
// A vector storing where the row group of each state starts.
std::vector<StateType> rowGroupIndices;
rowGroupIndices.push_back(0);
// A vector storing the mapping from state ids to row groups.
std::vector<StateType> stateToRowGroupMapping;
StateType unexploredMarker = std::numeric_limits<StateType>::max();
// A mapping of unexplored IDs to their actual compressed states.
std::unordered_map<StateType, storm::generator::CompressedState> unexploredStates;
// Create a callback for the next-state generator to enable it to request the index of states.
std::function<StateType (storm::generator::CompressedState const&)> stateToIdCallback = [&stateStorage, &stateToRowGroupMapping, &unexploredStates, &unexploredMarker] (storm::generator::CompressedState const& state) -> StateType {
StateType newIndex = stateStorage.numberOfStates;
// Check, if the state was already registered.
std::pair<uint32_t, std::size_t> actualIndexBucketPair = stateStorage.stateToId.findOrAddAndGetBucket(state, newIndex);
if (actualIndexBucketPair.first == newIndex) {
++stateStorage.numberOfStates;
stateToRowGroupMapping.push_back(unexploredMarker);
unexploredStates[newIndex] = state;
}
return actualIndexBucketPair.first;
};
// Compute and return result.
std::tuple<StateType, ValueType, ValueType> boundsForInitialState = performLearningProcedure(targetStateExpression, stateStorage, generator, stateToIdCallback, matrix, rowGroupIndices, stateToRowGroupMapping, unexploredStates, unexploredMarker);
return std::make_unique<ExplicitQuantitativeCheckResult<ValueType>>(std::get<0>(boundsForInitialState), std::get<1>(boundsForInitialState));
}
template class SparseMdpLearningModelChecker<double>;

26
src/modelchecker/reachability/SparseMdpLearningModelChecker.h

@ -10,9 +10,22 @@
#include "src/generator/CompressedState.h"
#include "src/generator/VariableInformation.h"
#include "src/utility/ConstantsComparator.h"
#include "src/utility/constants.h"
namespace storm {
namespace storage {
namespace sparse {
template<typename StateType>
class StateStorage;
}
}
namespace generator {
template<typename ValueType, typename StateType>
class PrismNextStateGenerator;
}
namespace modelchecker {
template<typename ValueType>
@ -27,6 +40,8 @@ namespace storm {
virtual std::unique_ptr<CheckResult> computeReachabilityProbabilities(CheckTask<storm::logic::EventuallyFormula> const& checkTask) override;
private:
std::tuple<StateType, ValueType, ValueType> performLearningProcedure(storm::expressions::Expression const& targetStateExpression, storm::storage::sparse::StateStorage<StateType>& stateStorage, storm::generator::PrismNextStateGenerator<ValueType, StateType>& generator, std::function<StateType (storm::generator::CompressedState const&)> const& stateToIdCallback, std::vector<std::vector<storm::storage::MatrixEntry<StateType, ValueType>>>& matrix, std::vector<StateType>& rowGroupIndices, std::vector<StateType>& stateToRowGroupMapping, std::unordered_map<StateType, storm::generator::CompressedState>& unexploredStates, StateType const& unexploredMarker);
void updateProbabilities(StateType const& sourceStateId, uint32_t action, std::vector<std::vector<storm::storage::MatrixEntry<StateType, ValueType>>> const& transitionMatrix, std::vector<StateType> const& rowGroupIndices, std::vector<StateType> const& stateToRowGroupMapping, std::vector<ValueType>& lowerBounds, std::vector<ValueType>& upperBounds, std::vector<ValueType>& lowerBoundsPerState, std::vector<ValueType>& upperBoundsPerState, StateType const& unexploredMarker) const;
void updateProbabilitiesUsingStack(std::vector<std::pair<StateType, uint32_t>>& stateActionStack, std::vector<std::vector<storm::storage::MatrixEntry<StateType, ValueType>>> const& transitionMatrix, std::vector<StateType> const& rowGroupIndices, std::vector<StateType> const& stateToRowGroupMapping, std::vector<ValueType>& lowerBounds, std::vector<ValueType>& upperBounds, std::vector<ValueType>& lowerBoundsPerState, std::vector<ValueType>& upperBoundsPerState, StateType const& unexploredMarker) const;
@ -34,7 +49,11 @@ namespace storm {
uint32_t sampleFromMaxActions(StateType currentStateId, std::vector<std::vector<storm::storage::MatrixEntry<StateType, ValueType>>> const& transitionMatrix, std::vector<StateType> const& rowGroupIndices, std::vector<StateType> const& stateToRowGroupMapping, std::vector<ValueType>& upperBounds, StateType const& unexploredMarker);
StateType sampleSuccessorFromAction(StateType currentStateId, std::vector<std::vector<storm::storage::MatrixEntry<StateType, ValueType>>> const& transitionMatrix, std::vector<StateType> const& rowGroupIndices, std::vector<StateType> const& stateToRowGroupMapping);
void detectEndComponents(std::vector<std::pair<StateType, uint32_t>> const& stateActionStack, boost::container::flat_set<StateType> const& targetStates, std::vector<std::vector<storm::storage::MatrixEntry<StateType, ValueType>>> const& transitionMatrix, std::vector<StateType> const& rowGroupIndices, std::vector<StateType> const& stateToRowGroupMapping, std::vector<ValueType>& lowerBoundsPerAction, std::vector<ValueType>& upperBoundsPerAction, std::vector<ValueType>& lowerBoundsPerState, std::vector<ValueType>& upperBoundsPerState, std::unordered_map<StateType, storm::generator::CompressedState>& unexploredStates, StateType const& unexploredMarker) const;
storm::expressions::Expression getTargetStateExpression(storm::logic::Formula const& subformula);
// The program that defines the model to check.
storm::prism::Program program;
@ -42,7 +61,10 @@ namespace storm {
storm::generator::VariableInformation variableInformation;
// The random number generator.
std::default_random_engine generator;
std::default_random_engine randomGenerator;
// A comparator used to determine whether values are equal.
storm::utility::ConstantsComparator<ValueType> comparator;
};
}
}

2
src/parser/DeterministicModelParser.cpp

@ -23,7 +23,7 @@ namespace storm {
uint_fast64_t stateCount = transitions.getColumnCount();
// Parse the state labeling.
storm::models::sparse::StateLabeling labeling(std::move(storm::parser::AtomicPropositionLabelingParser::parseAtomicPropositionLabeling(stateCount, labelingFilename)));
storm::models::sparse::StateLabeling labeling(storm::parser::AtomicPropositionLabelingParser::parseAtomicPropositionLabeling(stateCount, labelingFilename));
// Construct the result.
DeterministicModelParser<ValueType, RewardValueType>::Result result(std::move(transitions), std::move(labeling));

2
src/parser/NondeterministicModelParser.cpp

@ -25,7 +25,7 @@ namespace storm {
uint_fast64_t stateCount = transitions.getColumnCount();
// Parse the state labeling.
storm::models::sparse::StateLabeling labeling(std::move(storm::parser::AtomicPropositionLabelingParser::parseAtomicPropositionLabeling(stateCount, labelingFilename)));
storm::models::sparse::StateLabeling labeling(storm::parser::AtomicPropositionLabelingParser::parseAtomicPropositionLabeling(stateCount, labelingFilename));
// Only parse state rewards if a file is given.
boost::optional<std::vector<RewardValueType>> stateRewards;

32
src/storage/SparseMatrix.cpp

@ -106,14 +106,11 @@ namespace storm {
template<typename ValueType>
void SparseMatrixBuilder<ValueType>::addNextValue(index_type row, index_type column, ValueType const& value) {
// Check that we did not move backwards wrt. the row.
if (row < lastRow) {
throw storm::exceptions::InvalidArgumentException() << "Illegal call to SparseMatrixBuilder::addNextValue: adding an element in row " << row << ", but an element in row " << lastRow << " has already been added.";
}
STORM_LOG_THROW(row >= lastRow, storm::exceptions::InvalidArgumentException, "Adding an element in row " << row << ", but an element in row " << lastRow << " has already been added.");
// Check that we did not move backwards wrt. to column.
if (row == lastRow && column < lastColumn) {
throw storm::exceptions::InvalidArgumentException() << "Illegal call to SparseMatrixBuilder::addNextValue: adding an element in column " << column << " in row " << row << ", but an element in column " << lastColumn << " has already been added in that row.";
}
// If the element is in the same row, but was not inserted in the correct order, we need to fix the row after
// the insertion.
bool fixCurrentRow = row == lastRow && column < lastColumn;
// If we switched to another row, we have to adjust the missing entries in the row indices vector.
if (row != lastRow) {
@ -132,6 +129,27 @@ namespace storm {
highestColumn = std::max(highestColumn, column);
++currentEntryCount;
// If we need to fix the row, do so now.
if (fixCurrentRow) {
// First, we sort according to columns.
std::sort(columnsAndValues.begin() + rowIndications.back(), columnsAndValues.end(), [] (storm::storage::MatrixEntry<index_type, ValueType> const& a, storm::storage::MatrixEntry<index_type, ValueType> const& b) {
return a.getColumn() < b.getColumn();
});
// Then, we eliminate possible duplicate entries.
auto it = std::unique(columnsAndValues.begin() + rowIndications.back(), columnsAndValues.end(), [] (storm::storage::MatrixEntry<index_type, ValueType> const& a, storm::storage::MatrixEntry<index_type, ValueType> const& b) {
return a.getColumn() == b.getColumn();
});
// Finally, remove the superfluous elements.
std::size_t elementsToRemove = std::distance(it, columnsAndValues.end());
if (elementsToRemove > 0) {
STORM_LOG_WARN("Unordered insertion into matrix builder caused duplicate entries.");
currentEntryCount -= elementsToRemove;
columnsAndValues.resize(columnsAndValues.size() - elementsToRemove);
}
}
// In case we did not expect this value, we throw an exception.
if (forceInitialDimensions) {
STORM_LOG_THROW(!initialRowCountSet || lastRow < initialRowCount, storm::exceptions::OutOfRangeException, "Cannot insert value at illegal row " << lastRow << ".");

2
src/storage/SparseMatrix.h

@ -229,7 +229,7 @@ namespace storm {
* @return True if replacement took place, False if nothing changed.
*/
bool replaceColumns(std::vector<index_type> const& replacements, index_type offset);
private:
// A flag indicating whether a row count was set upon construction.
bool initialRowCountSet;

2
src/storage/expressions/ToExprtkStringVisitor.cpp

@ -10,7 +10,7 @@ namespace storm {
stream.str("");
stream.clear();
expression->accept(*this);
return std::move(stream.str());
return stream.str();
}
boost::any ToExprtkStringVisitor::visit(IfThenElseExpression const& expression) {

|||||||
100:0
Loading…
Cancel
Save