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
const prec = subEnv.solver().minMax().getPrecision();
46 if (subEnv.solver().minMax().getRelativeTerminationCriterion()) {
51 subEnv.solver().minMax().setPrecision(prec / divisor);
56template<
typename ValueType,
typename SolutionType>
58 std::vector<SolutionType>& x,
59 std::vector<ValueType>
const& b)
const {
60 STORM_LOG_ASSERT(x.size() == this->A->getRowGroupCount(),
"Provided x-vector has invalid size.");
61 STORM_LOG_ASSERT(b.size() == this->A->getRowCount(),
"Provided b-vector has invalid size.");
66 if (!this->sortedSccDecomposition || (needAdaptPrecision && !this->longestSccChainSize)) {
69 createSortedSccDecomposition(needAdaptPrecision);
72 << sccSw <<
". Found " << this->sortedSccDecomposition->size() <<
" SCC(s) containing a total of " << x.size()
73 <<
" states. Average SCC size is "
74 <<
static_cast<double>(this->A->getRowGroupCount()) /
static_cast<double>(this->sortedSccDecomposition->size()) <<
".");
78 needAdaptPrecision = needAdaptPrecision && (this->sortedSccDecomposition->size() != this->
A->getRowGroupCount());
80 storm::Environment sccSolverEnvironment = getEnvironmentForUnderlyingSolver(env, needAdaptPrecision);
82 if (this->longestSccChainSize) {
83 STORM_LOG_INFO(
"Longest SCC chain size is " << this->longestSccChainSize.get());
86 bool returnValue =
true;
90 bool allSccsExact =
true;
91 bool allSccsPrecise =
true;
92 if (this->sortedSccDecomposition->size() == 1 && (!this->choiceFixedForRowGroup || this->choiceFixedForRowGroup.get().empty())) {
94 if (
auto const& scc = *this->sortedSccDecomposition->begin(); scc.size() == 1) {
99 returnValue = solveTrivialScc(*scc.begin(), dir, x, b);
101 returnValue = solveFullyConnectedEquationSystem(sccSolverEnvironment, dir, x, b);
102 allSccsExact = this->sccSolver->hasExactSolutionBounds();
103 allSccsPrecise = lastSccMetPrecision(sccSolverEnvironment, x);
114 std::optional<storm::storage::BitVector> newRelevantValues;
116 this->sortedSccDecomposition->size() < this->A->getRowGroupCount()) {
119 std::vector<uint64_t> rowGroupToScc = this->sortedSccDecomposition->computeStateToSccIndexMap(this->
A->getRowGroupCount());
120 for (uint64_t rowGroup = 0; rowGroup < this->
A->getRowGroupCount(); ++rowGroup) {
121 auto currScc = rowGroupToScc[rowGroup];
122 for (
auto const& successor : this->
A->getRowGroup(rowGroup)) {
123 if (rowGroupToScc[successor.getColumn()] != currScc) {
124 newRelevantValues->set(successor.getColumn(),
true);
130 uint64_t sccIndex = 0;
134 for (
auto const& scc : *this->sortedSccDecomposition) {
135 if (scc.size() == 1) {
136 returnValue = solveTrivialScc(*scc.begin(), dir, x, b) && returnValue;
139 for (
auto const& state : scc) {
140 sccAsBitVector.
set(state,
true);
142 bool sccMetPrecision =
false;
143 returnValue = solveScc(sccSolverEnvironment, dir, scc, sccAsBitVector, x, b, newRelevantValues, sccMetPrecision) && returnValue;
144 allSccsExact = allSccsExact && this->sccSolver->hasExactSolutionBounds();
145 allSccsPrecise = allSccsPrecise && sccMetPrecision;
147 if (scc.size() * 64 < sccAsBitVector.
size()) {
148 for (
auto const& state : scc) {
149 sccAsBitVector.
set(state,
false);
152 sccAsBitVector.
clear();
158 STORM_LOG_WARN(
"Topological solver aborted after analyzing " << sccIndex <<
"/" << this->sortedSccDecomposition->size() <<
" SCCs.");
165 if (returnValue && !aborted) {
166 trySetSolutionBounds(env, x, allSccsExact, allSccsPrecise && needAdaptPrecision);
176template<
typename ValueType,
typename SolutionType>
177void TopologicalMinMaxLinearEquationSolver<ValueType, SolutionType>::trySetSolutionBounds(
Environment const& env, std::vector<SolutionType>
const& x,
178 bool allSccsExact,
bool allSccsPrecise)
const {
180 this->setSolutionBoundsExact(x);
183 if (!allSccsPrecise) {
190template<
typename ValueType,
typename SolutionType>
191bool TopologicalMinMaxLinearEquationSolver<ValueType, SolutionType>::lastSccMetPrecision(Environment
const& sccSolverEnvironment,
192 std::vector<SolutionType>
const& sccValues)
const {
193 return this->sccSolver->getSolutionBounds().isWithinPrecision(
195 sccSolverEnvironment.solver().minMax().getRelativeTerminationCriterion());
198template<
typename ValueType,
typename SolutionType>
199void TopologicalMinMaxLinearEquationSolver<ValueType, SolutionType>::createSortedSccDecomposition(
bool needLongestChainSize)
const {
201 this->sortedSccDecomposition = std::make_unique<storm::storage::StronglyConnectedComponentDecomposition<ValueType>>(
202 *this->A, storm::storage::StronglyConnectedComponentDecompositionOptions().forceTopologicalSort().computeSccDepths(needLongestChainSize));
203 if (needLongestChainSize) {
204 this->longestSccChainSize = this->sortedSccDecomposition->getMaxSccDepth() + 1;
208template<
typename ValueType,
typename SolutionType>
209bool TopologicalMinMaxLinearEquationSolver<ValueType, SolutionType>::solveTrivialScc(uint64_t
const& sccState,
OptimizationDirection dir,
210 std::vector<SolutionType>& globalX,
211 std::vector<ValueType>
const& globalB)
const {
212 SolutionType& xi = globalX[sccState];
213 if (this->choiceFixedForRowGroup && this->choiceFixedForRowGroup.get()[sccState]) {
215 uint_fast64_t row = this->A->getRowGroupIndices()[sccState] + this->getInitialScheduler()[sccState];
217 bool hasDiagonalEntry =
false;
219 for (
auto const& entry : this->A->getRow(row)) {
220 if (entry.getColumn() == sccState) {
221 hasDiagonalEntry =
true;
224 rowValue += entry.getValue() * globalX[entry.getColumn()];
227 if (hasDiagonalEntry) {
230 "State " << sccState <<
" has a selfloop with probability '1-(" << denominator <<
")'. This could be an indication for numerical issues.");
239 xi = std::move(rowValue);
241 bool firstRow =
true;
243 for (uint64_t row = this->A->getRowGroupIndices()[sccState]; row < this->A->getRowGroupIndices()[sccState + 1]; ++row) {
245 bool hasDiagonalEntry =
false;
247 for (
auto const& entry : this->A->getRow(row)) {
248 if (entry.getColumn() == sccState) {
249 hasDiagonalEntry =
true;
252 rowValue += entry.getValue() * globalX[entry.getColumn()];
255 if (hasDiagonalEntry) {
258 "State " << sccState <<
" has a selfloop with probability '1-(" << denominator <<
")'. This could be an indication for numerical issues.");
269 xi = std::move(rowValue);
275 xi = std::move(rowValue);
280 xi = std::move(rowValue);
286 if (this->isTrackSchedulerSet()) {
287 this->schedulerChoices.get()[sccState] = bestRow - this->A->getRowGroupIndices()[sccState];
289 STORM_LOG_THROW(!firstRow, storm::exceptions::UnexpectedException,
"Empty row group in MinMax equation system.");
294template<
typename ValueType,
typename SolutionType>
295bool TopologicalMinMaxLinearEquationSolver<ValueType, SolutionType>::solveFullyConnectedEquationSystem(storm::Environment
const& sccSolverEnvironment,
297 std::vector<ValueType>
const& b)
const {
298 STORM_LOG_ASSERT(!this->choiceFixedForRowGroup || this->choiceFixedForRowGroup.get().empty(),
299 "Expecting no fixed choices for states when solving the fully connected equation system.");
300 if (!this->sccSolver) {
302 this->sccSolver->setCachingEnabled(
true);
304 this->sccSolver->setMatrix(*this->A);
305 this->sccSolver->setHasUniqueSolution(this->hasUniqueSolution());
306 this->sccSolver->setHasNoEndComponents(this->hasNoEndComponents());
307 this->sccSolver->setTrackScheduler(this->isTrackSchedulerSet());
308 if (this->hasInitialScheduler()) {
309 auto choices = this->getInitialScheduler();
310 this->sccSolver->setInitialScheduler(std::move(choices));
312 if (this->hasRelevantValues()) {
313 this->sccSolver->setRelevantValues(this->getRelevantValues());
315 auto req = this->sccSolver->getRequirements(sccSolverEnvironment, dir);
316 this->sccSolver->setBoundsFromOtherSolver(*
this);
317 if (req.upperBounds() && this->hasUpperBound()) {
318 req.clearUpperBounds();
320 if (req.lowerBounds() && this->hasLowerBound()) {
321 req.clearLowerBounds();
323 if (req.validInitialScheduler() && this->hasInitialScheduler()) {
324 req.clearValidInitialScheduler();
326 if (req.uniqueSolution() && this->hasUniqueSolution()) {
327 req.clearUniqueSolution();
329 STORM_LOG_THROW(!req.hasEnabledCriticalRequirement(), storm::exceptions::UncheckedRequirementException,
330 "Solver requirements " + req.getEnabledRequirementsAsString() +
" not checked.");
331 this->sccSolver->setRequirementsChecked(
true);
333 bool res = this->sccSolver->solveEquations(sccSolverEnvironment, dir, x, b);
334 if (this->isTrackSchedulerSet()) {
335 this->schedulerChoices = this->sccSolver->getSchedulerChoices();
340template<
typename ValueType,
typename SolutionType>
341bool TopologicalMinMaxLinearEquationSolver<ValueType, SolutionType>::solveScc(storm::Environment
const& sccSolverEnvironment,
OptimizationDirection dir,
342 storm::storage::StronglyConnectedComponent
const& scc,
343 storm::storage::BitVector
const& sccAsBitVector,
344 std::vector<SolutionType>& globalX, std::vector<ValueType>
const& globalB,
345 std::optional<storm::storage::BitVector>
const& globalRelevantValues,
346 bool& metPrecision)
const {
348 auto restrictToScc = [&scc](
auto const& globalVector) {
349 std::vector<std::decay_t<
decltype(globalVector[0])>> result;
350 result.reserve(scc.
size());
351 for (uint64_t
const state : scc) {
352 result.push_back(globalVector[state]);
358 if (!this->sccSolver) {
360 this->sccSolver->setCachingEnabled(
true);
362 if (!this->sccSubmatrixBuilder) {
363 this->sccSubmatrixBuilder = std::make_unique<storm::storage::SubmatrixBuilder<ValueType>>(*this->A);
365 this->sccSolver->setHasUniqueSolution(this->hasUniqueSolution());
366 this->sccSolver->setHasNoEndComponents(this->hasNoEndComponents());
367 this->sccSolver->setTrackScheduler(this->isTrackSchedulerSet());
368 if (globalRelevantValues) {
369 storm::storage::BitVector sccRelevantValues(scc.size(),
false);
370 uint64_t sccState = 0;
371 for (uint64_t
const state : scc) {
372 sccRelevantValues.set(sccState++, globalRelevantValues->get(state));
374 this->sccSolver->setRelevantValues(std::move(sccRelevantValues));
377 auto const& rowGroupIndices = this->A->getRowGroupIndices();
378 bool const hasFixedChoices = this->choiceFixedForRowGroup && !this->choiceFixedForRowGroup->empty();
379 auto const isChoiceFixed = [&](uint64_t state) {
return hasFixedChoices && (*this->choiceFixedForRowGroup)[state]; };
382 auto const rowRange = [&](uint64_t state) {
383 if (isChoiceFixed(state)) {
384 auto const row = rowGroupIndices[state] + this->getInitialScheduler()[state];
385 STORM_LOG_TRACE(
"Fixing state " << state <<
" to choice " << this->getInitialScheduler()[state] <<
".");
386 return std::make_pair(row, row + 1);
388 return std::make_pair(rowGroupIndices[state], rowGroupIndices[state + 1]);
393 std::vector<uint64_t> sccRows;
394 uint64_t sccRowCount = 0;
395 for (uint64_t
const state : scc) {
396 auto const [rowBegin, rowEnd] = rowRange(state);
397 sccRowCount += rowEnd - rowBegin;
398 if (hasFixedChoices) {
399 for (uint64_t row = rowBegin; row < rowEnd; ++row) {
400 sccRows.push_back(row);
404 if (hasFixedChoices) {
405 this->sccSolver->setMatrix(this->sccSubmatrixBuilder->getByRowConstraint(sccRows, scc,
false));
407 this->sccSolver->setMatrix(this->sccSubmatrixBuilder->getByRowGroupConstraint(scc, scc,
false));
409 if (this->hasInitialScheduler()) {
410 auto sccInitChoices = restrictToScc(this->getInitialScheduler());
411 if (hasFixedChoices) {
414 uint64_t sccState = 0;
415 for (uint64_t
const state : scc) {
416 if (isChoiceFixed(state)) {
417 sccInitChoices[sccState] = 0;
422 this->sccSolver->setInitialScheduler(std::move(sccInitChoices));
426 auto sccX = restrictToScc(globalX);
429 std::vector<ValueType> sccB;
430 sccB.reserve(sccRowCount);
431 for (uint64_t
const state : scc) {
432 auto const [rowBegin, rowEnd] = rowRange(state);
433 for (uint64_t row = rowBegin; row < rowEnd; ++row) {
435 for (
auto const& entry : this->A->getRow(row)) {
436 if (!sccAsBitVector.
get(entry.getColumn())) {
437 bi += entry.getValue() * globalX[entry.getColumn()];
440 sccB.push_back(std::move(bi));
444 auto req = this->sccSolver->getRequirements(sccSolverEnvironment, dir);
445 this->sccSolver->clearBounds();
447 if (this->hasLowerBound(storm::solver::AbstractEquationSolver<ValueType>::BoundType::Global)) {
448 this->sccSolver->setLowerBound(this->getLowerBound());
449 req.clearLowerBounds();
450 }
else if (this->hasLowerBound(storm::solver::AbstractEquationSolver<ValueType>::BoundType::Local)) {
451 this->sccSolver->setLowerBounds(restrictToScc(this->getLowerBounds()));
452 req.clearLowerBounds();
454 if (this->hasUpperBound(storm::solver::AbstractEquationSolver<ValueType>::BoundType::Global)) {
455 this->sccSolver->setUpperBound(this->getUpperBound());
456 req.clearUpperBounds();
457 }
else if (this->hasUpperBound(storm::solver::AbstractEquationSolver<ValueType>::BoundType::Local)) {
458 this->sccSolver->setUpperBounds(restrictToScc(this->getUpperBounds()));
459 req.clearUpperBounds();
463 if (req.validInitialScheduler() && (this->hasInitialScheduler() || this->hasNoEndComponents())) {
464 req.clearValidInitialScheduler();
466 if (req.uniqueSolution() && this->hasUniqueSolution()) {
467 req.clearUniqueSolution();
469 STORM_LOG_THROW(!req.hasEnabledCriticalRequirement(), storm::exceptions::UncheckedRequirementException,
470 "Solver requirements " + req.getEnabledRequirementsAsString() +
" not checked.");
471 this->sccSolver->setRequirementsChecked(
true);
474 bool res = this->sccSolver->solveEquations(sccSolverEnvironment, dir, sccX, sccB);
475 metPrecision = lastSccMetPrecision(sccSolverEnvironment, sccX);
478 if (this->isTrackSchedulerSet()) {
479 auto const& sccChoices = this->sccSolver->getSchedulerChoices();
480 uint64_t sccState = 0;
481 for (uint64_t
const state : scc) {
482 this->schedulerChoices.get()[state] = sccChoices[sccState++];
485 uint64_t sccState = 0;
486 for (uint64_t
const state : scc) {
487 globalX[state] = std::move(sccX[sccState++]);
493template<
typename ValueType,
typename SolutionType>
502template<
typename ValueType,
typename SolutionType>
504 sortedSccDecomposition.reset();
505 longestSccChainSize = boost::none;
507 sccSubmatrixBuilder.reset();
508 auxiliaryRowGroupVector.reset();
SolverEnvironment & solver()
storm::RationalNumber const & getPrecision() const
bool const & getRelativeTerminationCriterion() const
TopologicalSolverEnvironment & topological()
MinMaxSolverEnvironment & minMax()
uint64_t getShowProgressDelay() const
bool isForceSoundness() const
storm::solver::MinMaxMethod const & getUnderlyingMinMaxMethod() const
bool const & isUnderlyingMinMaxMethodSetFromDefault() const
bool isExtendRelevantValues() const
storm::storage::BitVector const & getRelevantValues() const
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
bool hasUniqueSolution() const
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.
void set(uint64_t index, bool value=true)
Sets the given truth value at the given index.
size_t size() const
Retrieves the number of bits this bit vector can store.
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.
std::size_t size() const
Retrieves the number of states in this SCC.
A class that provides convenience operations to display run times.
bool updateProgress(uint64_t count)
Updates the progress to the current count and logs it (on the progress log channel) if the delay pass...
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).
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