Storm 1.14.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
AbstractEquationSolver.cpp
Go to the documentation of this file.
2
3#include <algorithm>
4
11
12namespace storm {
13namespace solver {
14
15template<typename SolutionType>
17 // Intentionally left empty. Call setShowProgress() after construction to enable progress.
18}
20template<typename SolutionType>
22 if (verbose) {
23 this->progressMeasurement = storm::utility::ProgressMeasurement("iterations", delay);
24 }
25}
26
27template<typename SolutionType>
31
32template<typename SolutionType>
36
37template<typename SolutionType>
41
42template<typename SolutionType>
46
47template<typename SolutionType>
48std::unique_ptr<TerminationCondition<SolutionType>> const& AbstractEquationSolver<SolutionType>::getTerminationConditionPointer() const {
50}
52template<typename SolutionType>
53bool AbstractEquationSolver<SolutionType>::terminateNow(std::vector<SolutionType> const& values, SolverGuarantee const& guarantee) const {
54 if (!this->hasCustomTerminationCondition()) {
55 return false;
56 }
57
58 return this->getTerminationCondition().terminateNow(values, guarantee);
59}
60
61template<typename SolutionType>
63 return static_cast<bool>(relevantValues);
64}
65
66template<typename SolutionType>
70
71template<typename SolutionType>
72boost::optional<storm::storage::BitVector> const& AbstractEquationSolver<SolutionType>::getOptionalRelevantValues() const {
73 return relevantValues;
74}
75
76template<typename SolutionType>
90
91template<typename SolutionType>
93 if (type == BoundType::Any) {
94 return static_cast<bool>(lowerBound) || static_cast<bool>(lowerBounds);
95 } else if (type == BoundType::Global) {
96 return static_cast<bool>(lowerBound);
97 } else if (type == BoundType::Local) {
98 return static_cast<bool>(lowerBounds);
99 }
100 return false;
101}
102
103template<typename SolutionType>
105 if (type == BoundType::Any) {
106 return static_cast<bool>(upperBound) || static_cast<bool>(upperBounds);
107 } else if (type == BoundType::Global) {
108 return static_cast<bool>(upperBound);
109 } else if (type == BoundType::Local) {
110 return static_cast<bool>(upperBounds);
111 }
112 return false;
113}
114
115template<typename SolutionType>
117 lowerBound = value;
118}
119
120template<typename SolutionType>
122 upperBound = value;
123}
124
125template<typename SolutionType>
126void AbstractEquationSolver<SolutionType>::setBounds(SolutionType const& lower, SolutionType const& upper) {
127 setLowerBound(lower);
129}
130
131template<typename SolutionType>
133 return lowerBound.get();
134}
135
136template<typename SolutionType>
137SolutionType const& AbstractEquationSolver<SolutionType>::getLowerBound(uint64_t const& index) const {
138 if (lowerBounds) {
139 STORM_LOG_ASSERT(index < lowerBounds->size(), "Invalid row index " << index << " for vector of size " << lowerBounds->size());
140 if (lowerBound) {
141 return std::max(lowerBound.get(), lowerBounds.get()[index]);
142 } else {
143 return lowerBounds.get()[index];
144 }
145 } else {
146 STORM_LOG_ASSERT(lowerBound, "Lower bound requested but was not specified before.");
147 return lowerBound.get();
148 }
149}
150
151template<typename SolutionType>
152SolutionType AbstractEquationSolver<SolutionType>::getLowerBound(bool convertLocalBounds) const {
153 if (lowerBound) {
154 return lowerBound.get();
155 } else if (convertLocalBounds) {
156 return *std::min_element(lowerBounds->begin(), lowerBounds->end());
158 STORM_LOG_THROW(false, storm::exceptions::InvalidOperationException, "No lower bound available but some was requested.");
159 return SolutionType();
160}
161
162template<typename SolutionType>
164 return upperBound.get();
165}
166
167template<typename SolutionType>
168SolutionType const& AbstractEquationSolver<SolutionType>::getUpperBound(uint64_t const& index) const {
169 if (upperBounds) {
170 STORM_LOG_ASSERT(index < upperBounds->size(), "Invalid row index " << index << " for vector of size " << upperBounds->size());
171 if (upperBound) {
172 return std::min(upperBound.get(), upperBounds.get()[index]);
173 } else {
174 return upperBounds.get()[index];
175 }
176 } else {
177 STORM_LOG_ASSERT(upperBound, "Upper bound requested but was not specified before.");
178 return upperBound.get();
180}
181
182template<typename SolutionType>
183SolutionType AbstractEquationSolver<SolutionType>::getUpperBound(bool convertLocalBounds) const {
185 return upperBound.get();
186 } else if (convertLocalBounds) {
187 return *std::max_element(upperBounds->begin(), upperBounds->end());
188 }
189 STORM_LOG_THROW(false, storm::exceptions::InvalidOperationException, "No upper bound available but some was requested.");
190 return SolutionType();
192
193template<typename SolutionType>
194std::vector<SolutionType> const& AbstractEquationSolver<SolutionType>::getLowerBounds() const {
195 return lowerBounds.get();
197
198template<typename SolutionType>
199std::vector<SolutionType> const& AbstractEquationSolver<SolutionType>::getUpperBounds() const {
200 return upperBounds.get();
201}
203template<typename SolutionType>
204void AbstractEquationSolver<SolutionType>::setLowerBounds(std::vector<SolutionType> const& values) {
205 lowerBounds = values;
206}
207
208template<typename SolutionType>
209void AbstractEquationSolver<SolutionType>::setLowerBounds(std::vector<SolutionType>&& values) {
210 lowerBounds = std::move(values);
211}
212
213template<typename SolutionType>
214void AbstractEquationSolver<SolutionType>::setUpperBounds(std::vector<SolutionType> const& values) {
215 upperBounds = values;
216}
217
218template<typename SolutionType>
219void AbstractEquationSolver<SolutionType>::setUpperBounds(std::vector<SolutionType>&& values) {
220 upperBounds = std::move(values);
221}
222
223template<typename SolutionType>
224void AbstractEquationSolver<SolutionType>::setBounds(std::vector<SolutionType> const& lower, std::vector<SolutionType> const& upper) {
225 setLowerBounds(lower);
226 setUpperBounds(upper);
227}
229template<typename SolutionType>
231 if (other.hasLowerBound(BoundType::Global)) {
232 this->setLowerBound(other.getLowerBound());
233 }
234 if (other.hasLowerBound(BoundType::Local)) {
235 this->setLowerBounds(other.getLowerBounds());
237 if (other.hasUpperBound(BoundType::Global)) {
238 this->setUpperBound(other.getUpperBound());
239 }
240 if (other.hasUpperBound(BoundType::Local)) {
241 this->setUpperBounds(other.getUpperBounds());
242 }
244
245template<typename SolutionType>
247 return solutionBounds.hasLower();
248}
249
250template<typename SolutionType>
252 return solutionBounds.hasUpper();
253}
254
255template<typename SolutionType>
257 return solutionBounds.isExact();
259
260template<typename SolutionType>
261std::vector<SolutionType> const& AbstractEquationSolver<SolutionType>::getSolutionLowerBounds() const {
262 STORM_LOG_ASSERT(this->hasSolutionLowerBounds(), "No lower bound on the solution was computed.");
263 return *solutionBounds.lower;
265
266template<typename SolutionType>
267std::vector<SolutionType> const& AbstractEquationSolver<SolutionType>::getSolutionUpperBounds() const {
268 STORM_LOG_ASSERT(this->hasSolutionUpperBounds(), "No upper bound on the solution was computed.");
269 return *solutionBounds.upper;
270}
271
272template<typename SolutionType>
276
277template<typename SolutionType>
279 STORM_LOG_ASSERT(!bounds.hasLower() || !bounds.hasUpper() || bounds.lower->size() == bounds.upper->size(),
280 "Bounds on the solution must have the same size.");
281 solutionBounds = std::move(bounds);
282}
283
284template<typename SolutionType>
285void AbstractEquationSolver<SolutionType>::setSolutionBoundsExact(std::vector<SolutionType> const& x) const {
287 bounds.setExact(x);
288 this->setSolutionBounds(std::move(bounds));
289}
290
291template<typename SolutionType>
292void AbstractEquationSolver<SolutionType>::setSolutionBoundsFromPrecision(std::vector<SolutionType> const& x, SolutionType const& precision,
293 bool relative) const {
294 if constexpr (std::is_same_v<SolutionType, storm::RationalFunction>) {
295 STORM_LOG_THROW(false, storm::exceptions::InvalidOperationException, "Cannot derive solution bounds from a precision for rational functions.");
296 } else {
297 STORM_LOG_ASSERT(!relative || precision < storm::utility::one<SolutionType>(), "A relative precision of one or more carries no information.");
299 std::vector<SolutionType>& lower = bounds.lower.emplace(x.size());
300 std::vector<SolutionType>& upper = bounds.upper.emplace(x.size());
301 for (uint64_t i = 0; i < x.size(); ++i) {
302 // For the relative criterion the guarantee reads |x_i - s_i| <= precision * |s_i| for the exact solution s.
303 // That gives |s_i| <= |x_i| / (1 - precision) and hence the deviation below, which does not refer to s.
304 SolutionType const deviation =
305 relative ? precision * storm::utility::abs<SolutionType>(x[i]) / (storm::utility::one<SolutionType>() - precision) : precision;
306 lower[i] = x[i] - deviation;
307 upper[i] = x[i] + deviation;
308 }
309 this->setSolutionBounds(std::move(bounds));
310 }
311}
312
313template<typename SolutionType>
314void AbstractEquationSolver<SolutionType>::finalizeSolutionBounds(std::vector<SolutionType> const& x) const {
315 if constexpr (std::is_same_v<SolutionType, storm::RationalFunction>) {
316 // Rational functions are not ordered, so there is no tighter of two bounds to pick.
317 return;
318 } else {
319 // Bounds that were known beforehand hold for the solution, so they tighten whatever was computed.
320 if (this->hasLowerBound()) {
321 bool const hadBound = solutionBounds.hasLower();
322 std::vector<SolutionType>& bound = hadBound ? *solutionBounds.lower : solutionBounds.lower.emplace(x.size());
323 STORM_LOG_ASSERT(bound.size() == x.size(), "Computed lower bound does not match the size of the solution.");
324 for (uint64_t i = 0; i < x.size(); ++i) {
325 bound[i] = hadBound ? std::max(bound[i], this->getLowerBound(i)) : this->getLowerBound(i);
326 }
327 }
328 if (this->hasUpperBound()) {
329 bool const hadBound = solutionBounds.hasUpper();
330 std::vector<SolutionType>& bound = hadBound ? *solutionBounds.upper : solutionBounds.upper.emplace(x.size());
331 STORM_LOG_ASSERT(bound.size() == x.size(), "Computed upper bound does not match the size of the solution.");
332 for (uint64_t i = 0; i < x.size(); ++i) {
333 bound[i] = hadBound ? std::min(bound[i], this->getUpperBound(i)) : this->getUpperBound(i);
334 }
335 }
336 // Every step between establishing a bound and handing back a solution, the extraction of a scheduler among
337 // them, can move a value a rounding error past it. Following the values keeps the bounds sound.
338 solutionBounds.widenTo(x);
339 }
340}
341
342template<typename SolutionType>
344 solutionBounds.clear();
345}
346
347template<typename SolutionType>
349 lowerBound = boost::none;
350 upperBound = boost::none;
351 lowerBounds = boost::none;
352 upperBounds = boost::none;
353}
354
355template<typename SolutionType>
356void AbstractEquationSolver<SolutionType>::createLowerBoundsVector(std::vector<SolutionType>& lowerBoundsVector) const {
357 if (this->hasLowerBound(BoundType::Local)) {
358 lowerBoundsVector = this->getLowerBounds();
359 } else {
360 SolutionType lowerBound = this->hasLowerBound(BoundType::Global) ? this->getLowerBound() : storm::utility::zero<SolutionType>();
361 for (auto& e : lowerBoundsVector) {
362 e = lowerBound;
363 }
364 }
365}
366
367template<typename SolutionType>
368void AbstractEquationSolver<SolutionType>::createUpperBoundsVector(std::vector<SolutionType>& upperBoundsVector) const {
369 STORM_LOG_ASSERT(this->hasUpperBound(), "Expecting upper bound(s).");
370 if (this->hasUpperBound(BoundType::Global)) {
371 upperBoundsVector.assign(upperBoundsVector.size(), this->getUpperBound());
372 } else {
373 upperBoundsVector.assign(this->getUpperBounds().begin(), this->getUpperBounds().end());
374 }
375}
376
377template<typename SolutionType>
378void AbstractEquationSolver<SolutionType>::createUpperBoundsVector(std::unique_ptr<std::vector<SolutionType>>& upperBoundsVector, uint64_t length) const {
379 STORM_LOG_ASSERT(this->hasUpperBound(), "Expecting upper bound(s).");
380 if (!upperBoundsVector) {
381 if (this->hasUpperBound(BoundType::Local)) {
382 STORM_LOG_ASSERT(length == this->getUpperBounds().size(), "Mismatching sizes.");
383 upperBoundsVector = std::make_unique<std::vector<SolutionType>>(this->getUpperBounds());
384 } else {
385 upperBoundsVector = std::make_unique<std::vector<SolutionType>>(length, this->getUpperBound());
386 }
387 } else {
388 createUpperBoundsVector(*upperBoundsVector);
389 }
390}
391
392template<typename SolutionType>
394 return this->progressMeasurement.is_initialized();
395}
396
397template<typename SolutionType>
399 STORM_LOG_ASSERT(this->isShowProgressSet(), "Tried to get the progress message delay but progress is not shown.");
400 return this->progressMeasurement->getShowProgressDelay();
401}
402
403template<typename SolutionType>
405 if (this->isShowProgressSet()) {
406 this->progressMeasurement->startNewMeasurement(startingIteration);
407 }
408}
409
410template<typename SolutionType>
411void AbstractEquationSolver<SolutionType>::showProgressIterative(uint64_t iteration, boost::optional<uint64_t> const& bound) const {
412 if (this->isShowProgressSet()) {
413 if (bound) {
414 this->progressMeasurement->setMaxCount(bound.get());
415 }
416 this->progressMeasurement->updateProgress(iteration);
417 }
418}
419
420template<typename SolutionType>
421void AbstractEquationSolver<SolutionType>::reportStatus(SolverStatus status, boost::optional<uint64_t> const& iterations) const {
422 if (iterations) {
423 switch (status) {
425 STORM_LOG_TRACE("Iterative solver converged after " << iterations.get() << " iterations.");
426 break;
428 STORM_LOG_TRACE("Iterative solver terminated early after " << iterations.get() << " iterations.");
429 break;
431 STORM_LOG_WARN("Iterative solver did not converge after " << iterations.get() << " iterations.");
432 break;
434 STORM_LOG_WARN("Iterative solver was aborted after " << iterations.get() << " iterations.");
435 break;
436 default:
437 STORM_LOG_THROW(false, storm::exceptions::InvalidStateException, "Iterative solver terminated unexpectedly.");
438 }
439 } else {
440 switch (status) {
442 STORM_LOG_TRACE("Solver converged.");
443 break;
445 STORM_LOG_TRACE("Solver terminated early.");
446 break;
448 STORM_LOG_ASSERT(false, "Non-iterative solver should not exceed maximal number of iterations.");
449 STORM_LOG_WARN("Solver did not converge.");
450 break;
452 STORM_LOG_WARN("Solver was aborted.");
453 break;
454 default:
455 STORM_LOG_THROW(false, storm::exceptions::InvalidStateException, "Solver terminated unexpectedly.");
456 }
457 }
458}
459
460template<typename SolutionType>
461SolverStatus AbstractEquationSolver<SolutionType>::updateStatus(SolverStatus status, bool earlyTermination, uint64_t iterations,
462 uint64_t maximalNumberOfIterations) const {
463 if (status != SolverStatus::Converged) {
464 if (earlyTermination) {
466 } else if (iterations >= maximalNumberOfIterations) {
469 status = SolverStatus::Aborted;
470 }
471 }
472 return status;
473}
474
475template<typename SolutionType>
476SolverStatus AbstractEquationSolver<SolutionType>::updateStatus(SolverStatus status, std::vector<SolutionType> const& x, SolverGuarantee const& guarantee,
477 uint64_t iterations, uint64_t maximalNumberOfIterations) const {
478 return this->updateStatus(status, this->hasCustomTerminationCondition() && this->getTerminationCondition().terminateNow(x, guarantee), iterations,
479 maximalNumberOfIterations);
480}
481
482template class AbstractEquationSolver<double>;
483
486
487} // namespace solver
488} // namespace storm
bool hasLowerBound(BoundType const &type=BoundType::Any) const
Retrieves whether this solver has a lower bound.
void setSolutionBoundsExact(std::vector< SolutionType > const &x) const
Reports the given solution as an exact one, i.e.
void setRelevantValues(storm::storage::BitVector &&valuesOfInterest)
Sets the relevant values.
void setSolutionBounds(SolutionBounds< SolutionType > bounds) const
Stores sound bounds on the solution that were obtained while solving.
bool hasCustomTerminationCondition() const
Retrieves whether a custom termination condition has been set.
std::unique_ptr< TerminationCondition< SolutionType > > const & getTerminationConditionPointer() const
void showProgressIterative(uint64_t iterations, boost::optional< uint64_t > const &bound=boost::none) const
Shows progress if this solver is asked to do so.
void setTerminationCondition(std::unique_ptr< TerminationCondition< SolutionType > > terminationCondition)
Sets a custom termination condition that is used together with the regular termination condition of t...
void setBounds(SolutionType const &lower, SolutionType const &upper)
Sets bounds for the solution that can potentially be used by the solver.
boost::optional< storm::storage::BitVector > const & getOptionalRelevantValues() const
std::vector< SolutionType > const & getSolutionUpperBounds() const
storm::storage::BitVector const & getRelevantValues() const
Retrieves the relevant values (if there are any).
bool hasUpperBound(BoundType const &type=BoundType::Any) const
Retrieves whether this solver has an upper bound.
std::unique_ptr< TerminationCondition< SolutionType > > terminationCondition
bool isShowProgressSet() const
Retrieves whether progress is to be shown.
bool hasExactSolutionBounds() const
Retrieves whether the bounds computed by the last call to this solver coincide.
SolutionType const & getUpperBound() const
Retrieves the upper bound (if there is any).
void setBoundsFromOtherSolver(AbstractEquationSolver< SolutionType > const &other)
void setSolutionBoundsFromPrecision(std::vector< SolutionType > const &x, SolutionType const &precision, bool relative) const
Turns a precision that the solving procedure is known to have achieved into sound bounds on the solut...
SolutionBounds< SolutionType > const & getSolutionBounds() const
Retrieves both bounds at once, either of which may be unset.
std::vector< SolutionType > const & getLowerBounds() const
Retrieves a vector containing the lower bounds (if there are any).
void setShowProgress(bool verbose, uint64_t delay=5)
Configures whether this solver should show progress during iterative solving.
bool hasSolutionLowerBounds() const
Retrieves whether the last call to this solver computed a sound lower resp.
void setUpperBounds(std::vector< SolutionType > const &values)
Sets upper bounds for the solution that can potentially be used by the solver.
SolverStatus updateStatus(SolverStatus status, std::vector< SolutionType > const &x, SolverGuarantee const &guarantee, uint64_t iterations, uint64_t maximalNumberOfIterations) const
Update the status of the solver with respect to convergence, early termination, abortion,...
boost::optional< SolutionType > upperBound
void createUpperBoundsVector(std::vector< SolutionType > &upperBoundsVector) const
void setLowerBound(SolutionType const &value)
Sets a lower bound for the solution that can potentially be used by the solver.
boost::optional< storm::storage::BitVector > relevantValues
void startMeasureProgress(uint64_t startingIteration=0) const
Starts to measure progress.
void resetTerminationCondition()
Removes a previously set custom termination condition.
TerminationCondition< SolutionType > const & getTerminationCondition() const
Retrieves the custom termination condition (if any was set).
SolutionType const & getLowerBound() const
Retrieves the lower bound (if there is any).
bool hasRelevantValues() const
Retrieves whether this solver has particularly relevant values.
void clearRelevantValues()
Removes the values of interest (if there were any).
std::vector< SolutionType > const & getUpperBounds() const
Retrieves a vector containing the upper bounds (if there are any).
bool terminateNow(std::vector< SolutionType > const &values, SolverGuarantee const &guarantee) const
Checks whether the solver can terminate wrt.
void clearSolutionBounds() const
Discards any bounds on the solution obtained by a previous call.
void clearBounds()
Removes all specified solution bounds.
boost::optional< std::vector< SolutionType > > upperBounds
void reportStatus(SolverStatus status, boost::optional< uint64_t > const &iterations=boost::none) const
Report the current status of the solver.
void createLowerBoundsVector(std::vector< SolutionType > &lowerBoundsVector) const
uint64_t getShowProgressDelay() const
Retrieves the delay between progress emissions.
void finalizeSolutionBounds(std::vector< SolutionType > const &x) const
Settles the bounds to report once solving is over: the ones handed to this solver beforehand are fold...
boost::optional< std::vector< ValueType > > lowerBounds
void setUpperBound(SolutionType const &value)
Sets an upper bound for the solution that can potentially be used by the solver.
void setLowerBounds(std::vector< SolutionType > const &values)
Sets lower bounds for the solution that can potentially be used by the solver.
std::vector< SolutionType > const & getSolutionLowerBounds() const
Retrieves sound bounds on the solution that the last call to this solver computed.
A bit vector that is internally represented as a vector of 64-bit values.
Definition BitVector.h:16
A class that provides convenience operations to display run times.
#define STORM_LOG_WARN(message)
Definition logging.h:35
#define STORM_LOG_TRACE(message)
Definition logging.h:22
#define STORM_LOG_ASSERT(cond, message)
Definition macros.h:9
#define STORM_LOG_THROW(cond, exception, message)
Definition macros.h:39
bool isTerminate()
Check whether the program should terminate (due to some abort signal).
ValueType abs(ValueType const &number)
ValueType zero()
Definition constants.cpp:26
ValueType one()
Definition constants.cpp:21
Sound lower and upper bounds on a solution vector.
std::optional< std::vector< ValueType > > lower
bool hasUpper() const
Returns true if the upper bound is set.
bool hasLower() const
Returns true if the lower bound is set.
std::optional< std::vector< ValueType > > upper
void setExact(std::vector< ValueType > const &values)
Sets both bounds to the given values, i.e.