Storm 1.14.0.1
A Modern Probabilistic Model Checker
Loading...
Searching...
No Matches
GradientDescentInstantiationSearcher.cpp
Go to the documentation of this file.
2
3#include <cmath>
4#include <iostream>
5#include <random>
6
15
16namespace storm {
17namespace derivative {
18
19template<typename FunctionType>
21template<typename FunctionType>
23
24template<typename FunctionType, typename ConstantType>
25ConstantType GradientDescentInstantiationSearcher<FunctionType, ConstantType>::doStep(
27 const std::map<VariableType<FunctionType>, ConstantType>& gradient, uint64_t stepNum) {
28 const ConstantType precisionAsConstant =
30 const CoefficientType<FunctionType> precision =
32 CoefficientType<FunctionType> const oldPos = position[steppingParameter];
33 ConstantType const oldPosAsConstant = utility::convertNumber<ConstantType>(position[steppingParameter]);
34
35 ConstantType projectedGradient;
37 // Project gradient
38 ConstantType newPlainPosition = oldPosAsConstant + precisionAsConstant * gradient.at(steppingParameter);
39 auto const lower =
40 region ? utility::convertNumber<ConstantType>(region->getLowerBoundary(steppingParameter)) : utility::zero<ConstantType>() + precisionAsConstant;
41 auto const upper =
42 region ? utility::convertNumber<ConstantType>(region->getUpperBoundary(steppingParameter)) : utility::one<ConstantType>() - precisionAsConstant;
43 if (newPlainPosition < lower || newPlainPosition > upper) {
44 projectedGradient = 0;
45 } else {
46 projectedGradient = gradient.at(steppingParameter);
47 }
48 } else if (constraintMethod == GradientDescentConstraintMethod::LOGISTIC_SIGMOID) {
49 // We want the derivative of f(logit(x)), this happens to be exp(x) * f'(logit(x)) / (exp(x) + 1)^2
50 const double expX = std::exp(utility::convertNumber<double>(oldPos));
51 projectedGradient = gradient.at(steppingParameter) * utility::convertNumber<ConstantType>(expX / std::pow(expX + 1, 2));
52 } else if (constraintMethod == GradientDescentConstraintMethod::BARRIER_INFINITY) {
53 if (oldPosAsConstant < precisionAsConstant) {
54 projectedGradient = 1000;
55 } else if (oldPosAsConstant > utility::one<ConstantType>() - precisionAsConstant) {
56 projectedGradient = -1000;
57 } else {
58 projectedGradient = gradient.at(steppingParameter);
59 }
60 } else if (constraintMethod == GradientDescentConstraintMethod::BARRIER_LOGARITHMIC) {
61 // Our barrier is:
62 // log(x) if 0 < x < 0.5
63 // log(1 - x) if 0.5 <= x < 1
64 // -infinity otherwise
65 // The gradient of this is
66 // 1/x, 1/(1-x), +/-infinity respectively
67 if (oldPosAsConstant >= precisionAsConstant && oldPosAsConstant <= utility::one<ConstantType>() - precisionAsConstant) {
68 /* const double mu = (double) parameters.size() / (double) stepNum; */
69 if (oldPosAsConstant * 2 < utility::one<ConstantType>()) {
70 projectedGradient = gradient.at(steppingParameter) + logarithmicBarrierTerm / (oldPosAsConstant - precisionAsConstant);
71 } else {
72 projectedGradient =
73 gradient.at(steppingParameter) - logarithmicBarrierTerm / (utility::one<ConstantType>() - precisionAsConstant - oldPosAsConstant);
74 }
75 } else {
76 if (oldPosAsConstant < precisionAsConstant) {
77 projectedGradient = utility::one<ConstantType>() / logarithmicBarrierTerm;
78 } else if (oldPosAsConstant > utility::one<ConstantType>() - precisionAsConstant) {
79 projectedGradient = -utility::one<ConstantType>() / logarithmicBarrierTerm;
80 }
81 }
82 } else {
83 projectedGradient = gradient.at(steppingParameter);
84 }
85
86 // Compute step based on used gradient descent method
87 ConstantType step;
88 if (Adam* adam = boost::get<Adam>(&gradientDescentType)) {
89 // For this algorihm, see the various sources available on the ADAM algorithm. This implementation should
90 // be correct, as it is compared with a run of keras's ADAM optimizer in the test.
91 adam->decayingStepAverage[steppingParameter] =
92 adam->averageDecay * adam->decayingStepAverage[steppingParameter] + (utility::one<ConstantType>() - adam->averageDecay) * projectedGradient;
93 adam->decayingStepAverageSquared[steppingParameter] = adam->squaredAverageDecay * adam->decayingStepAverageSquared[steppingParameter] +
94 (utility::one<ConstantType>() - adam->squaredAverageDecay) * utility::pow(projectedGradient, 2);
95
96 const ConstantType correctedGradient =
97 adam->decayingStepAverage[steppingParameter] / (utility::one<ConstantType>() - utility::pow(adam->averageDecay, stepNum + 1));
98 const ConstantType correctedSquaredGradient =
99 adam->decayingStepAverageSquared[steppingParameter] / (utility::one<ConstantType>() - utility::pow(adam->squaredAverageDecay, stepNum + 1));
100
101 const ConstantType toSqrt = correctedSquaredGradient;
102 ConstantType sqrtResult = constantTypeSqrt(toSqrt);
103
104 step = (adam->learningRate / (sqrtResult + precisionAsConstant)) * correctedGradient;
105 } else if (RAdam* radam = boost::get<RAdam>(&gradientDescentType)) {
106 // You can compare this with the RAdam paper's "Algorithm 2: Rectified Adam".
107 // The line numbers and comments are matched.
108 // Initializing / Compute Gradient: Already happened.
109 // 2: Compute maximum length of approximated simple moving average
110 const ConstantType maxLengthApproxSMA = 2 / (utility::one<ConstantType>() - radam->squaredAverageDecay) - utility::one<ConstantType>();
111
112 // 5: Update exponential moving 2nd moment
113 radam->decayingStepAverageSquared[steppingParameter] = radam->squaredAverageDecay * radam->decayingStepAverageSquared[steppingParameter] +
114 (utility::one<ConstantType>() - radam->squaredAverageDecay) * utility::pow(projectedGradient, 2);
115 // 6: Update exponential moving 1st moment
116 radam->decayingStepAverage[steppingParameter] =
117 radam->averageDecay * radam->decayingStepAverage[steppingParameter] + (utility::one<ConstantType>() - radam->averageDecay) * projectedGradient;
118 // 7: Compute bias corrected moving average
119 const ConstantType biasCorrectedMovingAverage =
120 radam->decayingStepAverage[steppingParameter] / (utility::one<ConstantType>() - utility::pow(radam->averageDecay, stepNum + 1));
121 const ConstantType squaredAverageDecayPow = utility::pow(radam->squaredAverageDecay, stepNum + 1);
122 // 8: Compute the length of the approximated single moving average
123 const ConstantType lengthApproxSMA =
124 maxLengthApproxSMA -
125 ((2 * (utility::convertNumber<ConstantType>(stepNum) + utility::one<ConstantType>()) * squaredAverageDecayPow) / (1 - squaredAverageDecayPow));
126 // 9: If the variance is tractable, i.e. lengthApproxSMA > 4, then
127 if (lengthApproxSMA > 4) {
128 // 10: Compute adaptive learning rate
129 const ConstantType adaptiveLearningRate =
130 constantTypeSqrt((utility::one<ConstantType>() - squaredAverageDecayPow) / radam->decayingStepAverageSquared[steppingParameter]);
131 // 11: Compute the variance rectification term
132 const ConstantType varianceRectification =
133 constantTypeSqrt(((lengthApproxSMA - 4) / (maxLengthApproxSMA - 4)) * ((lengthApproxSMA - 2) / (maxLengthApproxSMA - 2)) *
134 ((maxLengthApproxSMA) / (lengthApproxSMA)));
135 // 12: Update parameters with adaptive momentum
136 step = radam->learningRate * varianceRectification * biasCorrectedMovingAverage * adaptiveLearningRate;
137 } else {
138 // 14: Update parameters with un-adapted momentum
139 step = radam->learningRate * biasCorrectedMovingAverage;
140 }
141 } else if (RmsProp* rmsProp = boost::get<RmsProp>(&gradientDescentType)) {
142 rmsProp->rootMeanSquare[steppingParameter] = rmsProp->averageDecay * rmsProp->rootMeanSquare[steppingParameter] +
143 (utility::one<ConstantType>() - rmsProp->averageDecay) * projectedGradient * projectedGradient;
144
145 const ConstantType toSqrt = rmsProp->rootMeanSquare[steppingParameter] + precisionAsConstant;
146 ConstantType sqrtResult = constantTypeSqrt(toSqrt);
147
148 step = (rmsProp->learningRate / sqrtResult) * projectedGradient;
149 } else if (Plain* plain = boost::get<Plain>(&gradientDescentType)) {
150 if (useSignsOnly) {
151 if (projectedGradient < utility::zero<ConstantType>()) {
152 step = -plain->learningRate;
153 } else if (projectedGradient > utility::zero<ConstantType>()) {
154 step = plain->learningRate;
155 } else {
157 }
158 } else {
159 step = plain->learningRate * projectedGradient;
160 }
161 } else if (Momentum* momentum = boost::get<Momentum>(&gradientDescentType)) {
162 if (useSignsOnly) {
163 if (projectedGradient < utility::zero<ConstantType>()) {
164 step = -momentum->learningRate;
165 } else if (projectedGradient > utility::zero<ConstantType>()) {
166 step = momentum->learningRate;
167 } else {
169 }
170 } else {
171 step = momentum->learningRate * projectedGradient;
172 }
173 step += momentum->momentumTerm * momentum->pastStep.at(steppingParameter);
174 momentum->pastStep[steppingParameter] = step;
175 } else if (Nesterov* nesterov = boost::get<Nesterov>(&gradientDescentType)) {
176 if (useSignsOnly) {
177 if (projectedGradient < utility::zero<ConstantType>()) {
178 step = -nesterov->learningRate;
179 } else if (projectedGradient > utility::zero<ConstantType>()) {
180 step = nesterov->learningRate;
181 } else {
183 }
184 } else {
185 step = nesterov->learningRate * projectedGradient;
186 }
187 step += nesterov->momentumTerm * nesterov->pastStep.at(steppingParameter);
188 nesterov->pastStep[steppingParameter] = step;
189 } else {
190 STORM_LOG_ERROR("GradientDescentType was not a known one");
191 }
192
194 const CoefficientType<FunctionType> newPos = position[steppingParameter] + convertedStep;
195 position[steppingParameter] = newPos;
196 // Map parameter back to region
198 auto const lower = region ? region->getLowerBoundary(steppingParameter) : utility::zero<CoefficientType<FunctionType>>() + precision;
199 auto const upper = region ? region->getUpperBoundary(steppingParameter) : utility::one<CoefficientType<FunctionType>>() - precision;
200
201 position[steppingParameter] = utility::max(lower, position[steppingParameter]);
202 position[steppingParameter] = utility::min(upper, position[steppingParameter]);
203 }
204 return utility::abs<ConstantType>(oldPosAsConstant - utility::convertNumber<ConstantType>(position[steppingParameter]));
205}
206
207template<typename FunctionType, typename ConstantType>
208ConstantType GradientDescentInstantiationSearcher<FunctionType, ConstantType>::stochasticGradientDescent(
210 uint_fast64_t initialStateModel = model.getStates("init").getNextSetIndex(0);
211
212 ConstantType currentValue;
213 switch (this->synthesisTask->getBound().comparisonType) {
216 currentValue = -utility::infinity<ConstantType>();
217 break;
220 currentValue = utility::infinity<ConstantType>();
221 break;
222 }
223
224 // We count the number of iterations where the value changes less than the threshold, and terminate if it is large enough.
225 uint64_t tinyChangeIterations = 0;
226
227 std::map<VariableType<FunctionType>, ConstantType> deltaVector;
228
229 std::vector<VariableType<FunctionType>> parameterEnumeration;
230 for (auto parameter : this->parameters) {
231 parameterEnumeration.push_back(parameter);
232 }
233
234 utility::Stopwatch printUpdateStopwatch;
235 printUpdateStopwatch.start();
236
237 // The index to keep track of what parameter(s) to consider next.
238 // The "mini-batch", so the parameters to consider, are parameterNum..parameterNum+miniBatchSize-1
239 uint_fast64_t parameterNum = 0;
240 for (uint_fast64_t stepNum = 0; true; ++stepNum) {
241 if (printUpdateStopwatch.getTimeInSeconds() >= 15) {
242 printUpdateStopwatch.restart();
243 STORM_LOG_PROGRESS("Currently at " << currentValue << "\n");
244 }
245
246 std::vector<VariableType<FunctionType>> miniBatch;
247 for (uint_fast64_t i = parameterNum; i < std::min((uint_fast64_t)parameterEnumeration.size(), parameterNum + miniBatchSize); i++) {
248 miniBatch.push_back(parameterEnumeration[i]);
249 }
250
251 ConstantType oldValue = currentValue;
254
255 // If nesterov is enabled, we need to compute the gradient on the predicted position
256 std::map<VariableType<FunctionType>, CoefficientType<FunctionType>> nesterovPredictedPosition(position);
257 if (Nesterov* nesterov = boost::get<Nesterov>(&gradientDescentType)) {
259 for (auto const& parameter : miniBatch) {
260 ConstantType const addedTerm = nesterov->momentumTerm * nesterov->pastStep[parameter];
261 nesterovPredictedPosition[parameter] += storm::utility::convertNumber<CoefficientType<FunctionType>>(addedTerm);
262 nesterovPredictedPosition[parameter] = utility::max(precision, nesterovPredictedPosition[parameter]);
263 nesterovPredictedPosition[parameter] = utility::min(upperBound, nesterovPredictedPosition[parameter]);
264 }
265 }
267 // Apply sigmoid function
268 for (auto const& parameter : parameters) {
269 nesterovPredictedPosition[parameter] =
272 utility::convertNumber<CoefficientType<FunctionType>>(std::exp(-utility::convertNumber<double>(nesterovPredictedPosition[parameter]))));
273 }
274 }
275
276 // Compute the value of our position and terminate if it satisfies the bound or is
277 // zero or one when computing probabilities. The "valueVector" (just the probability/expected
278 // reward for eventually reaching the target from every state) is also used for computing
279 // the gradient later. We only need one computation of the "valueVector" per mini-batch.
280 //
281 // If nesterov is activated, we need to do this twice. First, to check the value of the current position.
282 // Second, to compute the valueVector at the nesterovPredictedPosition.
283 // If nesterov is deactivated, then nesterovPredictedPosition == position.
284
285 // Are we at a stochastic (in bounds) position?
286 bool stochasticPosition = true;
287 for (auto const& parameter : parameters) {
288 if (nesterovPredictedPosition[parameter] < 0 + precision || nesterovPredictedPosition[parameter] > 1 - precision) {
289 stochasticPosition = false;
290 break;
291 }
292 }
293
294 bool computeValue = true;
296 if (!stochasticPosition) {
297 computeValue = false;
298 }
299 }
300
301 if (computeValue) {
302 std::unique_ptr<storm::modelchecker::CheckResult> intermediateResult = instantiationModelChecker->check(env, nesterovPredictedPosition);
303 std::vector<ConstantType> valueVector = intermediateResult->asExplicitQuantitativeCheckResult<ConstantType>().getValueVector();
304 if (boost::get<Nesterov>(&gradientDescentType)) {
305 std::map<VariableType<FunctionType>, CoefficientType<FunctionType>> modelCheckPosition(position);
307 for (auto const& parameter : parameters) {
308 modelCheckPosition[parameter] =
312 }
313 }
314 std::unique_ptr<storm::modelchecker::CheckResult> terminationResult = instantiationModelChecker->check(env, modelCheckPosition);
315 std::vector<ConstantType> terminationValueVector = terminationResult->asExplicitQuantitativeCheckResult<ConstantType>().getValueVector();
316 currentValue = terminationValueVector[initialStateModel];
317 } else {
318 currentValue = valueVector[initialStateModel];
319 }
320
321 if (synthesisTask->getBound().isSatisfied(currentValue) && stochasticPosition) {
322 break;
323 }
324
325 for (auto const& parameter : miniBatch) {
326 auto checkResult = derivativeEvaluationHelper->check(env, nesterovPredictedPosition, parameter, valueVector);
327 ConstantType delta = checkResult->getValueVector()[derivativeEvaluationHelper->getInitialState()];
328 if (synthesisTask->getBound().comparisonType == logic::ComparisonType::Less ||
329 synthesisTask->getBound().comparisonType == logic::ComparisonType::LessEqual) {
330 delta = -delta;
331 }
332 deltaVector[parameter] = delta;
333 }
334 } else {
335 if (synthesisTask->getBound().comparisonType == logic::ComparisonType::Less ||
336 synthesisTask->getBound().comparisonType == logic::ComparisonType::LessEqual) {
337 currentValue = utility::infinity<ConstantType>();
338 } else {
339 currentValue = -utility::infinity<ConstantType>();
340 }
341 }
342
343 // Log position and probability information for later use in visualizing the descent, if wished.
344 if (recordRun) {
345 VisualizationPoint point;
346 point.position = nesterovPredictedPosition;
347 point.value = currentValue;
348 walk.push_back(point);
349 }
350
351 // Perform the step. The actualChange is the change in position the step caused. This is different from the
352 // delta in multiple ways: First, it's multiplied with the learning rate and stuff. Second, if the current value
353 // is at epsilon, and the delta would step out of the constrained which is then corrected, the actualChange is the
354 // change from the last to the current corrected position (so might be zero while the delta is not).
355 for (auto const& parameter : miniBatch) {
356 doStep(parameter, position, deltaVector, stepNum);
357 }
358
359 if (storm::utility::abs<ConstantType>(oldValue - currentValue) < terminationEpsilon) {
360 tinyChangeIterations += miniBatch.size();
361 if (tinyChangeIterations > parameterEnumeration.size()) {
362 break;
363 }
364 } else {
365 tinyChangeIterations = 0;
366 }
367
368 // Consider the next parameter
369 parameterNum = parameterNum + miniBatchSize;
370 if (parameterNum >= parameterEnumeration.size()) {
371 parameterNum = 0;
372 }
373
375 STORM_LOG_WARN("Aborting Gradient Descent, returning non-optimal value.");
376 break;
377 }
378 }
379 return currentValue;
380}
381
382template<typename FunctionType, typename ConstantType>
383std::pair<std::map<VariableType<FunctionType>, CoefficientType<FunctionType>>, ConstantType>
385 STORM_LOG_ASSERT(this->synthesisTask, "Call setup before calling gradientDescent.");
386
387 resetDynamicValues();
388
389 STORM_LOG_ASSERT(this->synthesisTask->isBoundSet(), "Task does not involve a bound.");
390
391 std::map<VariableType<FunctionType>, CoefficientType<FunctionType>> bestInstantiation;
392 // No value has been found yet; the first one we see is the best one so far, whichever direction we optimize in.
393 std::optional<ConstantType> bestValue;
394
395 std::random_device device;
396 std::default_random_engine engine(device());
397 std::uniform_real_distribution<> dist(0, 1);
398 bool initialGuess = true;
399 std::map<VariableType<FunctionType>, CoefficientType<FunctionType>> point;
400 while (true) {
401 STORM_LOG_PROGRESS("Trying out a new starting point\n");
402 if (initialGuess) {
403 STORM_LOG_PROGRESS("Trying initial guess (p->0.5 for every parameter p or set start point)\n");
404 }
405 // Generate random starting point
406 for (auto const& param : this->parameters) {
407 if (initialGuess) {
408 logarithmicBarrierTerm = utility::convertNumber<ConstantType>(0.1);
409 if (startPoint) {
410 point[param] = (*startPoint)[param];
411 } else {
413 }
414 } else if (!initialGuess && constraintMethod == GradientDescentConstraintMethod::BARRIER_LOGARITHMIC &&
415 logarithmicBarrierTerm > utility::convertNumber<ConstantType>(0.00001)) {
416 // Do nothing
417 } else {
418 logarithmicBarrierTerm = utility::convertNumber<ConstantType>(0.1);
419 point[param] = utility::convertNumber<CoefficientType<FunctionType>>(dist(engine));
420 }
421 }
422 initialGuess = false;
423
424 /* walk.clear(); */
425
426 stochasticWatch.start();
427 STORM_LOG_PROGRESS("Starting at " << point << "\n");
428 ConstantType prob = stochasticGradientDescent(point);
429 stochasticWatch.stop();
430
431 bool isFoundPointBetter = !bestValue;
432 if (bestValue) {
433 switch (this->synthesisTask->getBound().comparisonType) {
436 isFoundPointBetter = prob > *bestValue;
437 break;
440 isFoundPointBetter = prob < *bestValue;
441 break;
442 }
443 }
444 if (isFoundPointBetter) {
445 bestInstantiation = point;
446 bestValue = prob;
447 }
448
449 if (synthesisTask->getBound().isSatisfied(*bestValue)) {
450 STORM_LOG_PROGRESS("Aborting because the bound is satisfied\n");
451 break;
453 break;
454 } else {
456 logarithmicBarrierTerm = logarithmicBarrierTerm / 10;
457 STORM_LOG_PROGRESS("Smaller term\n" << *bestValue << "\n" << logarithmicBarrierTerm << "\n");
458 continue;
459 }
460 STORM_LOG_PROGRESS("Sorry, couldn't satisfy the bound (yet). Best found value so far: " << *bestValue << "\n");
461 continue;
462 }
463 }
464
466 // Apply sigmoid function
467 for (auto const& parameter : parameters) {
468 bestInstantiation[parameter] =
472 }
473 }
474
475 STORM_LOG_ASSERT(bestValue.has_value(), "Expected at least one evaluated instantiation.");
476 return std::make_pair(bestInstantiation, *bestValue);
477}
478
479template<typename FunctionType, typename ConstantType>
480void GradientDescentInstantiationSearcher<FunctionType, ConstantType>::resetDynamicValues() {
481 if (Adam* adam = boost::get<Adam>(&gradientDescentType)) {
482 for (auto const& parameter : this->parameters) {
483 adam->decayingStepAverage[parameter] = utility::zero<ConstantType>();
484 adam->decayingStepAverageSquared[parameter] = utility::zero<ConstantType>();
485 }
486 } else if (RAdam* radam = boost::get<RAdam>(&gradientDescentType)) {
487 for (auto const& parameter : this->parameters) {
488 radam->decayingStepAverage[parameter] = utility::zero<ConstantType>();
489 radam->decayingStepAverageSquared[parameter] = utility::zero<ConstantType>();
490 }
491 } else if (RmsProp* rmsProp = boost::get<RmsProp>(&gradientDescentType)) {
492 for (auto const& parameter : this->parameters) {
493 rmsProp->rootMeanSquare[parameter] = utility::zero<ConstantType>();
494 }
495 } else if (Momentum* momentum = boost::get<Momentum>(&gradientDescentType)) {
496 for (auto const& parameter : this->parameters) {
497 momentum->pastStep[parameter] = utility::zero<ConstantType>();
498 }
499 } else if (Nesterov* nesterov = boost::get<Nesterov>(&gradientDescentType)) {
500 for (auto const& parameter : this->parameters) {
501 nesterov->pastStep[parameter] = utility::zero<ConstantType>();
502 }
503 }
504}
505
506template<typename FunctionType, typename ConstantType>
508 // This emits a JSON document to stdout for external data collection, not a log message.
509 std::cout << "[";
510 for (auto s = walk.begin(); s != walk.end(); ++s) {
511 std::cout << "{";
512 auto point = s->position;
513 for (auto iter = point.begin(); iter != point.end(); ++iter) {
514 std::cout << "\"" << iter->first.name() << "\"";
515 std::cout << ":" << utility::convertNumber<double>(iter->second) << ",";
516 }
517 std::cout << "\"value\":" << s->value << "}";
518 if (std::next(s) != walk.end()) {
519 std::cout << ",";
520 }
521 }
522 std::cout << "]\n";
523 // Print value at last step for data collection
524 std::cout << storm::utility::convertNumber<double>(walk.at(walk.size() - 1).value) << "\n";
525}
526
527template<typename FunctionType, typename ConstantType>
528std::vector<typename GradientDescentInstantiationSearcher<FunctionType, ConstantType>::VisualizationPoint>
532
535} // namespace derivative
536} // namespace storm
std::vector< VisualizationPoint > getVisualizationWalk()
Get the visualization walk that is recorded if recordRun is set to true in the constructor (false by ...
std::pair< std::map< typename utility::parametric::VariableType< FunctionType >::type, typename utility::parametric::CoefficientType< FunctionType >::type >, ConstantType > gradientDescent()
Perform Gradient Descent.
#define STORM_LOG_WARN(message)
Definition logging.h:28
#define STORM_LOG_PROGRESS(message)
Definition logging.h:42
#define STORM_LOG_ERROR(message)
Definition logging.h:29
#define STORM_LOG_ASSERT(cond, message)
Definition macros.h:9
typename utility::parametric::CoefficientType< FunctionType >::type CoefficientType
typename utility::parametric::VariableType< FunctionType >::type VariableType
SettingsType const & getModule()
Get module.
bool isTerminate()
Check whether the program should terminate (due to some abort signal).
ValueType max(ValueType const &first, ValueType const &second)
ValueType min(ValueType const &first, ValueType const &second)
ValueType abs(ValueType const &number)
ValueType zero()
Definition constants.cpp:24
ValueType infinity()
Definition constants.cpp:29
ValueType pow(ValueType const &value, int_fast64_t exponent)
ValueType one()
Definition constants.cpp:19
TargetType convertNumber(SourceType const &number)