/*! * TraParser.cpp * * Created on: 20.11.2012 * Author: Gereon Kremer */ #include "src/parser/DeterministicSparseTransitionParser.h" #include #include #include #include #include #include #include "src/utility/constants.h" #include "src/exceptions/FileIoException.h" #include "src/exceptions/WrongFormatException.h" #include "src/settings/Settings.h" #include "log4cplus/logger.h" #include "log4cplus/loggingmacros.h" extern log4cplus::Logger logger; namespace storm { namespace parser { storm::storage::SparseMatrix DeterministicSparseTransitionParser::parseDeterministicTransitions(std::string const& filename, bool insertDiagonalEntriesIfMissing) { RewardMatrixInformationStruct nullInformation; return DeterministicSparseTransitionParser::parse(filename, false, nullInformation, insertDiagonalEntriesIfMissing); } storm::storage::SparseMatrix DeterministicSparseTransitionParser::parseDeterministicTransitionRewards(std::string const& filename, RewardMatrixInformationStruct const& rewardMatrixInformation) { return DeterministicSparseTransitionParser::parse(filename, true, rewardMatrixInformation); } DeterministicSparseTransitionParser::FirstPassResult DeterministicSparseTransitionParser::firstPass(char* buf, SupportedLineEndingsEnum lineEndings, bool insertDiagonalEntriesIfMissing) { DeterministicSparseTransitionParser::FirstPassResult result; // Skip the format hint if it is there. buf = trimWhitespaces(buf); if(buf[0] < '0' || buf[0] > '9') { buf = storm::parser::forwardToNextLine(buf, lineEndings); } // Check all transitions for non-zero diagonal entries and deadlock states. uint_fast64_t row, col, lastRow = 0; bool rowHadDiagonalEntry = false; while (buf[0] != '\0') { // Read the transition. row = checked_strtol(buf, &buf); col = checked_strtol(buf, &buf); // The actual read value is not needed here. checked_strtod(buf, &buf); // Compensate for missing diagonal entries if desired. if (insertDiagonalEntriesIfMissing) { if (lastRow != row) { if(!rowHadDiagonalEntry) { ++result.numberOfNonzeroEntries; } // Compensate for missing rows. for (uint_fast64_t skippedRow = lastRow + 1; skippedRow < row; ++skippedRow) { ++result.numberOfNonzeroEntries; } lastRow = row; rowHadDiagonalEntry = false; } if (col == row) { rowHadDiagonalEntry = true; } if (col > row && !rowHadDiagonalEntry) { rowHadDiagonalEntry = true; ++result.numberOfNonzeroEntries; } } // Check if a higher state id was found. if (row > result.highestStateIndex) result.highestStateIndex = row; if (col > result.highestStateIndex) result.highestStateIndex = col; ++result.numberOfNonzeroEntries; buf = trimWhitespaces(buf); } if(insertDiagonalEntriesIfMissing) { if (!rowHadDiagonalEntry) { ++result.numberOfNonzeroEntries; } //Compensate for missing rows at the end of the file. for (uint_fast64_t skippedRow = (uint_fast64_t)(lastRow + 1); skippedRow <= result.highestStateIndex; ++skippedRow) { ++result.numberOfNonzeroEntries; } } return result; } storm::storage::SparseMatrix DeterministicSparseTransitionParser::parse(std::string const& filename, bool isRewardFile, RewardMatrixInformationStruct const& rewardMatrixInformation, bool insertDiagonalEntriesIfMissing) { // Enforce locale where decimal point is '.'. 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::FileIoException() << "The supplied Transition input file \"" << filename << "\" does not exist or is not readable by this process."; } // Find out about the used line endings. SupportedLineEndingsEnum lineEndings = findUsedLineEndings(filename, true); // Open file. MappedFile file(filename.c_str()); char* buf = file.data; // Perform first pass, i.e. count entries that are not zero. DeterministicSparseTransitionParser::FirstPassResult firstPass = DeterministicSparseTransitionParser::firstPass(file.data, lineEndings, insertDiagonalEntriesIfMissing); LOG4CPLUS_INFO(logger, "First pass on " << filename << " shows " << firstPass.numberOfNonzeroEntries << " NonZeros."); // If first pass returned zero, the file format was wrong. if (firstPass.numberOfNonzeroEntries == 0) { LOG4CPLUS_ERROR(logger, "Error while parsing " << filename << ": empty or erroneous file format."); throw storm::exceptions::WrongFormatException(); } // Perform second pass. // Skip the format hint if it is there. buf = trimWhitespaces(buf); if(buf[0] < '0' || buf[0] > '9') { buf = storm::parser::forwardToNextLine(buf, lineEndings); } if(isRewardFile) { // The reward matrix should match the size of the transition matrix. if (firstPass.highestStateIndex + 1 > rewardMatrixInformation.rowCount || firstPass.highestStateIndex + 1 > rewardMatrixInformation.columnCount) { LOG4CPLUS_ERROR(logger, "Reward matrix has more rows or columns than transition matrix."); throw storm::exceptions::WrongFormatException() << "Reward matrix has more rows or columns than transition matrix."; } else { // If we found the right number of states or less, we set it to the number of states represented by the transition matrix. firstPass.highestStateIndex = rewardMatrixInformation.rowCount - 1; } } // Creating matrix builder here. // The actual matrix will be build once all contents are inserted. storm::storage::SparseMatrixBuilder resultMatrix(firstPass.highestStateIndex + 1, firstPass.highestStateIndex + 1, firstPass.numberOfNonzeroEntries); uint_fast64_t row, col, lastRow = 0; double val; bool fixDeadlocks = storm::settings::Settings::getInstance()->isSet("fixDeadlocks"); bool hadDeadlocks = false; bool rowHadDiagonalEntry = false; // Read all transitions from file. Note that we assume that the // transitions are listed in canonical order, otherwise this will not // work, i.e. the values in the matrix will be at wrong places. // Different parsing routines for transition systems and transition rewards. if(isRewardFile) { while (buf[0] != '\0') { // Read next transition. row = checked_strtol(buf, &buf); col = checked_strtol(buf, &buf); val = checked_strtod(buf, &buf); resultMatrix.addNextValue(row, col, val); buf = trimWhitespaces(buf); } } else { while (buf[0] != '\0') { // Read next transition. row = checked_strtol(buf, &buf); col = checked_strtol(buf, &buf); val = checked_strtod(buf, &buf); // Read probability of this transition. // Check, if the value is a probability, i.e. if it is between 0 and 1. if ((val < 0.0) || (val > 1.0)) { LOG4CPLUS_ERROR(logger, "Expected a positive probability but got \"" << val << "\"."); throw storm::exceptions::WrongFormatException(); } // Test if we moved to a new row. // Handle all incomplete or skipped rows. if (lastRow != row) { if (!rowHadDiagonalEntry) { if (insertDiagonalEntriesIfMissing) { resultMatrix.addNextValue(lastRow, lastRow, storm::utility::constantZero()); LOG4CPLUS_DEBUG(logger, "While parsing " << filename << ": state " << lastRow << " has no transition to itself. Inserted a 0-transition. (1)"); } else { LOG4CPLUS_WARN(logger, "Warning while parsing " << filename << ": state " << lastRow << " has no transition to itself."); } // No increment for lastRow. rowHadDiagonalEntry = true; } for (uint_fast64_t skippedRow = lastRow + 1; skippedRow < row; ++skippedRow) { hadDeadlocks = true; if (fixDeadlocks) { resultMatrix.addNextValue(skippedRow, skippedRow, storm::utility::constantOne()); rowHadDiagonalEntry = true; LOG4CPLUS_WARN(logger, "Warning while parsing " << filename << ": state " << skippedRow << " has no outgoing transitions. A self-loop was inserted."); } else { LOG4CPLUS_ERROR(logger, "Error while parsing " << filename << ": state " << skippedRow << " has no outgoing transitions."); // Before throwing the appropriate exception we will give notice of all deadlock states. } } lastRow = row; rowHadDiagonalEntry = false; } if (col == row) { rowHadDiagonalEntry = true; } if (col > row && !rowHadDiagonalEntry) { if (insertDiagonalEntriesIfMissing) { resultMatrix.addNextValue(row, row, storm::utility::constantZero()); LOG4CPLUS_DEBUG(logger, "While parsing " << filename << ": state " << row << " has no transition to itself. Inserted a 0-transition. (2)"); } else { LOG4CPLUS_WARN(logger, "Warning while parsing " << filename << ": state " << row << " has no transition to itself."); } rowHadDiagonalEntry = true; } resultMatrix.addNextValue(row, col, val); buf = trimWhitespaces(buf); } if (!rowHadDiagonalEntry) { if (insertDiagonalEntriesIfMissing) { resultMatrix.addNextValue(lastRow, lastRow, storm::utility::constantZero()); LOG4CPLUS_DEBUG(logger, "While parsing " << filename << ": state " << lastRow << " has no transition to itself. Inserted a 0-transition. (3)"); } else { LOG4CPLUS_WARN(logger, "Warning while parsing " << filename << ": state " << lastRow << " has no transition to itself."); } } // If we encountered deadlock and did not fix them, now is the time to throw the exception. if (!fixDeadlocks && hadDeadlocks) throw storm::exceptions::WrongFormatException() << "Some of the nodes had deadlocks. You can use --fixDeadlocks to insert self-loops on the fly."; } // Finally, build the actual matrix and return it. return resultMatrix.build(); } } // namespace parser } // namespace storm