Browse Source

A thousand things.

- More tests.
- Changed SparseStateRewardParser to a static class
- Added comments here and there
- Some reformatting.
- Fixed some warnings.
- Eliminated some unnecessary includes.
- ...


Former-commit-id: efe1c96fee
tempestpy_adaptions
masawei 11 years ago
parent
commit
f8566e9dc2
  1. 156
      src/models/MarkovAutomaton.h
  2. 2
      src/parser/DeterministicModelParser.cpp
  3. 48
      src/parser/MarkovAutomatonParser.cpp
  4. 41
      src/parser/MarkovAutomatonParser.h
  5. 518
      src/parser/MarkovAutomatonSparseTransitionParser.cpp
  6. 174
      src/parser/MarkovAutomatonSparseTransitionParser.h
  7. 2
      src/parser/NondeterministicModelParser.cpp
  8. 78
      src/parser/Parser.cpp
  9. 207
      src/parser/Parser.h
  10. 48
      src/parser/SparseStateRewardParser.cpp
  11. 15
      src/parser/SparseStateRewardParser.h
  12. 39
      test/functional/parser/MarkovAutomatonParserTest.cpp

156
src/models/MarkovAutomaton.h

@ -182,84 +182,84 @@ namespace storm {
return std::shared_ptr<AbstractModel<T>>(new MarkovAutomaton(newTransitionMatrix, this->getStateLabeling(), nondeterministicChoiceIndices, markovianStates, exitRates, this->hasStateRewards() ? this->getStateRewardVector() : boost::optional<std::vector<T>>(), this->hasTransitionRewards() ? this->getTransitionRewardMatrix() : boost::optional<storm::storage::SparseMatrix<T>>(), this->hasChoiceLabeling() ? this->getChoiceLabeling() : boost::optional<std::vector<storm::storage::VectorSet<uint_fast64_t>>>()));
}
virtual void writeDotToStream(std::ostream& outStream, bool includeLabeling = true, storm::storage::BitVector const* subsystem = nullptr, std::vector<T> const* firstValue = nullptr, std::vector<T> const* secondValue = nullptr, std::vector<uint_fast64_t> const* stateColoring = nullptr, std::vector<std::string> const* colors = nullptr, std::vector<uint_fast64_t>* scheduler = nullptr, bool finalizeOutput = true) const override {
AbstractModel<T>::writeDotToStream(outStream, includeLabeling, subsystem, firstValue, secondValue, stateColoring, colors, scheduler, false);
// Write the probability distributions for all the states.
auto rowIt = this->transitionMatrix.begin();
for (uint_fast64_t state = 0, highestStateIndex = this->getNumberOfStates() - 1; state <= highestStateIndex; ++state) {
uint_fast64_t rowCount = this->getNondeterministicChoiceIndices()[state + 1] - this->getNondeterministicChoiceIndices()[state];
bool highlightChoice = true;
// For this, we need to iterate over all available nondeterministic choices in the current state.
for (uint_fast64_t row = 0; row < rowCount; ++row, ++rowIt) {
if (scheduler != nullptr) {
// If the scheduler picked the current choice, we will not make it dotted, but highlight it.
if ((*scheduler)[state] == row) {
highlightChoice = true;
} else {
highlightChoice = false;
}
}
// If it's not a Markovian state or the current row is the first choice for this state, then we
// are dealing with a probabilitic choice.
if (!markovianStates.get(state) || row != 0) {
// For each nondeterministic choice, we draw an arrow to an intermediate node to better display
// the grouping of transitions.
outStream << "\t\"" << state << "c" << row << "\" [shape = \"point\"";
// If we were given a scheduler to highlight, we do so now.
if (scheduler != nullptr) {
if (highlightChoice) {
outStream << ", fillcolor=\"red\"";
}
}
outStream << "];" << std::endl;
outStream << "\t" << state << " -> \"" << state << "c" << row << "\"";
// If we were given a scheduler to highlight, we do so now.
if (scheduler != nullptr) {
if (highlightChoice) {
outStream << " [color=\"red\", penwidth = 2]";
} else {
outStream << " [style = \"dotted\"]";
}
}
outStream << ";" << std::endl;
// Now draw all probabilitic arcs that belong to this nondeterminstic choice.
for (auto transitionIt = rowIt.begin(), transitionIte = rowIt.end(); transitionIt != transitionIte; ++transitionIt) {
if (subsystem == nullptr || subsystem->get(transitionIt.column())) {
outStream << "\t\"" << state << "c" << row << "\" -> " << transitionIt.column() << " [ label= \"" << transitionIt.value() << "\" ]";
// If we were given a scheduler to highlight, we do so now.
if (scheduler != nullptr) {
if (highlightChoice) {
outStream << " [color=\"red\", penwidth = 2]";
} else {
outStream << " [style = \"dotted\"]";
}
}
outStream << ";" << std::endl;
}
}
} else {
// In this case we are emitting a Markovian choice, so draw the arrows directly to the target states.
for (auto transitionIt = rowIt.begin(), transitionIte = rowIt.end(); transitionIt != transitionIte; ++transitionIt) {
if (subsystem == nullptr || subsystem->get(transitionIt.column())) {
outStream << "\t\"" << state << "\" -> " << transitionIt.column() << " [ label= \"" << transitionIt.value() << " (" << this->exitRates[state] << ")\" ]";
}
}
}
}
}
if (finalizeOutput) {
outStream << "}" << std::endl;
}
}
virtual void writeDotToStream(std::ostream& outStream, bool includeLabeling = true, storm::storage::BitVector const* subsystem = nullptr, std::vector<T> const* firstValue = nullptr, std::vector<T> const* secondValue = nullptr, std::vector<uint_fast64_t> const* stateColoring = nullptr, std::vector<std::string> const* colors = nullptr, std::vector<uint_fast64_t>* scheduler = nullptr, bool finalizeOutput = true) const override {
AbstractModel<T>::writeDotToStream(outStream, includeLabeling, subsystem, firstValue, secondValue, stateColoring, colors, scheduler, false);
// Write the probability distributions for all the states.
auto rowIt = this->transitionMatrix.begin();
for (uint_fast64_t state = 0, highestStateIndex = this->getNumberOfStates() - 1; state <= highestStateIndex; ++state) {
uint_fast64_t rowCount = this->getNondeterministicChoiceIndices()[state + 1] - this->getNondeterministicChoiceIndices()[state];
bool highlightChoice = true;
// For this, we need to iterate over all available nondeterministic choices in the current state.
for (uint_fast64_t row = 0; row < rowCount; ++row, ++rowIt) {
if (scheduler != nullptr) {
// If the scheduler picked the current choice, we will not make it dotted, but highlight it.
if ((*scheduler)[state] == row) {
highlightChoice = true;
} else {
highlightChoice = false;
}
}
// If it's not a Markovian state or the current row is the first choice for this state, then we
// are dealing with a probabilitic choice.
if (!markovianStates.get(state) || row != 0) {
// For each nondeterministic choice, we draw an arrow to an intermediate node to better display
// the grouping of transitions.
outStream << "\t\"" << state << "c" << row << "\" [shape = \"point\"";
// If we were given a scheduler to highlight, we do so now.
if (scheduler != nullptr) {
if (highlightChoice) {
outStream << ", fillcolor=\"red\"";
}
}
outStream << "];" << std::endl;
outStream << "\t" << state << " -> \"" << state << "c" << row << "\"";
// If we were given a scheduler to highlight, we do so now.
if (scheduler != nullptr) {
if (highlightChoice) {
outStream << " [color=\"red\", penwidth = 2]";
} else {
outStream << " [style = \"dotted\"]";
}
}
outStream << ";" << std::endl;
// Now draw all probabilitic arcs that belong to this nondeterminstic choice.
for (auto transitionIt = rowIt.begin(), transitionIte = rowIt.end(); transitionIt != transitionIte; ++transitionIt) {
if (subsystem == nullptr || subsystem->get(transitionIt.column())) {
outStream << "\t\"" << state << "c" << row << "\" -> " << transitionIt.column() << " [ label= \"" << transitionIt.value() << "\" ]";
// If we were given a scheduler to highlight, we do so now.
if (scheduler != nullptr) {
if (highlightChoice) {
outStream << " [color=\"red\", penwidth = 2]";
} else {
outStream << " [style = \"dotted\"]";
}
}
outStream << ";" << std::endl;
}
}
} else {
// In this case we are emitting a Markovian choice, so draw the arrows directly to the target states.
for (auto transitionIt = rowIt.begin(), transitionIte = rowIt.end(); transitionIt != transitionIte; ++transitionIt) {
if (subsystem == nullptr || subsystem->get(transitionIt.column())) {
outStream << "\t\"" << state << "\" -> " << transitionIt.column() << " [ label= \"" << transitionIt.value() << " (" << this->exitRates[state] << ")\" ]";
}
}
}
}
}
if (finalizeOutput) {
outStream << "}" << std::endl;
}
}
private:
/*!

2
src/parser/DeterministicModelParser.cpp

@ -39,7 +39,7 @@ DeterministicModelParserResultContainer<double> parseDeterministicModel(std::str
DeterministicModelParserResultContainer<double> result(std::move(resultTransitionSystem), std::move(resultLabeling));
if (stateRewardFile != "") {
result.stateRewards.reset(storm::parser::SparseStateRewardParser(stateCount, stateRewardFile));
result.stateRewards.reset(storm::parser::SparseStateRewardParser::parseSparseStateReward(stateCount, stateRewardFile));
}
if (transitionRewardFile != "") {
RewardMatrixInformationStruct* rewardMatrixInfo = new RewardMatrixInformationStruct(rowCount, stateCount, nullptr);

48
src/parser/MarkovAutomatonParser.cpp

@ -3,26 +3,28 @@
#include "SparseStateRewardParser.h"
namespace storm {
namespace parser {
storm::models::MarkovAutomaton<double> MarkovAutomatonParser::parseMarkovAutomaton(std::string const& transitionsFilename, std::string const& labelingFilename, std::string const& stateRewardFilename, std::string const& transitionRewardFilename) {
storm::parser::MarkovAutomatonSparseTransitionParser::ResultType transitionResult(storm::parser::MarkovAutomatonSparseTransitionParser::parseMarkovAutomatonTransitions(transitionsFilename));
storm::models::AtomicPropositionsLabeling resultLabeling(storm::parser::AtomicPropositionLabelingParser(transitionResult.transitionMatrix.getColumnCount(), labelingFilename));
boost::optional<std::vector<double>> stateRewards;
if (stateRewardFilename != "") {
stateRewards.reset(storm::parser::SparseStateRewardParser(transitionResult.transitionMatrix.getColumnCount(), stateRewardFilename));
}
if (transitionRewardFilename != "") {
LOG4CPLUS_ERROR(logger, "Transition rewards are unsupported for Markov automata.");
throw storm::exceptions::WrongFormatException() << "Transition rewards are unsupported for Markov automata.";
}
storm::models::MarkovAutomaton<double> resultingAutomaton(std::move(transitionResult.transitionMatrix), std::move(resultLabeling), std::move(transitionResult.nondeterministicChoiceIndices), std::move(transitionResult.markovianStates), std::move(transitionResult.exitRates), std::move(stateRewards), boost::optional<storm::storage::SparseMatrix<double>>(), boost::optional<std::vector<storm::storage::VectorSet<uint_fast64_t>>>());
return resultingAutomaton;
}
} // namespace parser
} // namespace storm
namespace parser {
storm::models::MarkovAutomaton<double> MarkovAutomatonParser::parseMarkovAutomaton(std::string const& transitionsFilename, std::string const& labelingFilename, std::string const& stateRewardFilename, std::string const& transitionRewardFilename) {
storm::parser::MarkovAutomatonSparseTransitionParser::ResultType transitionResult(storm::parser::MarkovAutomatonSparseTransitionParser::parseMarkovAutomatonTransitions(transitionsFilename));
storm::models::AtomicPropositionsLabeling resultLabeling(storm::parser::AtomicPropositionLabelingParser(transitionResult.transitionMatrix.getColumnCount(), labelingFilename));
boost::optional<std::vector<double>> stateRewards;
if (stateRewardFilename != "") {
stateRewards.reset(storm::parser::SparseStateRewardParser::parseSparseStateReward(transitionResult.transitionMatrix.getColumnCount(), stateRewardFilename));
}
if (transitionRewardFilename != "") {
LOG4CPLUS_ERROR(logger, "Transition rewards are unsupported for Markov automata.");
throw storm::exceptions::WrongFormatException() << "Transition rewards are unsupported for Markov automata.";
}
storm::models::MarkovAutomaton<double> resultingAutomaton(std::move(transitionResult.transitionMatrix), std::move(resultLabeling), std::move(transitionResult.nondeterministicChoiceIndices), std::move(transitionResult.markovianStates), std::move(transitionResult.exitRates), std::move(stateRewards), boost::optional<storm::storage::SparseMatrix<double>>(), boost::optional<std::vector<storm::storage::VectorSet<uint_fast64_t>>>());
return resultingAutomaton;
}
} // namespace parser
} // namespace storm

41
src/parser/MarkovAutomatonParser.h

@ -5,25 +5,28 @@
#include "src/parser/MarkovAutomatonSparseTransitionParser.h"
namespace storm {
namespace parser {
/*!
* A class providing the functionality to parse a labeled Markov automaton.
*/
class MarkovAutomatonParser {
public:
/*!
* Parses the given Markov automaton and returns an object representing the automaton.
*
* @param transitionsFilename The name of the file containing the transitions of the Markov automaton.
* @param labelingFilename The name of the file containing the labels for the states of the Markov automaton.
* @param stateRewardFilename The name of the file that contains the state reward of the Markov automaton.
* @param transitionRewardFilename The name of the file that contains the transition rewards of the Markov automaton.
*/
static storm::models::MarkovAutomaton<double> parseMarkovAutomaton(std::string const& transitionsFilename, std::string const& labelingFilename, std::string const& stateRewardFilename, std::string const& transitionRewardFilename);
};
} // namespace parser
namespace parser {
/*!
* A class providing the functionality to parse a labeled Markov automaton.
*/
class MarkovAutomatonParser {
public:
/*!
* Parses the given Markov automaton and returns an object representing the automaton.
*
* @param transitionsFilename The name of the file containing the transitions of the Markov automaton.
* @param labelingFilename The name of the file containing the labels for the states of the Markov automaton.
* @param stateRewardFilename The name of the file that contains the state reward of the Markov automaton.
* @param transitionRewardFilename The name of the file that contains the transition rewards of the Markov automaton.
*/
static storm::models::MarkovAutomaton<double> parseMarkovAutomaton(std::string const& transitionsFilename, std::string const& labelingFilename, std::string const& stateRewardFilename, std::string const& transitionRewardFilename);
};
} // namespace parser
} // namespace storm
#endif /* STORM_PARSER_MARKOVAUTOMATONPARSER_H_ */

