diff --git a/src/solver/stateelimination/EliminatorBase.cpp b/src/solver/stateelimination/EliminatorBase.cpp new file mode 100644 index 000000000..43f94f329 --- /dev/null +++ b/src/solver/stateelimination/EliminatorBase.cpp @@ -0,0 +1,286 @@ +#include "src/solver/stateelimination/EliminatorBase.h" + +#include "src/utility/stateelimination.h" +#include "src/utility/macros.h" +#include "src/utility/constants.h" + +#include "src/exceptions/InvalidStateException.h" + +namespace storm { + namespace solver { + namespace stateelimination { + + using namespace storm::utility::stateelimination; + + template<typename ValueType, ScalingMode Mode> + EliminatorBase<ValueType, Mode>::EliminatorBase(storm::storage::FlexibleSparseMatrix<ValueType>& matrix, storm::storage::FlexibleSparseMatrix<ValueType>& transposedMatrix) : matrix(matrix), transposedMatrix(transposedMatrix) { + // Intentionally left empty. + } + + template<typename ValueType, ScalingMode Mode> + void EliminatorBase<ValueType, Mode>::eliminate(uint64_t row, uint64_t column, bool clearRow) { + // Start by finding the entry in the given column. + bool hasEntryInColumn = false; + ValueType columnValue = storm::utility::zero<ValueType>(); + FlexibleRowType& entriesInRow = matrix.getRow(row); + for (auto entryIt = entriesInRow.begin(), entryIte = entriesInRow.end(); entryIt != entryIte; ++entryIt) { + if (entryIt->getColumn() >= column) { + if (entryIt->getColumn() == column) { + columnValue = entryIt->getValue(); + hasEntryInColumn = true; + + // If we do not clear the row completely, we need to remove the entry in the requested column. + // All other elements are scaled with the entry anyway. + if (!clearRow) { + entriesInRow.erase(entryIt); + } + } + break; + } + } + + // Scale all entries in this row. + // Depending on the scaling mode, we scale the other entries of the row. + STORM_LOG_TRACE((hasEntryInColumn ? "State has entry in column." : "State does not have entry in column.")); + if (Mode == ScalingMode::Divide) { + STORM_LOG_ASSERT(hasEntryInColumn, "The scaling mode 'divide' requires an element in the given column."); + STORM_LOG_ASSERT(storm::utility::isZero(columnValue), "The scaling mode 'divide' requires a non-zero element in the given column."); + columnValue = storm::utility::one<ValueType>() / columnValue; + } else if (Mode == ScalingMode::DivideOneMinus) { + if (hasEntryInColumn) { + STORM_LOG_ASSERT(columnValue != storm::utility::one<ValueType>(), "The scaling mode 'divide-one-minus' requires a non-one value in the given column."); + columnValue = storm::utility::simplify(storm::utility::one<ValueType>() / (storm::utility::one<ValueType>() - columnValue)); + } + } + + if (hasEntryInColumn) { + for (auto entryIt = entriesInRow.begin(), entryIte = entriesInRow.end(); entryIt != entryIte; ++entryIt) { + // Only scale the entries in a different column. + if (entryIt->getColumn() != column) { + entryIt->setValue(storm::utility::simplify(entryIt->getValue() * columnValue)); + } + } + updateValue(column, columnValue); + } + + // Now substitute the row entries in all other rows that contain an element whose column is the current row. + FlexibleRowType& elementsWithEntryInColumnEqualRow = transposedMatrix.getRow(row); + + // In case we have a constrained elimination, we need to keep track of the rows that keep their value + // in the column equal to the current row. + FlexibleRowType rowsKeepingEntryInColumnEqualRow; + + // For each entry in the row d, we need to build a list of other rows that will contain an element in the + // column d. + std::vector<FlexibleRowType> newBackwardEntries(entriesInRow.size()); + for (auto& backwardEntry : newBackwardEntries) { + backwardEntry.reserve(elementsWithEntryInColumnEqualRow.size()); + } + + // Now go through the rows with an entry in the column corresponding to the current row and substitute + // the elements of this row unless the elimination is filtered. + for (auto const& predecessorEntry : elementsWithEntryInColumnEqualRow) { + uint_fast64_t predecessor = predecessorEntry.getColumn(); + STORM_LOG_TRACE("Found predecessor " << predecessor << "."); + + // Skip the row itself. + if (predecessor == row) { + assert(hasEntryInColumn); + continue; + } + + // Skip the state if the elimination is constrained, but the predecessor is not in the constraint. + if (isFilterPredecessor() && !filterPredecessor(predecessor)) { + rowsKeepingEntryInColumnEqualRow.emplace_back(predecessorEntry); + STORM_LOG_TRACE("Not eliminating predecessor " << predecessor << ", because it does not fit the filter."); + continue; + } + STORM_LOG_TRACE("Eliminating predecessor " << predecessor << "."); + + // First, find the probability with which the predecessor can move to the current state, because + // the forward probabilities of the state to be eliminated need to be scaled with this factor. + FlexibleRowType& predecessorForwardTransitions = matrix.getRow(predecessor); + FlexibleRowIterator multiplyElement = std::find_if(predecessorForwardTransitions.begin(), predecessorForwardTransitions.end(), [&](storm::storage::MatrixEntry<typename storm::storage::FlexibleSparseMatrix<ValueType>::index_type, typename storm::storage::FlexibleSparseMatrix<ValueType>::value_type> const& a) { return a.getColumn() == column; }); + + // Make sure we have found the probability and set it to zero. + STORM_LOG_THROW(multiplyElement != predecessorForwardTransitions.end(), storm::exceptions::InvalidStateException, "No probability for successor found."); + ValueType multiplyFactor = multiplyElement->getValue(); + multiplyElement->setValue(storm::utility::zero<ValueType>()); + + // At this point, we need to update the (forward) transitions of the predecessor. + FlexibleRowIterator first1 = predecessorForwardTransitions.begin(); + FlexibleRowIterator last1 = predecessorForwardTransitions.end(); + FlexibleRowIterator first2 = entriesInRow.begin(); + FlexibleRowIterator last2 = entriesInRow.end(); + + FlexibleRowType newSuccessors; + newSuccessors.reserve((last1 - first1) + (last2 - first2)); + std::insert_iterator<FlexibleRowType> result(newSuccessors, newSuccessors.end()); + + uint_fast64_t successorOffsetInNewBackwardTransitions = 0; + // Now we merge the two successor lists. (Code taken from std::set_union and modified to suit our needs). + for (; first1 != last1; ++result) { + // Skip the transitions to the state that is currently being eliminated. + if (first1->getColumn() == column || (first2 != last2 && first2->getColumn() == column)) { + if (first1->getColumn() == column) { + ++first1; + } + if (first2 != last2 && first2->getColumn() == column) { + ++first2; + } + continue; + } + + if (first2 == last2) { + std::copy_if(first1, last1, result, [&] (storm::storage::MatrixEntry<typename storm::storage::FlexibleSparseMatrix<ValueType>::index_type, typename storm::storage::FlexibleSparseMatrix<ValueType>::value_type> const& a) { return a.getColumn() != column; } ); + break; + } + if (first2->getColumn() < first1->getColumn()) { + auto successorEntry = storm::utility::simplify(std::move(*first2 * multiplyFactor)); + *result = successorEntry; + newBackwardEntries[successorOffsetInNewBackwardTransitions].emplace_back(predecessor, successorEntry.getValue()); + ++first2; + ++successorOffsetInNewBackwardTransitions; + } else if (first1->getColumn() < first2->getColumn()) { + *result = *first1; + ++first1; + } else { + auto probability = storm::utility::simplify(first1->getValue() + storm::utility::simplify(multiplyFactor * first2->getValue())); + *result = storm::storage::MatrixEntry<typename storm::storage::FlexibleSparseMatrix<ValueType>::index_type, typename storm::storage::FlexibleSparseMatrix<ValueType>::value_type>(first1->getColumn(), probability); + newBackwardEntries[successorOffsetInNewBackwardTransitions].emplace_back(predecessor, probability); + ++first1; + ++first2; + ++successorOffsetInNewBackwardTransitions; + } + } + for (; first2 != last2; ++first2) { + if (first2->getColumn() != column) { + auto stateProbability = storm::utility::simplify(std::move(*first2 * multiplyFactor)); + *result = stateProbability; + newBackwardEntries[successorOffsetInNewBackwardTransitions].emplace_back(predecessor, stateProbability.getValue()); + ++successorOffsetInNewBackwardTransitions; + } + } + + // Now move the new transitions in place. + predecessorForwardTransitions = std::move(newSuccessors); + STORM_LOG_TRACE("Fixed new next-state probabilities of predecessor state " << predecessor << "."); + + updatePredecessor(predecessor, multiplyFactor, column); + + STORM_LOG_TRACE("Updating priority of predecessor."); + updatePriority(predecessor); + } + + // Finally, we need to add the predecessor to the set of predecessors of every successor. + uint_fast64_t successorOffsetInNewBackwardTransitions = 0; + for (auto const& successorEntry : entriesInRow) { + if (successorEntry.getColumn() == column) { + continue; + } + + FlexibleRowType& successorBackwardTransitions = transposedMatrix.getRow(successorEntry.getColumn()); + + // Delete the current state as a predecessor of the successor state only if we are going to remove the + // current state's forward transitions. + if (clearRow) { + FlexibleRowIterator elimIt = std::find_if(successorBackwardTransitions.begin(), successorBackwardTransitions.end(), [&](storm::storage::MatrixEntry<typename storm::storage::FlexibleSparseMatrix<ValueType>::index_type, typename storm::storage::FlexibleSparseMatrix<ValueType>::value_type> const& a) { return a.getColumn() == column; }); + STORM_LOG_ASSERT(elimIt != successorBackwardTransitions.end(), "Expected a proper backward transition from " << successorEntry.getColumn() << " to " << column << ", but found none."); + successorBackwardTransitions.erase(elimIt); + } + + FlexibleRowIterator first1 = successorBackwardTransitions.begin(); + FlexibleRowIterator last1 = successorBackwardTransitions.end(); + FlexibleRowIterator first2 = newBackwardEntries[successorOffsetInNewBackwardTransitions].begin(); + FlexibleRowIterator last2 = newBackwardEntries[successorOffsetInNewBackwardTransitions].end(); + + FlexibleRowType newPredecessors; + newPredecessors.reserve((last1 - first1) + (last2 - first2)); + std::insert_iterator<FlexibleRowType> result(newPredecessors, newPredecessors.end()); + + for (; first1 != last1; ++result) { + if (first2 == last2) { + std::copy(first1, last1, result); + break; + } + if (first2->getColumn() < first1->getColumn()) { + if (first2->getColumn() != column) { + *result = *first2; + } + ++first2; + } else if (first1->getColumn() == first2->getColumn()) { + if (estimateComplexity(first1->getValue()) > estimateComplexity(first2->getValue())) { + *result = *first1; + } else { + *result = *first2; + } + ++first1; + ++first2; + } else { + *result = *first1; + ++first1; + } + } + if (isFilterPredecessor()) { + std::copy_if(first2, last2, result, [&] (storm::storage::MatrixEntry<typename storm::storage::FlexibleSparseMatrix<ValueType>::index_type, typename storm::storage::FlexibleSparseMatrix<ValueType>::value_type> const& a) { return a.getColumn() != column && filterPredecessor(a.getColumn()); }); + } else { + std::copy_if(first2, last2, result, [&] (storm::storage::MatrixEntry<typename storm::storage::FlexibleSparseMatrix<ValueType>::index_type, typename storm::storage::FlexibleSparseMatrix<ValueType>::value_type> const& a) { return a.getColumn() != column; }); + } + // Now move the new predecessors in place. + successorBackwardTransitions = std::move(newPredecessors); + ++successorOffsetInNewBackwardTransitions; + } + STORM_LOG_TRACE("Fixed predecessor lists of successor states."); + + // Clear the row if requested. + if (clearRow) { + entriesInRow.clear(); + entriesInRow.shrink_to_fit(); + } + + // If the substitution was filtered, we need to store the new rows that have an entry in column equal to this row. + if (isFilterPredecessor()) { + elementsWithEntryInColumnEqualRow = std::move(rowsKeepingEntryInColumnEqualRow); + } else { + elementsWithEntryInColumnEqualRow.clear(); + elementsWithEntryInColumnEqualRow.shrink_to_fit(); + } + } + + template<typename ValueType, ScalingMode Mode> + void EliminatorBase<ValueType, Mode>::updateValue(storm::storage::sparse::state_type const& state, ValueType const& loopProbability) { + // Intentionally left empty. + } + + template<typename ValueType, ScalingMode Mode> + void EliminatorBase<ValueType, Mode>::updatePredecessor(storm::storage::sparse::state_type const& predecessor, ValueType const& probability, storm::storage::sparse::state_type const& state) { + // Intentionally left empty. + } + + template<typename ValueType, ScalingMode Mode> + void EliminatorBase<ValueType, Mode>::updatePriority(storm::storage::sparse::state_type const& state) { + // Intentionally left empty. + } + + template<typename ValueType, ScalingMode Mode> + bool EliminatorBase<ValueType, Mode>::filterPredecessor(storm::storage::sparse::state_type const& state) { + STORM_LOG_ASSERT(false, "Must not filter predecessors."); + return false; + } + + template<typename ValueType, ScalingMode Mode> + bool EliminatorBase<ValueType, Mode>::isFilterPredecessor() const { + return false; + } + + template class EliminatorBase<double, ScalingMode::Divide>; + template class EliminatorBase<storm::RationalNumber, ScalingMode::Divide>; + template class EliminatorBase<storm::RationalFunction, ScalingMode::Divide>; + + template class EliminatorBase<double, ScalingMode::DivideOneMinus>; + template class EliminatorBase<storm::RationalNumber, ScalingMode::DivideOneMinus>; + template class EliminatorBase<storm::RationalFunction, ScalingMode::DivideOneMinus>; + + } + } +} \ No newline at end of file diff --git a/src/solver/stateelimination/EliminatorBase.h b/src/solver/stateelimination/EliminatorBase.h new file mode 100644 index 000000000..d4513dc19 --- /dev/null +++ b/src/solver/stateelimination/EliminatorBase.h @@ -0,0 +1,39 @@ +#pragma once + +#include "src/storage/sparse/StateType.h" + +#include "src/storage/FlexibleSparseMatrix.h" + +namespace storm { + namespace solver { + namespace stateelimination { + + enum class ScalingMode { + Divide, DivideOneMinus + }; + + template<typename ValueType, ScalingMode Mode> + class EliminatorBase { + public: + typedef typename storm::storage::FlexibleSparseMatrix<ValueType>::row_type FlexibleRowType; + typedef typename FlexibleRowType::iterator FlexibleRowIterator; + + EliminatorBase(storm::storage::FlexibleSparseMatrix<ValueType>& matrix, storm::storage::FlexibleSparseMatrix<ValueType>& transposedMatrix); + + void eliminate(uint64_t row, uint64_t column, bool clearRow); + + // Provide virtual methods that can be customized by subclasses to govern side-effect of the elimination. + virtual void updateValue(storm::storage::sparse::state_type const& state, ValueType const& loopProbability); + virtual void updatePredecessor(storm::storage::sparse::state_type const& predecessor, ValueType const& probability, storm::storage::sparse::state_type const& state); + virtual void updatePriority(storm::storage::sparse::state_type const& state); + virtual bool filterPredecessor(storm::storage::sparse::state_type const& state); + virtual bool isFilterPredecessor() const; + + protected: + storm::storage::FlexibleSparseMatrix<ValueType>& matrix; + storm::storage::FlexibleSparseMatrix<ValueType>& transposedMatrix; + }; + + } // namespace stateelimination + } // namespace storage +} // namespace storm \ No newline at end of file diff --git a/src/solver/stateelimination/EquationSystemEliminator.cpp b/src/solver/stateelimination/EquationSystemEliminator.cpp new file mode 100644 index 000000000..2ffe29705 --- /dev/null +++ b/src/solver/stateelimination/EquationSystemEliminator.cpp @@ -0,0 +1,17 @@ +#include "src/solver/stateelimination/EquationSystemEliminator.h" + +namespace storm { + namespace solver { + namespace stateelimination { + + template<typename ValueType> + EquationSystemEliminator<ValueType>::EquationSystemEliminator(storm::storage::FlexibleSparseMatrix<ValueType>& matrix, storm::storage::FlexibleSparseMatrix<ValueType>& transposedMatrix) : EliminatorBase<ValueType, ScalingMode::Divide>(matrix, transposedMatrix) { + // Intentionally left empty. + } + + template class EquationSystemEliminator<double>; + template class EquationSystemEliminator<storm::RationalNumber>; + template class EquationSystemEliminator<storm::RationalFunction>; + } + } +} \ No newline at end of file diff --git a/src/solver/stateelimination/EquationSystemEliminator.h b/src/solver/stateelimination/EquationSystemEliminator.h new file mode 100644 index 000000000..21062ff5d --- /dev/null +++ b/src/solver/stateelimination/EquationSystemEliminator.h @@ -0,0 +1,17 @@ +#pragma once + +#include "src/solver/stateelimination/EliminatorBase.h" + +namespace storm { + namespace solver { + namespace stateelimination { + + template<typename ValueType> + class EquationSystemEliminator : public EliminatorBase<ValueType, ScalingMode::Divide> { + public: + EquationSystemEliminator(storm::storage::FlexibleSparseMatrix<ValueType>& matrix, storm::storage::FlexibleSparseMatrix<ValueType>& transposedMatrix); + }; + + } // namespace stateelimination + } // namespace storage +} // namespace storm diff --git a/src/solver/stateelimination/StateEliminator.cpp b/src/solver/stateelimination/StateEliminator.cpp index 41d3ef2d9..98b1d64b3 100644 --- a/src/solver/stateelimination/StateEliminator.cpp +++ b/src/solver/stateelimination/StateEliminator.cpp @@ -17,250 +17,14 @@ namespace storm { using namespace storm::utility::stateelimination; template<typename ValueType> - StateEliminator<ValueType>::StateEliminator(storm::storage::FlexibleSparseMatrix<ValueType>& transitionMatrix, storm::storage::FlexibleSparseMatrix<ValueType>& backwardTransitions) : transitionMatrix(transitionMatrix), backwardTransitions(backwardTransitions) { + StateEliminator<ValueType>::StateEliminator(storm::storage::FlexibleSparseMatrix<ValueType>& transitionMatrix, storm::storage::FlexibleSparseMatrix<ValueType>& backwardTransitions) : EliminatorBase<ValueType, ScalingMode::DivideOneMinus>(transitionMatrix, backwardTransitions) { // Intentionally left empty. } template<typename ValueType> - void StateEliminator<ValueType>::eliminateState(storm::storage::sparse::state_type state, bool removeForwardTransitions, storm::storage::BitVector predecessorConstraint) { + void StateEliminator<ValueType>::eliminateState(storm::storage::sparse::state_type state, bool removeForwardTransitions) { STORM_LOG_TRACE("Eliminating state " << state << "."); - - // Start by finding loop probability. - bool hasSelfLoop = false; - ValueType loopProbability = storm::utility::zero<ValueType>(); - FlexibleRowType& currentStateSuccessors = transitionMatrix.getRow(state); - for (auto entryIt = currentStateSuccessors.begin(), entryIte = currentStateSuccessors.end(); entryIt != entryIte; ++entryIt) { - if (entryIt->getColumn() >= state) { - if (entryIt->getColumn() == state) { - loopProbability = entryIt->getValue(); - hasSelfLoop = true; - - // If we do not clear the forward transitions completely, we need to remove the self-loop, - // because we scale all the other outgoing transitions with it anyway. - if (!removeForwardTransitions) { - currentStateSuccessors.erase(entryIt); - } - } - break; - } - } - - // Scale all entries in this row with (1 / (1 - loopProbability)) only in case there was a self-loop. - STORM_LOG_TRACE((hasSelfLoop ? "State has self-loop." : "State does not have a self-loop.")); - if (hasSelfLoop) { - STORM_LOG_ASSERT(loopProbability != storm::utility::one<ValueType>(), "Must not eliminate state with probability 1 self-loop."); - loopProbability = storm::utility::simplify(storm::utility::one<ValueType>() / (storm::utility::one<ValueType>() - loopProbability)); - for (auto& entry : transitionMatrix.getRow(state)) { - // Only scale the non-diagonal entries. - if (entry.getColumn() != state) { - entry.setValue(storm::utility::simplify(entry.getValue() * loopProbability)); - } - } - updateValue(state, loopProbability); - } - - // Now connect the predecessors of the state being eliminated with its successors. - FlexibleRowType& currentStatePredecessors = backwardTransitions.getRow(state); - - // In case we have a constrained elimination, we need to keep track of the new predecessors. - FlexibleRowType newCurrentStatePredecessors; - - std::vector<FlexibleRowType> newBackwardProbabilities(currentStateSuccessors.size()); - for (auto& backwardProbabilities : newBackwardProbabilities) { - backwardProbabilities.reserve(currentStatePredecessors.size()); - } - - // Now go through the predecessors and eliminate the ones (satisfying the constraint if given). - for (auto const& predecessorEntry : currentStatePredecessors) { - uint_fast64_t predecessor = predecessorEntry.getColumn(); - STORM_LOG_TRACE("Found predecessor " << predecessor << "."); - - // Skip the state itself as one of its predecessors. - if (predecessor == state) { - assert(hasSelfLoop); - continue; - } - - // Skip the state if the elimination is constrained, but the predecessor is not in the constraint. - if (isFilterPredecessor() && !filterPredecessor(predecessor)) { - newCurrentStatePredecessors.emplace_back(predecessorEntry); - STORM_LOG_TRACE("Not eliminating predecessor " << predecessor << ", because it does not fit the filter."); - continue; - } - STORM_LOG_TRACE("Eliminating predecessor " << predecessor << "."); - - // First, find the probability with which the predecessor can move to the current state, because - // the forward probabilities of the state to be eliminated need to be scaled with this factor. - FlexibleRowType& predecessorForwardTransitions = transitionMatrix.getRow(predecessor); - FlexibleRowIterator multiplyElement = std::find_if(predecessorForwardTransitions.begin(), predecessorForwardTransitions.end(), [&](storm::storage::MatrixEntry<typename storm::storage::FlexibleSparseMatrix<ValueType>::index_type, typename storm::storage::FlexibleSparseMatrix<ValueType>::value_type> const& a) { return a.getColumn() == state; }); - - // Make sure we have found the probability and set it to zero. - STORM_LOG_THROW(multiplyElement != predecessorForwardTransitions.end(), storm::exceptions::InvalidStateException, "No probability for successor found."); - ValueType multiplyFactor = multiplyElement->getValue(); - multiplyElement->setValue(storm::utility::zero<ValueType>()); - - // At this point, we need to update the (forward) transitions of the predecessor. - FlexibleRowIterator first1 = predecessorForwardTransitions.begin(); - FlexibleRowIterator last1 = predecessorForwardTransitions.end(); - FlexibleRowIterator first2 = currentStateSuccessors.begin(); - FlexibleRowIterator last2 = currentStateSuccessors.end(); - - FlexibleRowType newSuccessors; - newSuccessors.reserve((last1 - first1) + (last2 - first2)); - std::insert_iterator<FlexibleRowType> result(newSuccessors, newSuccessors.end()); - - uint_fast64_t successorOffsetInNewBackwardTransitions = 0; - // Now we merge the two successor lists. (Code taken from std::set_union and modified to suit our needs). - for (; first1 != last1; ++result) { - // Skip the transitions to the state that is currently being eliminated. - if (first1->getColumn() == state || (first2 != last2 && first2->getColumn() == state)) { - if (first1->getColumn() == state) { - ++first1; - } - if (first2 != last2 && first2->getColumn() == state) { - ++first2; - } - continue; - } - - if (first2 == last2) { - std::copy_if(first1, last1, result, [&] (storm::storage::MatrixEntry<typename storm::storage::FlexibleSparseMatrix<ValueType>::index_type, typename storm::storage::FlexibleSparseMatrix<ValueType>::value_type> const& a) { return a.getColumn() != state; } ); - break; - } - if (first2->getColumn() < first1->getColumn()) { - auto successorEntry = storm::utility::simplify(std::move(*first2 * multiplyFactor)); - *result = successorEntry; - newBackwardProbabilities[successorOffsetInNewBackwardTransitions].emplace_back(predecessor, successorEntry.getValue()); - ++first2; - ++successorOffsetInNewBackwardTransitions; - } else if (first1->getColumn() < first2->getColumn()) { - *result = *first1; - ++first1; - } else { - auto probability = storm::utility::simplify(first1->getValue() + storm::utility::simplify(multiplyFactor * first2->getValue())); - *result = storm::storage::MatrixEntry<typename storm::storage::FlexibleSparseMatrix<ValueType>::index_type, typename storm::storage::FlexibleSparseMatrix<ValueType>::value_type>(first1->getColumn(), probability); - newBackwardProbabilities[successorOffsetInNewBackwardTransitions].emplace_back(predecessor, probability); - ++first1; - ++first2; - ++successorOffsetInNewBackwardTransitions; - } - } - for (; first2 != last2; ++first2) { - if (first2->getColumn() != state) { - auto stateProbability = storm::utility::simplify(std::move(*first2 * multiplyFactor)); - *result = stateProbability; - newBackwardProbabilities[successorOffsetInNewBackwardTransitions].emplace_back(predecessor, stateProbability.getValue()); - ++successorOffsetInNewBackwardTransitions; - } - } - - // Now move the new transitions in place. - predecessorForwardTransitions = std::move(newSuccessors); - STORM_LOG_TRACE("Fixed new next-state probabilities of predecessor state " << predecessor << "."); - - updatePredecessor(predecessor, multiplyFactor, state); - - STORM_LOG_TRACE("Updating priority of predecessor."); - updatePriority(predecessor); - } - - // Finally, we need to add the predecessor to the set of predecessors of every successor. - uint_fast64_t successorOffsetInNewBackwardTransitions = 0; - for (auto const& successorEntry : currentStateSuccessors) { - if (successorEntry.getColumn() == state) { - continue; - } - - FlexibleRowType& successorBackwardTransitions = backwardTransitions.getRow(successorEntry.getColumn()); - - // Delete the current state as a predecessor of the successor state only if we are going to remove the - // current state's forward transitions. - if (removeForwardTransitions) { - FlexibleRowIterator elimIt = std::find_if(successorBackwardTransitions.begin(), successorBackwardTransitions.end(), [&](storm::storage::MatrixEntry<typename storm::storage::FlexibleSparseMatrix<ValueType>::index_type, typename storm::storage::FlexibleSparseMatrix<ValueType>::value_type> const& a) { return a.getColumn() == state; }); - STORM_LOG_ASSERT(elimIt != successorBackwardTransitions.end(), "Expected a proper backward transition from " << successorEntry.getColumn() << " to " << state << ", but found none."); - successorBackwardTransitions.erase(elimIt); - } - - FlexibleRowIterator first1 = successorBackwardTransitions.begin(); - FlexibleRowIterator last1 = successorBackwardTransitions.end(); - FlexibleRowIterator first2 = newBackwardProbabilities[successorOffsetInNewBackwardTransitions].begin(); - FlexibleRowIterator last2 = newBackwardProbabilities[successorOffsetInNewBackwardTransitions].end(); - - FlexibleRowType newPredecessors; - newPredecessors.reserve((last1 - first1) + (last2 - first2)); - std::insert_iterator<FlexibleRowType> result(newPredecessors, newPredecessors.end()); - - for (; first1 != last1; ++result) { - if (first2 == last2) { - std::copy(first1, last1, result); - break; - } - if (first2->getColumn() < first1->getColumn()) { - if (first2->getColumn() != state) { - *result = *first2; - } - ++first2; - } else if (first1->getColumn() == first2->getColumn()) { - if (estimateComplexity(first1->getValue()) > estimateComplexity(first2->getValue())) { - *result = *first1; - } else { - *result = *first2; - } - ++first1; - ++first2; - } else { - *result = *first1; - ++first1; - } - } - if (isFilterPredecessor()) { - std::copy_if(first2, last2, result, [&] (storm::storage::MatrixEntry<typename storm::storage::FlexibleSparseMatrix<ValueType>::index_type, typename storm::storage::FlexibleSparseMatrix<ValueType>::value_type> const& a) { return a.getColumn() != state && filterPredecessor(a.getColumn()); }); - } else { - std::copy_if(first2, last2, result, [&] (storm::storage::MatrixEntry<typename storm::storage::FlexibleSparseMatrix<ValueType>::index_type, typename storm::storage::FlexibleSparseMatrix<ValueType>::value_type> const& a) { return a.getColumn() != state; }); - } - // Now move the new predecessors in place. - successorBackwardTransitions = std::move(newPredecessors); - ++successorOffsetInNewBackwardTransitions; - } - STORM_LOG_TRACE("Fixed predecessor lists of successor states."); - - if (removeForwardTransitions) { - // Clear the eliminated row to reduce memory consumption. - currentStateSuccessors.clear(); - currentStateSuccessors.shrink_to_fit(); - } - if (isFilterPredecessor()) { - currentStatePredecessors = std::move(newCurrentStatePredecessors); - } else { - currentStatePredecessors.clear(); - currentStatePredecessors.shrink_to_fit(); - } - } - - template<typename ValueType> - void StateEliminator<ValueType>::updateValue(storm::storage::sparse::state_type const& state, ValueType const& loopProbability) { - // Intentionally left empty. - } - - template<typename ValueType> - void StateEliminator<ValueType>::updatePredecessor(storm::storage::sparse::state_type const& predecessor, ValueType const& probability, storm::storage::sparse::state_type const& state) { - // Intentionally left empty. - } - - template<typename ValueType> - void StateEliminator<ValueType>::updatePriority(storm::storage::sparse::state_type const& state) { - // Intentionally left empty. - } - - template<typename ValueType> - bool StateEliminator<ValueType>::filterPredecessor(storm::storage::sparse::state_type const& state) { - STORM_LOG_ASSERT(false, "Must not filter predecessors."); - return false; - } - - template<typename ValueType> - bool StateEliminator<ValueType>::isFilterPredecessor() const { - return false; + this->eliminate(state, state, removeForwardTransitions); } template class StateEliminator<double>; diff --git a/src/solver/stateelimination/StateEliminator.h b/src/solver/stateelimination/StateEliminator.h index bf2abd695..45bb5d65a 100644 --- a/src/solver/stateelimination/StateEliminator.h +++ b/src/solver/stateelimination/StateEliminator.h @@ -1,35 +1,18 @@ #ifndef STORM_SOLVER_STATEELIMINATION_STATEELIMINATOR_H_ #define STORM_SOLVER_STATEELIMINATION_STATEELIMINATOR_H_ -#include "src/storage/sparse/StateType.h" - -#include "src/storage/FlexibleSparseMatrix.h" -#include "src/storage/BitVector.h" +#include "src/solver/stateelimination/EliminatorBase.h" namespace storm { namespace solver { namespace stateelimination { template<typename ValueType> - class StateEliminator { + class StateEliminator : public EliminatorBase<ValueType, ScalingMode::DivideOneMinus> { public: - typedef typename storm::storage::FlexibleSparseMatrix<ValueType>::row_type FlexibleRowType; - typedef typename FlexibleRowType::iterator FlexibleRowIterator; - StateEliminator(storm::storage::FlexibleSparseMatrix<ValueType>& transitionMatrix, storm::storage::FlexibleSparseMatrix<ValueType>& backwardTransitions); - void eliminateState(storm::storage::sparse::state_type state, bool removeForwardTransitions, storm::storage::BitVector predecessorConstraint = storm::storage::BitVector()); - - // Provide virtual methods that can be customized by subclasses to govern side-effect of the elimination. - virtual void updateValue(storm::storage::sparse::state_type const& state, ValueType const& loopProbability); - virtual void updatePredecessor(storm::storage::sparse::state_type const& predecessor, ValueType const& probability, storm::storage::sparse::state_type const& state); - virtual void updatePriority(storm::storage::sparse::state_type const& state); - virtual bool filterPredecessor(storm::storage::sparse::state_type const& state); - virtual bool isFilterPredecessor() const; - - protected: - storm::storage::FlexibleSparseMatrix<ValueType>& transitionMatrix; - storm::storage::FlexibleSparseMatrix<ValueType>& backwardTransitions; + void eliminateState(storm::storage::sparse::state_type state, bool removeForwardTransitions); }; } // namespace stateelimination