19template<
typename ValueType,
typename SolutionType>
24template<
typename ValueType,
typename SolutionType>
30template<
typename ValueType,
typename SolutionType>
36template<
typename ValueType,
typename SolutionType>
38 bool adaptPrecision)
const {
43 STORM_LOG_ASSERT(this->longestSccChainSize,
"Did not compute the longest SCC chain size although it is needed.");
44 storm::RationalNumber subEnvPrec =
46 subEnv.solver().minMax().setPrecision(subEnvPrec);
51template<
typename ValueType,
typename SolutionType>
53 std::vector<SolutionType>& x,
54 std::vector<ValueType>
const& b)
const {
55 STORM_LOG_ASSERT(x.size() == this->A->getRowGroupCount(),
"Provided x-vector has invalid size.");
56 STORM_LOG_ASSERT(b.size() == this->A->getRowCount(),
"Provided b-vector has invalid size.");
61 if (!this->sortedSccDecomposition || (needAdaptPrecision && !this->longestSccChainSize)) {
64 createSortedSccDecomposition(needAdaptPrecision);
67 << sccSw <<
". Found " << this->sortedSccDecomposition->size() <<
" SCC(s) containing a total of " << x.size()
68 <<
" states. Average SCC size is "
69 <<
static_cast<double>(this->A->getRowGroupCount()) /
static_cast<double>(this->sortedSccDecomposition->size()) <<
".");
73 needAdaptPrecision = needAdaptPrecision && (this->sortedSccDecomposition->size() != this->
A->getRowGroupCount());
75 storm::Environment sccSolverEnvironment = getEnvironmentForUnderlyingSolver(env, needAdaptPrecision);
77 if (this->longestSccChainSize) {
78 STORM_LOG_INFO(
"Longest SCC chain size is " << this->longestSccChainSize.get());
81 bool returnValue =
true;
82 if (this->sortedSccDecomposition->size() == 1 && (!this->choiceFixedForRowGroup || this->choiceFixedForRowGroup.get().empty())) {
84 if (
auto const& scc = *this->sortedSccDecomposition->begin(); scc.size() == 1) {
89 returnValue = solveTrivialScc(*scc.begin(), dir, x, b);
91 returnValue = solveFullyConnectedEquationSystem(sccSolverEnvironment, dir, x, b);
102 std::optional<storm::storage::BitVector> newRelevantValues;
104 this->sortedSccDecomposition->size() < this->A->getRowGroupCount()) {
107 std::vector<uint64_t> rowGroupToScc = this->sortedSccDecomposition->computeStateToSccIndexMap(this->
A->getRowGroupCount());
108 for (uint64_t rowGroup = 0; rowGroup < this->
A->getRowGroupCount(); ++rowGroup) {
109 auto currScc = rowGroupToScc[rowGroup];
110 for (
auto const& successor : this->
A->getRowGroup(rowGroup)) {
111 if (rowGroupToScc[successor.getColumn()] != currScc) {
112 newRelevantValues->set(successor.getColumn(),
true);
119 uint64_t sccIndex = 0;
123 for (
auto const& scc : *this->sortedSccDecomposition) {
124 if (scc.size() == 1) {
125 returnValue = solveTrivialScc(*scc.begin(), dir, x, b) && returnValue;
128 sccRowGroupsAsBitVector.
clear();
129 sccRowsAsBitVector.
clear();
130 for (
auto const& group : scc) {
131 sccRowGroupsAsBitVector.
set(group,
true);
134 for (uint64_t row = this->
A->getRowGroupIndices()[group]; row < this->
A->getRowGroupIndices()[group + 1]; ++row) {
135 sccRowsAsBitVector.
set(row,
true);
139 sccRowsAsBitVector.
set(row,
true);
143 returnValue = solveScc(sccSolverEnvironment, dir, sccRowGroupsAsBitVector, sccRowsAsBitVector, x, b, newRelevantValues) && returnValue;
148 STORM_LOG_WARN(
"Topological solver aborted after analyzing " << sccIndex <<
"/" << this->sortedSccDecomposition->size() <<
" SCCs.");
161template<
typename ValueType,
typename SolutionType>
162void TopologicalMinMaxLinearEquationSolver<ValueType, SolutionType>::createSortedSccDecomposition(
bool needLongestChainSize)
const {
164 this->sortedSccDecomposition = std::make_unique<storm::storage::StronglyConnectedComponentDecomposition<ValueType>>(
166 if (needLongestChainSize) {
167 this->longestSccChainSize = this->sortedSccDecomposition->getMaxSccDepth() + 1;
171template<
typename ValueType,
typename SolutionType>
172bool TopologicalMinMaxLinearEquationSolver<ValueType, SolutionType>::solveTrivialScc(uint64_t
const& sccState,
OptimizationDirection dir,
173 std::vector<SolutionType>& globalX,
174 std::vector<ValueType>
const& globalB)
const {
175 SolutionType& xi = globalX[sccState];
176 if (this->choiceFixedForRowGroup && this->choiceFixedForRowGroup.get()[sccState]) {
178 uint_fast64_t row = this->A->getRowGroupIndices()[sccState] + this->getInitialScheduler()[sccState];
179 ValueType rowValue = globalB[row];
180 bool hasDiagonalEntry =
false;
181 ValueType denominator;
182 for (
auto const& entry : this->A->getRow(row)) {
183 if (entry.getColumn() == sccState) {
184 hasDiagonalEntry =
true;
187 rowValue += entry.getValue() * globalX[entry.getColumn()];
190 if (hasDiagonalEntry) {
193 "State " << sccState <<
" has a selfloop with probability '1-(" << denominator <<
")'. This could be an indication for numerical issues.");
202 xi = std::move(rowValue);
204 bool firstRow =
true;
206 for (uint64_t row = this->A->getRowGroupIndices()[sccState]; row < this->A->getRowGroupIndices()[sccState + 1]; ++row) {
208 bool hasDiagonalEntry =
false;
210 for (
auto const& entry : this->A->getRow(row)) {
211 if (entry.getColumn() == sccState) {
212 hasDiagonalEntry =
true;
215 rowValue += entry.getValue() * globalX[entry.getColumn()];
218 if (hasDiagonalEntry) {
221 "State " << sccState <<
" has a selfloop with probability '1-(" << denominator <<
")'. This could be an indication for numerical issues.");
232 xi = std::move(rowValue);
238 xi = std::move(rowValue);
243 xi = std::move(rowValue);
249 if (this->isTrackSchedulerSet()) {
250 this->schedulerChoices.get()[sccState] = bestRow - this->A->getRowGroupIndices()[sccState];
252 STORM_LOG_THROW(!firstRow, storm::exceptions::UnexpectedException,
"Empty row group in MinMax equation system.");
257template<
typename ValueType,
typename SolutionType>
258bool TopologicalMinMaxLinearEquationSolver<ValueType, SolutionType>::solveFullyConnectedEquationSystem(storm::Environment
const& sccSolverEnvironment,
260 std::vector<ValueType>
const& b)
const {
261 STORM_LOG_ASSERT(!this->choiceFixedForRowGroup || this->choiceFixedForRowGroup.get().empty(),
262 "Expecting no fixed choices for states when solving the fully connected equation system");
263 if (!this->sccSolver) {
265 this->sccSolver->setCachingEnabled(
true);
267 this->sccSolver->setMatrix(*this->A);
268 this->sccSolver->setHasUniqueSolution(this->hasUniqueSolution());
269 this->sccSolver->setHasNoEndComponents(this->hasNoEndComponents());
270 this->sccSolver->setTrackScheduler(this->isTrackSchedulerSet());
271 if (this->hasInitialScheduler()) {
272 auto choices = this->getInitialScheduler();
273 this->sccSolver->setInitialScheduler(std::move(choices));
275 if (this->hasRelevantValues()) {
276 this->sccSolver->setRelevantValues(this->getRelevantValues());
278 auto req = this->sccSolver->getRequirements(sccSolverEnvironment, dir);
279 this->sccSolver->setBoundsFromOtherSolver(*
this);
280 if (req.upperBounds() && this->hasUpperBound()) {
281 req.clearUpperBounds();
283 if (req.lowerBounds() && this->hasLowerBound()) {
284 req.clearLowerBounds();
286 if (req.validInitialScheduler() && this->hasInitialScheduler()) {
287 req.clearValidInitialScheduler();
289 if (req.uniqueSolution() && this->hasUniqueSolution()) {
290 req.clearUniqueSolution();
292 STORM_LOG_THROW(!req.hasEnabledCriticalRequirement(), storm::exceptions::UncheckedRequirementException,
293 "Solver requirements " + req.getEnabledRequirementsAsString() +
" not checked.");
294 this->sccSolver->setRequirementsChecked(
true);
296 bool res = this->sccSolver->solveEquations(sccSolverEnvironment, dir, x, b);
297 if (this->isTrackSchedulerSet()) {
298 this->schedulerChoices = this->sccSolver->getSchedulerChoices();
303template<
typename ValueType,
typename SolutionType>
304bool TopologicalMinMaxLinearEquationSolver<ValueType, SolutionType>::solveScc(storm::Environment
const& sccSolverEnvironment,
OptimizationDirection dir,
305 storm::storage::BitVector
const& sccRowGroups,
306 storm::storage::BitVector
const& sccRows, std::vector<SolutionType>& globalX,
307 std::vector<ValueType>
const& globalB,
308 std::optional<storm::storage::BitVector>
const& globalRelevantValues)
const {
310 if (!this->sccSolver) {
312 this->sccSolver->setCachingEnabled(
true);
314 this->sccSolver->setHasUniqueSolution(this->hasUniqueSolution());
315 this->sccSolver->setHasNoEndComponents(this->hasNoEndComponents());
316 this->sccSolver->setTrackScheduler(this->isTrackSchedulerSet());
317 if (globalRelevantValues) {
318 this->sccSolver->setRelevantValues((*globalRelevantValues) % sccRowGroups);
321 storm::storage::SparseMatrix<ValueType> sccA;
322 if (this->choiceFixedForRowGroup) {
324 storm::storage::BitVector choiceFixedForStateSCC = this->choiceFixedForRowGroup.get() % sccRowGroups;
325 sccA = this->A->
getSubmatrix(
false, sccRows, sccRowGroups);
328 if (this->hasInitialScheduler()) {
333 this->sccSolver->setInitialScheduler(std::move(sccInitChoices));
337 sccA = this->A->
getSubmatrix(
true, sccRowGroups, sccRowGroups);
340 if (this->hasInitialScheduler()) {
342 this->sccSolver->setInitialScheduler(std::move(sccInitChoices));
346 this->sccSolver->setMatrix(std::move(sccA));
352 std::vector<ValueType> sccB;
354 for (
auto row : sccRows) {
356 for (
auto const& entry : this->A->getRow(row)) {
357 if (!sccRowGroups.
get(entry.getColumn())) {
358 bi += entry.getValue() * globalX[entry.getColumn()];
361 sccB.push_back(std::move(bi));
364 auto req = this->sccSolver->getRequirements(sccSolverEnvironment, dir);
365 this->sccSolver->clearBounds();
368 this->sccSolver->setLowerBound(this->getLowerBound());
369 req.clearLowerBounds();
372 req.clearLowerBounds();
375 this->sccSolver->setUpperBound(this->getUpperBound());
376 req.clearUpperBounds();
379 req.clearUpperBounds();
383 if (req.validInitialScheduler() && (this->hasInitialScheduler() || this->hasNoEndComponents())) {
384 req.clearValidInitialScheduler();
386 if (req.uniqueSolution() && this->hasUniqueSolution()) {
387 req.clearUniqueSolution();
389 STORM_LOG_THROW(!req.hasEnabledCriticalRequirement(), storm::exceptions::UncheckedRequirementException,
390 "Solver requirements " + req.getEnabledRequirementsAsString() +
" not checked.");
391 this->sccSolver->setRequirementsChecked(
true);
394 bool res = this->sccSolver->solveEquations(sccSolverEnvironment, dir, sccX, sccB);
397 if (this->isTrackSchedulerSet()) {
407template<
typename ValueType,
typename SolutionType>
416template<
typename ValueType,
typename SolutionType>
418 sortedSccDecomposition.reset();
419 longestSccChainSize = boost::none;
421 auxiliaryRowGroupVector.reset();
SolverEnvironment & solver()
TopologicalSolverEnvironment & topological()
bool isForceSoundness() const
storm::solver::MinMaxMethod const & getUnderlyingMinMaxMethod() const
bool const & isUnderlyingMinMaxMethodSetFromDefault() const
bool isExtendRelevantValues() const
storm::storage::BitVector const & getRelevantValues() const
Retrieves the relevant values (if there are any).
virtual std::unique_ptr< MinMaxLinearEquationSolver< ValueType, SolutionType > > create(Environment const &env) const override
MinMaxLinearEquationSolverRequirements getRequirements(Environment const &env, bool hasUniqueSolution=false, bool hasNoEndComponents=false, boost::optional< storm::solver::OptimizationDirection > const &direction=boost::none, bool hasInitialScheduler=false, bool trackScheduler=false) const
Retrieves the requirements of the solver that would be created when calling create() right now.
virtual void clearCache() const
OptimizationDirectionSetting direction
std::vector< uint_fast64_t > const & getInitialScheduler() const
bool hasUniqueSolution() const
boost::optional< storm::storage::BitVector > choiceFixedForRowGroup
bool isTrackSchedulerSet() const
bool hasNoEndComponents() const
bool isCachingEnabled() const
boost::optional< std::vector< uint_fast64_t > > schedulerChoices
bool hasInitialScheduler() const
StandardMinMaxLinearEquationSolver()
storm::storage::SparseMatrix< ValueType > const * A
TopologicalMinMaxLinearEquationSolver()
virtual bool internalSolveEquations(storm::Environment const &env, OptimizationDirection d, std::vector< SolutionType > &x, std::vector< ValueType > const &b) const override
virtual void clearCache() const override
Clears the currently cached data that has been stored during previous calls of the solver.
virtual MinMaxLinearEquationSolverRequirements getRequirements(Environment const &env, boost::optional< storm::solver::OptimizationDirection > const &direction=boost::none, bool const &hasInitialScheduler=false) const override
Retrieves the requirements of this solver for solving equations with the current settings.
A bit vector that is internally represented as a vector of 64-bit values.
void clear()
Removes all set bits from the bit vector.
uint64_t getNumberOfSetBits() const
Returns the number of bits that are set to true in this bit vector.
void set(uint64_t index, bool value=true)
Sets the given truth value at the given index.
bool get(uint64_t index) const
Retrieves the truth value of the bit at the given index and performs a bound check.
A class that holds a possibly non-square matrix in the compressed row storage format.
SparseMatrix getSubmatrix(bool useGroups, storm::storage::BitVector const &rowConstraint, storm::storage::BitVector const &columnConstraint, bool insertDiagonalEntries=false, storm::storage::BitVector const &makeZeroColumns=storm::storage::BitVector()) const
Creates a submatrix of the current matrix by dropping all rows and columns whose bits are not set to ...
A class that provides convenience operations to display run times.
bool updateProgress(uint64_t count)
Updates the progress to the current count and prints it if the delay passed.
void setMaxCount(uint64_t maxCount)
Sets the maximal possible count.
void startNewMeasurement(uint64_t startCount)
Starts a new measurement, dropping all progress information collected so far.
A class that provides convenience operations to display run times.
void stop()
Stop stopwatch and add measured time to total time.
#define STORM_LOG_INFO(message)
#define STORM_LOG_WARN(message)
#define STORM_LOG_TRACE(message)
#define STORM_LOG_ASSERT(cond, message)
#define STORM_LOG_THROW(cond, exception, message)
#define STORM_LOG_WARN_COND_DEBUG(cond, message)
SFTBDDChecker::ValueType ValueType
bool constexpr minimize(OptimizationDirection d)
bool isTerminate()
Check whether the program should terminate (due to some abort signal).
void setVectorValues(std::vector< T > &vector, storm::storage::BitVector const &positions, std::vector< T > const &values)
Sets the provided values at the provided positions in the given vector.
std::vector< Type > filterVector(std::vector< Type > const &in, storm::storage::BitVector const &filter)
NumberTraits< RationalType >::IntegerType denominator(RationalType const &number)
bool isAlmostZero(ValueType const &a)
bool isZero(ValueType const &a)
TargetType convertNumber(SourceType const &number)
static const bool IsExact
StronglyConnectedComponentDecompositionOptions & computeSccDepths(bool value=true)
Sets if scc depths can be retrieved.
StronglyConnectedComponentDecompositionOptions & forceTopologicalSort(bool value=true)
Enforces that the returned SCCs are sorted in a topological order.