518
src/parser/MarkovAutomatonSparseTransitionParser.cpp

@ -3,264 +3,262 @@
#include "src/settings/Settings.h"
namespace storm {
namespace parser {
MarkovAutomatonSparseTransitionParser::FirstPassResult MarkovAutomatonSparseTransitionParser::firstPass(char* buf, SupportedLineEndingsEnum lineEndings) {
MarkovAutomatonSparseTransitionParser::FirstPassResult result;
bool fixDeadlocks = storm::settings::Settings::getInstance()->isSet("fixDeadlocks");
// Skip the format hint.
buf = storm::parser::forwardToNextLine(buf, lineEndings);
// Now read the transitions.
int_fast64_t source, target = -1;
int_fast64_t lastsource = -1;
bool encounteredEOF = false;
bool stateHasMarkovianChoice = false;
bool stateHasProbabilisticChoice = false;
while (buf[0] != '\0' && !encounteredEOF) {
// At the current point, the next thing to read is the source state of the next choice to come.
source = checked_strtol(buf, &buf);
// Check if we encountered a state index that is bigger than all previously seen ones and record it if necessary.
if (source > result.highestStateIndex) {
result.highestStateIndex = source;
}
// If we have skipped some states, we need to reserve the space for the self-loop insertion in the second pass.
if (source > lastsource + 1) {
if (fixDeadlocks) {
result.numberOfNonzeroEntries += source - lastsource - 1;
result.numberOfChoices += source - lastsource - 1;
} else {
LOG4CPLUS_ERROR(logger, "Found deadlock states (e.g. " << lastsource + 1 << ") during parsing. Please fix them or set the appropriate flag.");
throw storm::exceptions::WrongFormatException() << "Found deadlock states (e.g. " << lastsource + 1 << ") during parsing. Please fix them or set the appropriate flag.";
}
} else if (source < lastsource) {
LOG4CPLUS_ERROR(logger, "Illegal state choice order. A choice of state " << source << " appears at an illegal position.");
throw storm::exceptions::WrongFormatException() << "Illegal state choice order. A choice of state " << source << " appears at an illegal position.";
}
++result.numberOfChoices;
// If we have moved to the next state, we need to clear the flag that stores whether or not the source has a Markovian or probabilistic choice.
if (source != lastsource) {
stateHasMarkovianChoice = false;
stateHasProbabilisticChoice = false;
}
// Record that the current source was the last source.
lastsource = source;
buf = trimWhitespaces(buf);
// Depending on the action name, the choice is either a probabilitic one or a markovian one.
bool isMarkovianChoice = false;
if (buf[0] == '!' && skipWord(buf) - buf == 1) {
isMarkovianChoice = true;
}else {
isMarkovianChoice = false;
}
buf = skipWord(buf);
if (isMarkovianChoice) {
if (stateHasMarkovianChoice) {
LOG4CPLUS_ERROR(logger, "The state " << source << " has multiple Markovian choices.");
throw storm::exceptions::WrongFormatException() << "The state " << source << " has multiple Markovian choices.";
}
if (stateHasProbabilisticChoice) {
LOG4CPLUS_ERROR(logger, "The state " << source << " has a probabilistic choice preceding a Markovian choice. The Markovian choice must be the first choice listed.");
throw storm::exceptions::WrongFormatException() << "The state " << source << " has a probabilistic choice preceding a Markovian choice. The Markovian choice must be the first choice listed.";
}
stateHasMarkovianChoice = true;
} else {
stateHasProbabilisticChoice = true;
}
buf = forwardToNextLine(buf, lineEndings);
// Now that we have the source state and the information whether or not the current choice is probabilistic or Markovian, we need to read the list of successors and the probabilities/rates.
bool hasSuccessorState = false;
bool encounteredNewDistribution = false;
uint_fast64_t lastSuccessorState = 0;
// At this point, we need to check whether there is an additional successor or we have reached the next choice for the same or a different state.
do {
buf = trimWhitespaces(buf);
// If the end of the file was reached, we need to abort and check whether we are in a legal state.
if (buf[0] == '\0') {
if (!hasSuccessorState) {
LOG4CPLUS_ERROR(logger, "Premature end-of-file. Expected at least one successor state for state " << source << ".");
throw storm::exceptions::WrongFormatException() << "Premature end-of-file. Expected at least one successor state for state " << source << ".";
} else {
// If there was at least one successor for the current choice, this is legal and we need to move on.
encounteredEOF = true;
}
} else if (buf[0] == '*') {
// As we have encountered a "*", we know that there is an additional successor state for the current choice.
buf= skipWord(buf);
// Now we need to read the successor state and check if we already saw a higher state index.
target = checked_strtol(buf, &buf);
if (target > result.highestStateIndex) {
result.highestStateIndex = target;
}
if (hasSuccessorState && target <= lastSuccessorState) {
LOG4CPLUS_ERROR(logger, "Illegal transition order for source state " << source << ".");
throw storm::exceptions::WrongFormatException() << "Illegal transition order for source state " << source << ".";
}
// And the corresponding probability/rate.
double val = checked_strtod(buf, &buf);
if (val <= 0.0) {
LOG4CPLUS_ERROR(logger, "Illegal probability/rate value for transition from " << source << " to " << target << ": " << val << ".");
throw storm::exceptions::WrongFormatException() << "Illegal probability/rate value for transition from " << source << " to " << target << ": " << val << ".";
}
// We need to record that we found at least one successor state for the current choice.
hasSuccessorState = true;
lastSuccessorState = target;
// As we found a new successor, we need to increase the number of nonzero entries.
++result.numberOfNonzeroEntries;
buf = forwardToNextLine(buf, lineEndings);
} else {
// If it was not a "*", we have to assume that we encountered the beginning of a new choice definition. In this case, we don't move the pointer
// to the buffer, because we still need to read the new source state.
encounteredNewDistribution = true;
}
} while (!encounteredEOF && !encounteredNewDistribution);
}
return result;
}
MarkovAutomatonSparseTransitionParser::ResultType MarkovAutomatonSparseTransitionParser::secondPass(char* buf, SupportedLineEndingsEnum lineEndings, FirstPassResult const& firstPassResult) {
ResultType result(firstPassResult);
bool fixDeadlocks = storm::settings::Settings::getInstance()->isSet("fixDeadlocks");
// Skip the format hint.
buf = storm::parser::forwardToNextLine(buf, lineEndings);
// Now read the transitions.
int_fast64_t source, target = -1;
int_fast64_t lastsource = -1;
bool encounteredEOF = false;
uint_fast64_t currentChoice = 0;
while (buf[0] != '\0' && !encounteredEOF) {
// At the current point, the next thing to read is the source state of the next choice to come.
source = checked_strtol(buf, &buf);
// If we have skipped some states, we need to insert self-loops if requested.
if (source > lastsource + 1) {
if (fixDeadlocks) {
for (uint_fast64_t index = lastsource + 1; index < source; ++index) {
result.nondeterministicChoiceIndices[index] = currentChoice;
result.transitionMatrix.addNextValue(currentChoice, index, 1);
++currentChoice;
}
} else {
LOG4CPLUS_ERROR(logger, "Found deadlock states (e.g. " << lastsource + 1 << ") during parsing. Please fix them or set the appropriate flag.");
throw storm::exceptions::WrongFormatException() << "Found deadlock states (e.g. " << lastsource + 1 << ") during parsing. Please fix them or set the appropriate flag.";
}
}
if (source != lastsource) {
// If we skipped to a new state we need to record the beginning of the choices in the nondeterministic choice indices.
result.nondeterministicChoiceIndices[source] = currentChoice;
}
// Record that the current source was the last source.
lastsource = source;
buf = trimWhitespaces(buf);
// Depending on the action name, the choice is either a probabilitic one or a markovian one.
bool isMarkovianChoice = false;
if (buf[0] == '!' && skipWord(buf) - buf == 1) {
isMarkovianChoice = true;
// Mark the current state as a Markovian one.
result.markovianStates.set(source, true);
} else {
isMarkovianChoice = false;
}
buf = forwardToNextLine(buf, lineEndings);
// Now that we have the source state and the information whether or not the current choice is probabilistic or Markovian, we need to read the list of successors and the probabilities/rates.
bool hasSuccessorState = false;
bool encounteredNewDistribution = false;
// At this point, we need to check whether there is an additional successor or we have reached the next choice for the same or a different state.
do {
buf = trimWhitespaces(buf);
// If the end of the file was reached, we need to abort and check whether we are in a legal state.
if (buf[0] == '\0') {
// Under the assumption that the currently open choice has at least one successor (which is given after the first run)
// we may legally stop reading here.
encounteredEOF = true;
} else if (buf[0] == '*') {
// We need to record that we found at least one successor state for the current choice.
hasSuccessorState = true;
// As we have encountered a "*", we know that there is an additional successor state for the current choice.
buf = skipWord(buf);
// Now we need to read the successor state and check if we already saw a higher state index.
target = checked_strtol(buf, &buf);
// And the corresponding probability/rate.
double val = checked_strtod(buf, &buf);
// Record the value as well as the exit rate in case of a Markovian choice.
result.transitionMatrix.addNextValue(currentChoice, target, val);
if (isMarkovianChoice) {
result.exitRates[source] += val;
}
buf = forwardToNextLine(buf, lineEndings);
} else {
// If it was not a "*", we have to assume that we encountered the beginning of a new choice definition. In this case, we don't move the pointer
// to the buffer, because we still need to read the new source state.
encounteredNewDistribution = true;
}
} while (!encounteredEOF && !encounteredNewDistribution);
++currentChoice;
}
// As we have added all entries at this point, we need to finalize the matrix.
result.transitionMatrix.finalize();
// Put a sentinel element at the end.
result.nondeterministicChoiceIndices[firstPassResult.highestStateIndex + 1] = currentChoice;
return result;
}
MarkovAutomatonSparseTransitionParser::ResultType MarkovAutomatonSparseTransitionParser::parseMarkovAutomatonTransitions(std::string const& filename) {
// Set the locale to correctly recognize floating point numbers.
setlocale(LC_NUMERIC, "C");
if (!fileExistsAndIsReadable(filename.c_str())) {
LOG4CPLUS_ERROR(logger, "Error while parsing " << filename << ": File does not exist or is not readable.");
throw storm::exceptions::WrongFormatException() << "Error while parsing " << filename << ": File does not exist or is not readable.";
}
// Determine used line endings.
SupportedLineEndingsEnum lineEndings = findUsedLineEndings(filename, true);
// Open file and prepare pointer to buffer.
MappedFile file(filename.c_str());
char* buf = file.data;
return secondPass(buf, lineEndings, firstPass(buf, lineEndings));
}
} // namespace parser
namespace parser {
MarkovAutomatonSparseTransitionParser::FirstPassResult MarkovAutomatonSparseTransitionParser::firstPass(char* buf, SupportedLineEndingsEnum lineEndings) {
MarkovAutomatonSparseTransitionParser::FirstPassResult result;
bool fixDeadlocks = storm::settings::Settings::getInstance()->isSet("fixDeadlocks");
// Skip the format hint.
buf = storm::parser::forwardToNextLine(buf, lineEndings);
// Now read the transitions.
int_fast64_t source, target = -1;
int_fast64_t lastsource = -1;
bool encounteredEOF = false;
bool stateHasMarkovianChoice = false;
bool stateHasProbabilisticChoice = false;
while (buf[0] != '\0' && !encounteredEOF) {
// At the current point, the next thing to read is the source state of the next choice to come.
source = checked_strtol(buf, &buf);
// Check if we encountered a state index that is bigger than all previously seen ones and record it if necessary.
if (source > result.highestStateIndex) {
result.highestStateIndex = source;
}
// If we have skipped some states, we need to reserve the space for the self-loop insertion in the second pass.
if (source > lastsource + 1) {
if (fixDeadlocks) {
result.numberOfNonzeroEntries += source - lastsource - 1;
result.numberOfChoices += source - lastsource - 1;
} else {
LOG4CPLUS_ERROR(logger, "Found deadlock states (e.g. " << lastsource + 1 << ") during parsing. Please fix them or set the appropriate flag.");
throw storm::exceptions::WrongFormatException() << "Found deadlock states (e.g. " << lastsource + 1 << ") during parsing. Please fix them or set the appropriate flag.";
}
} else if (source < lastsource) {
LOG4CPLUS_ERROR(logger, "Illegal state choice order. A choice of state " << source << " appears at an illegal position.");
throw storm::exceptions::WrongFormatException() << "Illegal state choice order. A choice of state " << source << " appears at an illegal position.";
}
++result.numberOfChoices;
// If we have moved to the next state, we need to clear the flag that stores whether or not the source has a Markovian or probabilistic choice.
if (source != lastsource) {
stateHasMarkovianChoice = false;
stateHasProbabilisticChoice = false;
}
// Record that the current source was the last source.
lastsource = source;
buf = trimWhitespaces(buf);
// Depending on the action name, the choice is either a probabilitic one or a markovian one.
bool isMarkovianChoice = false;
if (buf[0] == '!' && skipWord(buf) - buf == 1) {
isMarkovianChoice = true;
}else {
isMarkovianChoice = false;
}
buf = skipWord(buf);
if (isMarkovianChoice) {
if (stateHasMarkovianChoice) {
LOG4CPLUS_ERROR(logger, "The state " << source << " has multiple Markovian choices.");
throw storm::exceptions::WrongFormatException() << "The state " << source << " has multiple Markovian choices.";
}
if (stateHasProbabilisticChoice) {
LOG4CPLUS_ERROR(logger, "The state " << source << " has a probabilistic choice preceding a Markovian choice. The Markovian choice must be the first choice listed.");
throw storm::exceptions::WrongFormatException() << "The state " << source << " has a probabilistic choice preceding a Markovian choice. The Markovian choice must be the first choice listed.";
}
stateHasMarkovianChoice = true;
} else {
stateHasProbabilisticChoice = true;
}
buf = forwardToNextLine(buf, lineEndings);
// Now that we have the source state and the information whether or not the current choice is probabilistic or Markovian, we need to read the list of successors and the probabilities/rates.
bool hasSuccessorState = false;
bool encounteredNewDistribution = false;
uint_fast64_t lastSuccessorState = 0;
// At this point, we need to check whether there is an additional successor or we have reached the next choice for the same or a different state.
do {
buf = trimWhitespaces(buf);
// If the end of the file was reached, we need to abort and check whether we are in a legal state.
if (buf[0] == '\0') {
if (!hasSuccessorState) {
LOG4CPLUS_ERROR(logger, "Premature end-of-file. Expected at least one successor state for state " << source << ".");
throw storm::exceptions::WrongFormatException() << "Premature end-of-file. Expected at least one successor state for state " << source << ".";
} else {
// If there was at least one successor for the current choice, this is legal and we need to move on.
encounteredEOF = true;
}
} else if (buf[0] == '*') {
// As we have encountered a "*", we know that there is an additional successor state for the current choice.
buf= skipWord(buf);
// Now we need to read the successor state and check if we already saw a higher state index.
target = checked_strtol(buf, &buf);
if (target > result.highestStateIndex) {
result.highestStateIndex = target;
}
if (hasSuccessorState && target <= lastSuccessorState) {
LOG4CPLUS_ERROR(logger, "Illegal transition order for source state " << source << ".");
throw storm::exceptions::WrongFormatException() << "Illegal transition order for source state " << source << ".";
}
// And the corresponding probability/rate.
double val = checked_strtod(buf, &buf);
if (val <= 0.0) {
LOG4CPLUS_ERROR(logger, "Illegal probability/rate value for transition from " << source << " to " << target << ": " << val << ".");
throw storm::exceptions::WrongFormatException() << "Illegal probability/rate value for transition from " << source << " to " << target << ": " << val << ".";
}
// We need to record that we found at least one successor state for the current choice.
hasSuccessorState = true;
lastSuccessorState = target;
// As we found a new successor, we need to increase the number of nonzero entries.
++result.numberOfNonzeroEntries;
buf = forwardToNextLine(buf, lineEndings);
} else {
// If it was not a "*", we have to assume that we encountered the beginning of a new choice definition. In this case, we don't move the pointer
// to the buffer, because we still need to read the new source state.
encounteredNewDistribution = true;
}
} while (!encounteredEOF && !encounteredNewDistribution);
}
return result;
}
MarkovAutomatonSparseTransitionParser::ResultType MarkovAutomatonSparseTransitionParser::secondPass(char* buf, SupportedLineEndingsEnum lineEndings, FirstPassResult const& firstPassResult) {
ResultType result(firstPassResult);
bool fixDeadlocks = storm::settings::Settings::getInstance()->isSet("fixDeadlocks");
// Skip the format hint.
buf = storm::parser::forwardToNextLine(buf, lineEndings);
// Now read the transitions.
int_fast64_t source, target = -1;
int_fast64_t lastsource = -1;
bool encounteredEOF = false;
uint_fast64_t currentChoice = 0;
while (buf[0] != '\0' && !encounteredEOF) {
// At the current point, the next thing to read is the source state of the next choice to come.
source = checked_strtol(buf, &buf);
// If we have skipped some states, we need to insert self-loops if requested.
if (source > lastsource + 1) {
if (fixDeadlocks) {
for (uint_fast64_t index = lastsource + 1; index < source; ++index) {
result.nondeterministicChoiceIndices[index] = currentChoice;
result.transitionMatrix.addNextValue(currentChoice, index, 1);
++currentChoice;
}
} else {
LOG4CPLUS_ERROR(logger, "Found deadlock states (e.g. " << lastsource + 1 << ") during parsing. Please fix them or set the appropriate flag.");
throw storm::exceptions::WrongFormatException() << "Found deadlock states (e.g. " << lastsource + 1 << ") during parsing. Please fix them or set the appropriate flag.";
}
}
if (source != lastsource) {
// If we skipped to a new state we need to record the beginning of the choices in the nondeterministic choice indices.
result.nondeterministicChoiceIndices[source] = currentChoice;
}
// Record that the current source was the last source.
lastsource = source;
buf = trimWhitespaces(buf);
// Depending on the action name, the choice is either a probabilitic one or a markovian one.
bool isMarkovianChoice = false;
if (buf[0] == '!' && skipWord(buf) - buf == 1) {
isMarkovianChoice = true;
// Mark the current state as a Markovian one.
result.markovianStates.set(source, true);
} else {
isMarkovianChoice = false;
}
buf = forwardToNextLine(buf, lineEndings);
// Now that we have the source state and the information whether or not the current choice is probabilistic or Markovian, we need to read the list of successors and the probabilities/rates.
bool encounteredNewDistribution = false;
// At this point, we need to check whether there is an additional successor or we have reached the next choice for the same or a different state.
do {
buf = trimWhitespaces(buf);
// If the end of the file was reached, we need to abort and check whether we are in a legal state.
if (buf[0] == '\0') {
// Under the assumption that the currently open choice has at least one successor (which is given after the first run)
// we may legally stop reading here.
encounteredEOF = true;
} else if (buf[0] == '*') {
// As we have encountered a "*", we know that there is an additional successor state for the current choice.
buf = skipWord(buf);
// Now we need to read the successor state and check if we already saw a higher state index.
target = checked_strtol(buf, &buf);
// And the corresponding probability/rate.
double val = checked_strtod(buf, &buf);
// Record the value as well as the exit rate in case of a Markovian choice.
result.transitionMatrix.addNextValue(currentChoice, target, val);
if (isMarkovianChoice) {
result.exitRates[source] += val;
}
buf = forwardToNextLine(buf, lineEndings);
} else {
// If it was not a "*", we have to assume that we encountered the beginning of a new choice definition. In this case, we don't move the pointer
// to the buffer, because we still need to read the new source state.
encounteredNewDistribution = true;
}
} while (!encounteredEOF && !encounteredNewDistribution);
++currentChoice;
}
// As we have added all entries at this point, we need to finalize the matrix.
result.transitionMatrix.finalize();
// Put a sentinel element at the end.
result.nondeterministicChoiceIndices[firstPassResult.highestStateIndex + 1] = currentChoice;
return result;
}
MarkovAutomatonSparseTransitionParser::ResultType MarkovAutomatonSparseTransitionParser::parseMarkovAutomatonTransitions(std::string const& filename) {
// Set the locale to correctly recognize floating point numbers.
setlocale(LC_NUMERIC, "C");
if (!fileExistsAndIsReadable(filename.c_str())) {
LOG4CPLUS_ERROR(logger, "Error while parsing " << filename << ": File does not exist or is not readable.");
throw storm::exceptions::WrongFormatException() << "Error while parsing " << filename << ": File does not exist or is not readable.";
}
// Determine used line endings.
SupportedLineEndingsEnum lineEndings = findUsedLineEndings(filename, true);
// Open file and prepare pointer to buffer.
MappedFile file(filename.c_str());
char* buf = file.data;
return secondPass(buf, lineEndings, firstPass(buf, lineEndings));
}
} // namespace parser
} // namespace storm

