Browse Source
started generalizing elimination to equation system solving
started generalizing elimination to equation system solving
Former-commit-id: aabe89b65f
main
6 changed files with 365 additions and 259 deletions
-
286src/solver/stateelimination/EliminatorBase.cpp
-
39src/solver/stateelimination/EliminatorBase.h
-
17src/solver/stateelimination/EquationSystemEliminator.cpp
-
17src/solver/stateelimination/EquationSystemEliminator.h
-
242src/solver/stateelimination/StateEliminator.cpp
-
23src/solver/stateelimination/StateEliminator.h
@ -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>; |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
} |
@ -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 |
@ -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>; |
||||
|
} |
||||
|
} |
||||
|
} |
@ -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 |
Reference in new issue
xxxxxxxxxx