174
src/parser/MarkovAutomatonSparseTransitionParser.h

@ -6,90 +6,96 @@
#include "Parser.h"
namespace storm {
namespace parser {
/*
* A class providing the functionality to parse the transitions of a Markov automaton.
*/
class MarkovAutomatonSparseTransitionParser {
public:
/*
* A structure representing the result of the first pass of this parser. It contains the number of non-zero entries in the model, the highest state index
* and the total number of choices.
*/
struct FirstPassResult {
FirstPassResult() : numberOfNonzeroEntries(0), highestStateIndex(0), numberOfChoices(0) {
// Intentionally left empty.
}
// The total number of non-zero entries of the model.
uint_fast64_t numberOfNonzeroEntries;
// The highest state index that appears in the model.
uint_fast64_t highestStateIndex;
// The total number of choices in the model.
uint_fast64_t numberOfChoices;
};
/*
* A structure representing the result of the parser. It contains the sparse matrix that represents the transitions (along with a vector indicating
* at which index the choices of a given state begin) as well as the exit rates for all Markovian choices.
*/
struct ResultType {
ResultType(FirstPassResult const& firstPassResult) : transitionMatrix(firstPassResult.numberOfChoices, firstPassResult.highestStateIndex + 1), nondeterministicChoiceIndices(firstPassResult.highestStateIndex + 2), markovianChoices(firstPassResult.numberOfChoices), markovianStates(firstPassResult.highestStateIndex + 1), exitRates(firstPassResult.highestStateIndex + 1) {
transitionMatrix.initialize(firstPassResult.numberOfNonzeroEntries);
// Intentionally left empty.
}
// A matrix representing the transitions of the model.
storm::storage::SparseMatrix<double> transitionMatrix;
// A vector indicating which rows of the matrix represent the choices of a given state.
std::vector<uint_fast64_t> nondeterministicChoiceIndices;
// A bit vector indicating which choices are Markovian. By duality, all other choices are probabilitic.
storm::storage::BitVector markovianChoices;
// A bit vector indicating which states possess a Markovian choice.
storm::storage::BitVector markovianStates;
// A vector that stores the exit rates for each state. For all states that do not possess Markovian choices this is equal to 0.
std::vector<double> exitRates;
};
/*!
* Parses the given file under the assumption that it contains a Markov automaton specified in the appropriate format.
*
* @param filename The name of the file to parse.
* @return A structure representing the result of the parser.
*/
static ResultType parseMarkovAutomatonTransitions(std::string const& filename);
private:
/*
* Performs the first pass on the input pointed to by the given buffer.
*
* @param buffer The buffer that cointains the input.
* @param lineEndings The line endings that are to be used while parsing.
* @return A structure representing the result of the first pass.
*/
static FirstPassResult firstPass(char* buffer, SupportedLineEndingsEnum lineEndings);
/*
* Performs the second pass on the input pointed to by the given buffer with the information of the first pass.
*
* @param buffer The buffer that cointains the input.
* @param lineEndings The line endings that are to be used while parsing.
* @param firstPassResult The result of the first pass performed on the same input.
* @return A structure representing the result of the second pass.
*/
static ResultType secondPass(char* buffer, SupportedLineEndingsEnum lineEndings, FirstPassResult const& firstPassResult);
};
} // namespace parser
namespace parser {
/*
* A class providing the functionality to parse the transitions of a Markov automaton.
*/
class MarkovAutomatonSparseTransitionParser {
public:
/*
* A structure representing the result of the first pass of this parser. It contains the number of non-zero entries in the model, the highest state index
* and the total number of choices.
*/
struct FirstPassResult {
FirstPassResult() : numberOfNonzeroEntries(0), highestStateIndex(0), numberOfChoices(0) {
// Intentionally left empty.
}
// The total number of non-zero entries of the model.
uint_fast64_t numberOfNonzeroEntries;
// The highest state index that appears in the model.
uint_fast64_t highestStateIndex;
// The total number of choices in the model.
uint_fast64_t numberOfChoices;
};
/*
* A structure representing the result of the parser. It contains the sparse matrix that represents the transitions (along with a vector indicating
* at which index the choices of a given state begin) as well as the exit rates for all Markovian choices.
*/
struct ResultType {
/*
* Creates a new instance of the struct using the result of the first pass to correctly initialize the container.
* @param firstPassResult A reference to the result of the first pass.
*/
ResultType(FirstPassResult const& firstPassResult) : transitionMatrix(firstPassResult.numberOfChoices, firstPassResult.highestStateIndex + 1), nondeterministicChoiceIndices(firstPassResult.highestStateIndex + 2), markovianChoices(firstPassResult.numberOfChoices), markovianStates(firstPassResult.highestStateIndex + 1), exitRates(firstPassResult.highestStateIndex + 1) {
transitionMatrix.initialize(firstPassResult.numberOfNonzeroEntries);
// Intentionally left empty.
}
// A matrix representing the transitions of the model.
storm::storage::SparseMatrix<double> transitionMatrix;
// A vector indicating which rows of the matrix represent the choices of a given state.
std::vector<uint_fast64_t> nondeterministicChoiceIndices;
// A bit vector indicating which choices are Markovian. By duality, all other choices are probabilitic.
storm::storage::BitVector markovianChoices;
// A bit vector indicating which states possess a Markovian choice.
storm::storage::BitVector markovianStates;
// A vector that stores the exit rates for each state. For all states that do not possess Markovian choices this is equal to 0.
std::vector<double> exitRates;
};
/*!
* Parses the given file under the assumption that it contains a Markov automaton specified in the appropriate format.
*
* @param filename The name of the file to parse.
* @return A structure representing the result of the parser.
*/
static ResultType parseMarkovAutomatonTransitions(std::string const& filename);
private:
/*
* Performs the first pass on the input pointed to by the given buffer.
*
* @param buffer The buffer that cointains the input.
* @param lineEndings The line endings that are to be used while parsing.
* @return A structure representing the result of the first pass.
*/
static FirstPassResult firstPass(char* buffer, SupportedLineEndingsEnum lineEndings);
/*
* Performs the second pass on the input pointed to by the given buffer with the information of the first pass.
*
* @param buffer The buffer that cointains the input.
* @param lineEndings The line endings that are to be used while parsing.
* @param firstPassResult The result of the first pass performed on the same input.
* @return A structure representing the result of the second pass.
*/
static ResultType secondPass(char* buffer, SupportedLineEndingsEnum lineEndings, FirstPassResult const& firstPassResult);
};
} // namespace parser
} // namespace storm
#endif /* STORM_PARSER_MARKOVAUTOMATONSPARSETRANSITIONPARSER_H_ */

2
src/parser/NondeterministicModelParser.cpp

@ -40,7 +40,7 @@ NondeterministicModelParserResultContainer<double> parseNondeterministicModel(st
NondeterministicModelParserResultContainer<double> result(std::move(resultTransitionSystem), std::move(nondeterministicSparseTransitionParserResult.second), std::move(resultLabeling));
if (stateRewardFile != "") {
result.stateRewards.reset(storm::parser::SparseStateRewardParser(stateCount, stateRewardFile));
result.stateRewards.reset(storm::parser::SparseStateRewardParser::parseSparseStateReward(stateCount, stateRewardFile));
}
if (transitionRewardFile != "") {
RewardMatrixInformationStruct* rewardMatrixInfo = new RewardMatrixInformationStruct(rowCount, stateCount, &result.rowMapping);

78
src/parser/Parser.cpp

@ -13,6 +13,10 @@
#include "log4cplus/loggingmacros.h"
extern log4cplus::Logger logger;
namespace storm {
namespace parser {
/*!
* Calls strtol() internally and checks if the new pointer is different
* from the original one, i.e. if str != *end. If they are the same, a
@ -21,7 +25,7 @@ extern log4cplus::Logger logger;
* @param end New pointer will be written there
* @return Result of strtol()
*/
uint_fast64_t storm::parser::checked_strtol(const char* str, char** end) {
uint_fast64_t checked_strtol(const char* str, char** end) {
uint_fast64_t res = strtol(str, end, 10);
if (str == *end) {
LOG4CPLUS_ERROR(logger, "Error while parsing integer. Next input token is not a number.");
@ -39,7 +43,7 @@ uint_fast64_t storm::parser::checked_strtol(const char* str, char** end) {
* @param end New pointer will be written there
* @return Result of strtod()
*/
double storm::parser::checked_strtod(const char* str, char** end) {
double checked_strtod(const char* str, char** end) {
double res = strtod(str, end);
if (str == *end) {
LOG4CPLUS_ERROR(logger, "Error while parsing floating point. Next input token is not a number.");
@ -53,7 +57,7 @@ double storm::parser::checked_strtod(const char* str, char** end) {
* @brief Tests whether the given file exists and is readable.
* @return True iff the file exists and is readable.
*/
bool storm::parser::fileExistsAndIsReadable(const char* fileName) {
bool fileExistsAndIsReadable(const char* fileName) {
std::ifstream fin(fileName);
bool returnValue = !fin.fail();
return returnValue;
@ -62,8 +66,10 @@ bool storm::parser::fileExistsAndIsReadable(const char* fileName) {
/*!
* Skips all numbers, letters and special characters.
* Returns a pointer to the first char that is a whitespace.
* @param buf The string buffer to operate on.
* @return A pointer to the first whitespace character.
*/
char* storm::parser::skipWord(char* buf){
char* skipWord(char* buf){
while((*buf != ' ') && (*buf != '\t') && (*buf != '\n') && (*buf != '\r')) buf++;
return buf;
}
@ -71,10 +77,10 @@ char* storm::parser::skipWord(char* buf){
/*!
* Skips spaces, tabs, newlines and carriage returns. Returns a pointer
* to first char that is not a whitespace.
* @param buf String buffer
* @return pointer to first non-whitespace character
* @param buf The string buffer to operate on.
* @return A pointer to the first non-whitespace character.
*/
char* storm::parser::trimWhitespaces(char* buf) {
char* trimWhitespaces(char* buf) {
while ((*buf == ' ') || (*buf == '\t') || (*buf == '\n') || (*buf == '\r')) buf++;
return buf;
}
@ -82,7 +88,7 @@ char* storm::parser::trimWhitespaces(char* buf) {
/*!
* @briefs Analyzes the given file and tries to find out the used file endings.
*/
storm::parser::SupportedLineEndingsEnum storm::parser::findUsedLineEndings(std::string const& fileName, bool throwOnUnsupported) {
SupportedLineEndingsEnum findUsedLineEndings(std::string const& fileName, bool throwOnUnsupported) {
MappedFile fileMap(fileName.c_str());
char* buf = nullptr;
char* const bufferEnd = fileMap.dataend;
@ -91,11 +97,11 @@ storm::parser::SupportedLineEndingsEnum storm::parser::findUsedLineEndings(std::
if (*buf == '\r') {
// check for following \n
if (((buf + sizeof(char)) < bufferEnd) && (*(buf + sizeof(char)) == '\n')) {
return storm::parser::SupportedLineEndingsEnum::SlashRN;
return SupportedLineEndingsEnum::SlashRN;
}
return storm::parser::SupportedLineEndingsEnum::SlashR;
return SupportedLineEndingsEnum::SlashR;
} else if (*buf == '\n') {
return storm::parser::SupportedLineEndingsEnum::SlashN;
return SupportedLineEndingsEnum::SlashN;
}
}
@ -105,25 +111,25 @@ storm::parser::SupportedLineEndingsEnum storm::parser::findUsedLineEndings(std::
}
LOG4CPLUS_WARN(logger, "Error while parsing \"" << fileName << "\": Unsupported or unknown line-endings. Please use either of \\r, \\n or \\r\\n");
return storm::parser::SupportedLineEndingsEnum::Unsupported;
return SupportedLineEndingsEnum::Unsupported;
}
/*!
* @brief Encapsulates the usage of function @strchr to forward to the next line
*/
char* storm::parser::forwardToNextLine(char* buffer, storm::parser::SupportedLineEndingsEnum lineEndings) {
char* forwardToNextLine(char* buffer, SupportedLineEndingsEnum lineEndings) {
switch (lineEndings) {
case storm::parser::SupportedLineEndingsEnum::SlashN:
case SupportedLineEndingsEnum::SlashN:
return strchr(buffer, '\n') + 1;
break;
case storm::parser::SupportedLineEndingsEnum::SlashR:
case SupportedLineEndingsEnum::SlashR:
return strchr(buffer, '\r') + 1;
break;
case storm::parser::SupportedLineEndingsEnum::SlashRN:
case SupportedLineEndingsEnum::SlashRN:
return strchr(buffer, '\r') + 2;
break;
default:
case storm::parser::SupportedLineEndingsEnum::Unsupported:
case SupportedLineEndingsEnum::Unsupported:
// This Line will never be reached as the Parser would have thrown already.
throw;
break;
@ -136,26 +142,26 @@ char* storm::parser::forwardToNextLine(char* buffer, storm::parser::SupportedLin
* @param targetBuffer The Target for the hint, must be at least 64 bytes long
* @param buffer The Source Buffer from which the Model Hint will be read
*/
void storm::parser::scanForModelHint(char* targetBuffer, uint_fast64_t targetBufferSize, char const* buffer, storm::parser::SupportedLineEndingsEnum lineEndings) {
void scanForModelHint(char* targetBuffer, uint_fast64_t targetBufferSize, char const* buffer, SupportedLineEndingsEnum lineEndings) {
if (targetBufferSize <= 4) {
throw;
}
switch (lineEndings) {
case storm::parser::SupportedLineEndingsEnum::SlashN:
case SupportedLineEndingsEnum::SlashN:
#ifdef WINDOWS
sscanf_s(buffer, "%60s\n", targetBuffer, targetBufferSize);
#else
sscanf(buffer, "%60s\n", targetBuffer);
#endif
break;
case storm::parser::SupportedLineEndingsEnum::SlashR:
case SupportedLineEndingsEnum::SlashR:
#ifdef WINDOWS
sscanf_s(buffer, "%60s\r", targetBuffer, sizeof(targetBufferSize));
#else
sscanf(buffer, "%60s\r", targetBuffer);
#endif
break;
case storm::parser::SupportedLineEndingsEnum::SlashRN:
case SupportedLineEndingsEnum::SlashRN:
#ifdef WINDOWS
sscanf_s(buffer, "%60s\r\n", targetBuffer, sizeof(targetBufferSize));
#else
@ -163,7 +169,7 @@ void storm::parser::scanForModelHint(char* targetBuffer, uint_fast64_t targetBuf
#endif
break;
default:
case storm::parser::SupportedLineEndingsEnum::Unsupported:
case SupportedLineEndingsEnum::Unsupported:
// This Line will never be reached as the Parser would have thrown already.
throw;
break;
@ -173,9 +179,9 @@ void storm::parser::scanForModelHint(char* targetBuffer, uint_fast64_t targetBuf
/*!
* @brief Returns the matching Separator-String in the format of "BLANK\t\NEWLINESYMBOL(S)\0
*/
void storm::parser::getMatchingSeparatorString(char* targetBuffer, uint_fast64_t targetBufferSize, storm::parser::SupportedLineEndingsEnum lineEndings) {
void getMatchingSeparatorString(char* targetBuffer, uint_fast64_t targetBufferSize, SupportedLineEndingsEnum lineEndings) {
if (targetBufferSize < 5) {
LOG4CPLUS_ERROR(logger, "storm::parser::getMatchingSeparatorString: The passed Target Buffer is too small.");
LOG4CPLUS_ERROR(logger, "getMatchingSeparatorString: The passed Target Buffer is too small.");
throw;
}
switch (lineEndings) {
@ -209,7 +215,7 @@ void storm::parser::getMatchingSeparatorString(char* targetBuffer, uint_fast64_t
default:
case SupportedLineEndingsEnum::Unsupported:
// This Line will never be reached as the Parser would have thrown already.
LOG4CPLUS_ERROR(logger, "storm::parser::getMatchingSeparatorString: The passed lineEndings were Unsupported. Check your input file.");
LOG4CPLUS_ERROR(logger, "getMatchingSeparatorString: The passed lineEndings were Unsupported. Check your input file.");
throw;
break;
}
@ -221,7 +227,7 @@ void storm::parser::getMatchingSeparatorString(char* targetBuffer, uint_fast64_t
* and a log entry is written.
* @param filename file to be opened
*/
storm::parser::MappedFile::MappedFile(const char* filename) {
MappedFile::MappedFile(const char* filename) {
#if defined LINUX || defined MACOSX
/*
* Do file mapping for reasonable systems.
@ -233,20 +239,20 @@ storm::parser::MappedFile::MappedFile(const char* filename) {
if (stat64(filename, &(this->st)) != 0) {
#endif
LOG4CPLUS_ERROR(logger, "Error in stat(" << filename << "): Probably, this file does not exist.");
throw exceptions::FileIoException() << "storm::parser::MappedFile Error in stat(): Probably, this file does not exist.";
throw exceptions::FileIoException() << "MappedFile Error in stat(): Probably, this file does not exist.";
}
this->file = open(filename, O_RDONLY);
if (this->file < 0) {
LOG4CPLUS_ERROR(logger, "Error in open(" << filename << "): Probably, we may not read this file.");
throw exceptions::FileIoException() << "storm::parser::MappedFile Error in open(): Probably, we may not read this file.";
throw exceptions::FileIoException() << "MappedFile Error in open(): Probably, we may not read this file.";
}
this->data = reinterpret_cast<char*>(mmap(NULL, this->st.st_size, PROT_READ, MAP_PRIVATE, this->file, 0));
if (this->data == reinterpret_cast<char*>(-1)) {
close(this->file);
LOG4CPLUS_ERROR(logger, "Error in mmap(" << filename << "): " << std::strerror(errno));
throw exceptions::FileIoException() << "storm::parser::MappedFile Error in mmap(): " << std::strerror(errno);
throw exceptions::FileIoException() << "MappedFile Error in mmap(): " << std::strerror(errno);
}
this->dataend = this->data + this->st.st_size;
#elif defined WINDOWS
@ -256,20 +262,20 @@ storm::parser::MappedFile::MappedFile(const char* filename) {
*/
if (_stat64(filename, &(this->st)) != 0) {
LOG4CPLUS_ERROR(logger, "Error in _stat(" << filename << "): Probably, this file does not exist.");
throw exceptions::FileIoException("storm::parser::MappedFile Error in stat(): Probably, this file does not exist.");
throw exceptions::FileIoException("MappedFile Error in stat(): Probably, this file does not exist.");
}
this->file = CreateFileA(filename, GENERIC_READ, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (this->file == INVALID_HANDLE_VALUE) {
LOG4CPLUS_ERROR(logger, "Error in CreateFileA(" << filename << "): Probably, we may not read this file.");
throw exceptions::FileIoException("storm::parser::MappedFile Error in CreateFileA(): Probably, we may not read this file.");
throw exceptions::FileIoException("MappedFile Error in CreateFileA(): Probably, we may not read this file.");
}
this->mapping = CreateFileMappingA(this->file, NULL, PAGE_READONLY, (DWORD)(st.st_size >> 32), (DWORD)st.st_size, NULL);
if (this->mapping == NULL) {
CloseHandle(this->file);
LOG4CPLUS_ERROR(logger, "Error in CreateFileMappingA(" << filename << ").");
throw exceptions::FileIoException("storm::parser::MappedFile Error in CreateFileMappingA().");
throw exceptions::FileIoException("MappedFile Error in CreateFileMappingA().");
}
this->data = static_cast<char*>(MapViewOfFile(this->mapping, FILE_MAP_READ, 0, 0, this->st.st_size));
@ -277,7 +283,7 @@ storm::parser::MappedFile::MappedFile(const char* filename) {
CloseHandle(this->mapping);
CloseHandle(this->file);
LOG4CPLUS_ERROR(logger, "Error in MapViewOfFile(" << filename << ").");
throw exceptions::FileIoException("storm::parser::MappedFile Error in MapViewOfFile().");
throw exceptions::FileIoException("MappedFile Error in MapViewOfFile().");
}
this->dataend = this->data + this->st.st_size;
#endif
@ -286,7 +292,7 @@ storm::parser::MappedFile::MappedFile(const char* filename) {
/*!
* Will unmap the data and close the file.
*/
storm::parser::MappedFile::~MappedFile() {
MappedFile::~MappedFile() {
#if defined LINUX || defined MACOSX
munmap(this->data, this->st.st_size);
close(this->file);
@ -295,3 +301,7 @@ storm::parser::MappedFile::~MappedFile() {
CloseHandle(this->file);
#endif
}
} // namespace parser
} // namespace storm

207
src/parser/Parser.h

@ -34,146 +34,147 @@ namespace storm {
*/
namespace parser {
struct RewardMatrixInformationStruct {
RewardMatrixInformationStruct() : rowCount(0), columnCount(0), nondeterministicChoiceIndices(nullptr) {
// Intentionally left empty.
}
struct RewardMatrixInformationStruct {
RewardMatrixInformationStruct() : rowCount(0), columnCount(0), nondeterministicChoiceIndices(nullptr) {
// Intentionally left empty.
}
RewardMatrixInformationStruct(uint_fast64_t rowCount, uint_fast64_t columnCount, std::vector<uint_fast64_t> const * const nondeterministicChoiceIndices)
: rowCount(rowCount), columnCount(columnCount), nondeterministicChoiceIndices(nondeterministicChoiceIndices) {
// Intentionally left empty.
}
RewardMatrixInformationStruct(uint_fast64_t rowCount, uint_fast64_t columnCount, std::vector<uint_fast64_t> const * const nondeterministicChoiceIndices)
: rowCount(rowCount), columnCount(columnCount), nondeterministicChoiceIndices(nondeterministicChoiceIndices) {
// Intentionally left empty.
}
uint_fast64_t rowCount;
uint_fast64_t columnCount;
std::vector<uint_fast64_t> const * const nondeterministicChoiceIndices;
};
uint_fast64_t rowCount;
uint_fast64_t columnCount;
std::vector<uint_fast64_t> const * const nondeterministicChoiceIndices;
};
/*!
* @brief Opens a file and maps it to memory providing a char*
* containing the file content.
*
* This class is a very simple interface to read files efficiently.
* The given file is opened and mapped to memory using mmap().
* The public member data is a pointer to the actual file content.
* Using this method, the kernel will take care of all buffering. This is
* most probably much more efficient than doing this manually.
*/
/*!
* @brief Opens a file and maps it to memory providing a char*
* containing the file content.
*
* This class is a very simple interface to read files efficiently.
* The given file is opened and mapped to memory using mmap().
* The public member data is a pointer to the actual file content.
* Using this method, the kernel will take care of all buffering. This is
* most probably much more efficient than doing this manually.
*/
#if !defined LINUX && !defined MACOSX && !defined WINDOWS
#error Platform not supported
#error Platform not supported
#endif
class MappedFile {
private:
class MappedFile {
private:
#if defined LINUX || defined MACOSX
/*!
* @brief file descriptor obtained by open().
*/
int file;
/*!
* @brief file descriptor obtained by open().
*/
int file;
#elif defined WINDOWS
HANDLE file;
HANDLE mapping;
HANDLE file;
HANDLE mapping;
#endif
#if defined LINUX
/*!
* @brief stat information about the file.
*/
struct stat64 st;
/*!
* @brief stat information about the file.
*/
struct stat64 st;
#elif defined MACOSX
/*!
* @brief stat information about the file.
*/
struct stat st;
/*!
* @brief stat information about the file.
*/
struct stat st;
#elif defined WINDOWS
/*!
* @brief stat information about the file.
*/
struct __stat64 st;
/*!
* @brief stat information about the file.
*/
struct __stat64 st;
#endif
public:
/*!
* @brief pointer to actual file content.
*/
char* data;
/*!
* @brief pointer to end of file content.
*/
char* dataend;
public:
/*!
* @brief Constructor of MappedFile.
* @brief pointer to actual file content.
*/
MappedFile(const char* filename);
char* data;
/*!
* @brief Destructor of MappedFile.
* @brief pointer to end of file content.
*/
~MappedFile();
};
char* dataend;
/*!
* @brief Parses integer and checks, if something has been parsed.
* @brief Constructor of MappedFile.
*/
uint_fast64_t checked_strtol(const char* str, char** end);
/*!
* @brief Parses floating point and checks, if something has been parsed.
*/
double checked_strtod(const char* str, char** end);
MappedFile(const char* filename);
/*!
* @brief Skips all non whitespace characters until the next whitespace.
* @brief Destructor of MappedFile.
*/
char* skipWord(char* buf);
~MappedFile();
};
/*!
* @brief Skips common whitespaces in a string.
*/
char* trimWhitespaces(char* buf);
/*!
* @brief Parses integer and checks, if something has been parsed.
*/
uint_fast64_t checked_strtol(const char* str, char** end);
/*!
* @brief Tests whether the given file exists and is readable.
*/
bool fileExistsAndIsReadable(const char* fileName);
/*!
* @brief Parses floating point and checks, if something has been parsed.
*/
double checked_strtod(const char* str, char** end);
/*!
* @brief Enum Class Type containing all supported file endings.
*/
enum class SupportedLineEndingsEnum : unsigned short {
Unsupported = 0,
SlashR,
SlashN,
SlashRN
};
/*!
* @brief Skips all non whitespace characters until the next whitespace.
*/
char* skipWord(char* buf);
/*!
* @briefs Analyzes the given file and tries to find out the used line endings.
*/
storm::parser::SupportedLineEndingsEnum findUsedLineEndings(std::string const& fileName, bool throwOnUnsupported = false);
/*!
* @brief Skips common whitespaces in a string.
*/
char* trimWhitespaces(char* buf);
/*!
* @brief Encapsulates the usage of function @strchr to forward to the next line
*/
char* forwardToNextLine(char* buffer, storm::parser::SupportedLineEndingsEnum lineEndings);
/*!
* @brief Tests whether the given file exists and is readable.
*/
bool fileExistsAndIsReadable(const char* fileName);
/*!
* @brief Encapsulates the usage of function @sscanf to scan for the model type hint
* @param targetBuffer The Target for the hint, should be at least 64 bytes long
* @param buffer The Source Buffer from which the Model Hint will be read
/*!
* @brief Enum Class Type containing all supported file endings.
*/
enum class SupportedLineEndingsEnum : unsigned short {
Unsupported = 0,
SlashR,
SlashN,
SlashRN
};
*/
void scanForModelHint(char* targetBuffer, uint_fast64_t targetBufferSize, char const* buffer, storm::parser::SupportedLineEndingsEnum lineEndings);
/*!
* @briefs Analyzes the given file and tries to find out the used line endings.
*/
storm::parser::SupportedLineEndingsEnum findUsedLineEndings(std::string const& fileName, bool throwOnUnsupported = false);
/*!
* @brief Returns the matching Separator-String in the format of "BLANK\t\NEWLINESYMBOL(S)\0
*/
void getMatchingSeparatorString(char* targetBuffer, uint_fast64_t targetBufferSize, storm::parser::SupportedLineEndingsEnum lineEndings);
/*!
* @brief Encapsulates the usage of function @strchr to forward to the next line
*/
char* forwardToNextLine(char* buffer, storm::parser::SupportedLineEndingsEnum lineEndings);
/*!
* @brief Encapsulates the usage of function @sscanf to scan for the model type hint
* @param targetBuffer The Target for the hint, should be at least 64 bytes long
* @param buffer The Source Buffer from which the Model Hint will be read
*/
void scanForModelHint(char* targetBuffer, uint_fast64_t targetBufferSize, char const* buffer, storm::parser::SupportedLineEndingsEnum lineEndings);
/*!
* @brief Returns the matching Separator-String in the format of "BLANK\t\NEWLINESYMBOL(S)\0
*/
void getMatchingSeparatorString(char* targetBuffer, uint_fast64_t targetBufferSize, storm::parser::SupportedLineEndingsEnum lineEndings);
} // namespace parser
} // namespace storm
#endif /* STORM_PARSER_PARSER_H_ */

48
src/parser/SparseStateRewardParser.cpp

@ -7,29 +7,18 @@
#include "src/parser/SparseStateRewardParser.h"
#include <errno.h>
#include <time.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <locale.h>
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <string>
#include <vector>
#include <clocale>
#include "src/exceptions/WrongFormatException.h"
#include "src/exceptions/FileIoException.h"
#include "src/utility/OsDetection.h"
#include "src/parser/Parser.h"
#include "log4cplus/logger.h"
#include "log4cplus/loggingmacros.h"
extern log4cplus::Logger logger;
namespace storm {
namespace parser {
namespace parser {
/*!
* Reads a state reward file and puts the result in a state reward vector.
@ -38,7 +27,7 @@ namespace parser {
* @param filename The filename of the state reward file.
* @return The created state reward vector.
*/
std::vector<double> SparseStateRewardParser(uint_fast64_t stateCount, std::string const & filename) {
std::vector<double> SparseStateRewardParser::parseSparseStateReward(uint_fast64_t stateCount, std::string const & filename) {
// Open file.
if (!fileExistsAndIsReadable(filename.c_str())) {
LOG4CPLUS_ERROR(logger, "Error while parsing " << filename << ": File does not exist or is not readable.");
@ -51,28 +40,27 @@ std::vector<double> SparseStateRewardParser(uint_fast64_t stateCount, std::strin
// Create state reward vector with given state count.
std::vector<double> stateRewards(stateCount);
{
// Now parse state reward assignments.
uint_fast64_t state;
double reward;
// Now parse state reward assignments.
uint_fast64_t state;
double reward;
// Iterate over states.
while (buf[0] != '\0') {
// Parse state number and reward value.
state = checked_strtol(buf, &buf);
reward = checked_strtod(buf, &buf);
if (reward < 0.0) {
LOG4CPLUS_ERROR(logger, "Expected positive reward value but got \"" << reward << "\".");
throw storm::exceptions::WrongFormatException() << "State reward file specifies illegal reward value.";
}
// Iterate over states.
while (buf[0] != '\0') {
// Parse state number and reward value.
state = checked_strtol(buf, &buf);
reward = checked_strtod(buf, &buf);
if (reward < 0.0) {
LOG4CPLUS_ERROR(logger, "Expected positive reward value but got \"" << reward << "\".");
throw storm::exceptions::WrongFormatException() << "State reward file specifies illegal reward value.";
}
stateRewards[state] = reward;
stateRewards[state] = reward;
buf = trimWhitespaces(buf);
}
buf = trimWhitespaces(buf);
}
return stateRewards;
}
} // namespace parser
} // namespace storm

15
src/parser/SparseStateRewardParser.h

@ -2,18 +2,25 @@
#define STORM_PARSER_SPARSESTATEREWARDPARSER_H_
#include <cstdint>
#include "src/parser/Parser.h"
#include <memory>
#include <vector>
#include <string>
namespace storm {
namespace parser {
/*!
* @brief Load state reward file and return vector of state rewards.
* A class providing the functionality to parse a the state rewards of a model.
*/
std::vector<double> SparseStateRewardParser(uint_fast64_t stateCount, std::string const &filename);
class SparseStateRewardParser {
public:
/*!
* @brief Load state reward file and return vector of state rewards.
*/
static std::vector<double> parseSparseStateReward(uint_fast64_t stateCount, std::string const &filename);
};
} // namespace parser

39
test/functional/parser/MarkovAutomatonParserTest.cpp

@ -9,8 +9,13 @@
#include "storm-config.h"
#include "src/settings/Settings.h"
#include <cmath>
#include <vector>
#include "src/parser/MarkovAutomatonSparseTransitionParser.h"
#include "src/parser/SparseStateRewardParser.h"
#include "src/parser/Parser.h"
#include "src/parser/MarkovAutomatonParser.h"
#define STATE_COUNT 6
#define CHOICE_COUNT 7
@ -176,3 +181,37 @@ TEST(MarkovAutomatonSparseTransitionParserTest, DeadlockTest) {
storm::settings::Settings::getInstance()->set("fixDeadlocks");
}
}*/
double round(double val, int precision)
{
std::stringstream s;
s << std::setprecision(precision) << std::setiosflags(std::ios_base::fixed) << val;
s >> val;
return val;
}
TEST(SparseStateRewardParserTest, BasicParseTest) {
// Get the parsing result.
std::vector<double> result = storm::parser::SparseStateRewardParser::parseSparseStateReward(100, STORM_CPP_TESTS_BASE_PATH "/functional/parser/rew_files/state_reward_parser_basic.state.rew");
// Now test if the correct value were parsed.
for(int i = 0; i < 100; i++) {
ASSERT_EQ(std::round(result[i]) , std::round(2*i + 15/13*i*i - 1.5/(i+0.1) + 15.7));
}
}
TEST(MarkovAutomatonParserTest, BasicParseTest) {
// Get the parsing result.
storm::models::MarkovAutomaton<double> result = storm::parser::MarkovAutomatonParser::parseMarkovAutomaton(STORM_CPP_TESTS_BASE_PATH "/functional/parser/tra_files/ma_general_input_01.tra", STORM_CPP_TESTS_BASE_PATH "/functional/parser/lab_files/ma_general_input_01.lab", STORM_CPP_TESTS_BASE_PATH "/functional/parser/rew_files/ma_general_input_01.state.rew", "");
// Test sizes and counts.
ASSERT_EQ(result.getNumberOfStates(), STATE_COUNT);
ASSERT_EQ(result.getNumberOfChoices(), CHOICE_COUNT);
ASSERT_EQ(result.getNumberOfTransitions(), 12);
// Test
std::vector<double> rates = result.getExitRates();
ASSERT_EQ(rates[0], 2);
}
Loading…
Cancel
Save