23template<
typename FunctionType>
25template<
typename FunctionType>
28template<
typename FunctionType,
typename ConstantType>
32 const ConstantType precisionAsConstant =
33 utility::convertNumber<ConstantType>(storm::settings::getModule<storm::settings::modules::GeneralSettings>().getPrecision());
35 storm::utility::convertNumber<CoefficientType<FunctionType>>(storm::settings::getModule<storm::settings::modules::GeneralSettings>().getPrecision());
37 ConstantType
const oldPosAsConstant = utility::convertNumber<ConstantType>(position[steppingParameter]);
39 ConstantType projectedGradient;
42 ConstantType newPlainPosition = oldPosAsConstant + precisionAsConstant * gradient.at(steppingParameter);
44 region ? utility::convertNumber<ConstantType>(region->getLowerBoundary(steppingParameter)) : utility::zero<ConstantType>() + precisionAsConstant;
46 region ? utility::convertNumber<ConstantType>(region->getUpperBoundary(steppingParameter)) : utility::one<ConstantType>() - precisionAsConstant;
47 if (newPlainPosition < lower || newPlainPosition > upper) {
48 projectedGradient = 0;
50 projectedGradient = gradient.at(steppingParameter);
54 const double expX = std::exp(utility::convertNumber<double>(oldPos));
55 projectedGradient = gradient.at(steppingParameter) * utility::convertNumber<ConstantType>(expX / std::pow(expX + 1, 2));
57 if (oldPosAsConstant < precisionAsConstant) {
58 projectedGradient = 1000;
59 }
else if (oldPosAsConstant > utility::one<ConstantType>() - precisionAsConstant) {
60 projectedGradient = -1000;
62 projectedGradient = gradient.at(steppingParameter);
71 if (oldPosAsConstant >= precisionAsConstant && oldPosAsConstant <= utility::one<ConstantType>() - precisionAsConstant) {
73 if (oldPosAsConstant * 2 < utility::one<ConstantType>()) {
74 projectedGradient = gradient.at(steppingParameter) + logarithmicBarrierTerm / (oldPosAsConstant - precisionAsConstant);
77 gradient.at(steppingParameter) - logarithmicBarrierTerm / (utility::one<ConstantType>() - precisionAsConstant - oldPosAsConstant);
80 if (oldPosAsConstant < precisionAsConstant) {
81 projectedGradient = utility::one<ConstantType>() / logarithmicBarrierTerm;
82 }
else if (oldPosAsConstant > utility::one<ConstantType>() - precisionAsConstant) {
83 projectedGradient = -utility::one<ConstantType>() / logarithmicBarrierTerm;
87 projectedGradient = gradient.at(steppingParameter);
92 if (Adam* adam = boost::get<Adam>(&gradientDescentType)) {
95 adam->decayingStepAverage[steppingParameter] =
96 adam->averageDecay * adam->decayingStepAverage[steppingParameter] + (utility::one<ConstantType>() - adam->averageDecay) * projectedGradient;
97 adam->decayingStepAverageSquared[steppingParameter] = adam->squaredAverageDecay * adam->decayingStepAverageSquared[steppingParameter] +
98 (utility::one<ConstantType>() - adam->squaredAverageDecay) *
utility::pow(projectedGradient, 2);
100 const ConstantType correctedGradient =
101 adam->decayingStepAverage[steppingParameter] / (utility::one<ConstantType>() -
utility::pow(adam->averageDecay, stepNum + 1));
102 const ConstantType correctedSquaredGradient =
103 adam->decayingStepAverageSquared[steppingParameter] / (utility::one<ConstantType>() -
utility::pow(adam->squaredAverageDecay, stepNum + 1));
105 const ConstantType toSqrt = correctedSquaredGradient;
106 ConstantType sqrtResult = constantTypeSqrt(toSqrt);
108 step = (adam->learningRate / (sqrtResult + precisionAsConstant)) * correctedGradient;
109 }
else if (RAdam* radam = boost::get<RAdam>(&gradientDescentType)) {
114 const ConstantType maxLengthApproxSMA = 2 / (utility::one<ConstantType>() - radam->squaredAverageDecay) - utility::one<ConstantType>();
117 radam->decayingStepAverageSquared[steppingParameter] = radam->squaredAverageDecay * radam->decayingStepAverageSquared[steppingParameter] +
118 (utility::one<ConstantType>() - radam->squaredAverageDecay) *
utility::pow(projectedGradient, 2);
120 radam->decayingStepAverage[steppingParameter] =
121 radam->averageDecay * radam->decayingStepAverage[steppingParameter] + (utility::one<ConstantType>() - radam->averageDecay) * projectedGradient;
123 const ConstantType biasCorrectedMovingAverage =
124 radam->decayingStepAverage[steppingParameter] / (utility::one<ConstantType>() -
utility::pow(radam->averageDecay, stepNum + 1));
125 const ConstantType squaredAverageDecayPow =
utility::pow(radam->squaredAverageDecay, stepNum + 1);
127 const ConstantType lengthApproxSMA =
129 ((2 * (utility::convertNumber<ConstantType>(stepNum) + utility::one<ConstantType>()) * squaredAverageDecayPow) / (1 - squaredAverageDecayPow));
131 if (lengthApproxSMA > 4) {
133 const ConstantType adaptiveLearningRate =
134 constantTypeSqrt((utility::one<ConstantType>() - squaredAverageDecayPow) / radam->decayingStepAverageSquared[steppingParameter]);
136 const ConstantType varianceRectification =
137 constantTypeSqrt(((lengthApproxSMA - 4) / (maxLengthApproxSMA - 4)) * ((lengthApproxSMA - 2) / (maxLengthApproxSMA - 2)) *
138 ((maxLengthApproxSMA) / (lengthApproxSMA)));
140 step = radam->learningRate * varianceRectification * biasCorrectedMovingAverage * adaptiveLearningRate;
143 step = radam->learningRate * biasCorrectedMovingAverage;
145 }
else if (RmsProp* rmsProp = boost::get<RmsProp>(&gradientDescentType)) {
146 rmsProp->rootMeanSquare[steppingParameter] = rmsProp->averageDecay * rmsProp->rootMeanSquare[steppingParameter] +
147 (utility::one<ConstantType>() - rmsProp->averageDecay) * projectedGradient * projectedGradient;
149 const ConstantType toSqrt = rmsProp->rootMeanSquare[steppingParameter] + precisionAsConstant;
150 ConstantType sqrtResult = constantTypeSqrt(toSqrt);
152 step = (rmsProp->learningRate / sqrtResult) * projectedGradient;
153 }
else if (Plain* plain = boost::get<Plain>(&gradientDescentType)) {
155 if (projectedGradient < utility::zero<ConstantType>()) {
156 step = -plain->learningRate;
157 }
else if (projectedGradient > utility::zero<ConstantType>()) {
158 step = plain->learningRate;
160 step = utility::zero<ConstantType>();
163 step = plain->learningRate * projectedGradient;
165 }
else if (Momentum* momentum = boost::get<Momentum>(&gradientDescentType)) {
167 if (projectedGradient < utility::zero<ConstantType>()) {
168 step = -momentum->learningRate;
169 }
else if (projectedGradient > utility::zero<ConstantType>()) {
170 step = momentum->learningRate;
172 step = utility::zero<ConstantType>();
175 step = momentum->learningRate * projectedGradient;
177 step += momentum->momentumTerm * momentum->pastStep.at(steppingParameter);
178 momentum->pastStep[steppingParameter] = step;
179 }
else if (Nesterov* nesterov = boost::get<Nesterov>(&gradientDescentType)) {
181 if (projectedGradient < utility::zero<ConstantType>()) {
182 step = -nesterov->learningRate;
183 }
else if (projectedGradient > utility::zero<ConstantType>()) {
184 step = nesterov->learningRate;
186 step = utility::zero<ConstantType>();
189 step = nesterov->learningRate * projectedGradient;
191 step += nesterov->momentumTerm * nesterov->pastStep.at(steppingParameter);
192 nesterov->pastStep[steppingParameter] = step;
199 position[steppingParameter] = newPos;
202 auto const lower = region ? region->getLowerBoundary(steppingParameter) : utility::zero<CoefficientType<FunctionType>>() + precision;
203 auto const upper = region ? region->getUpperBoundary(steppingParameter) : utility::one<CoefficientType<FunctionType>>() - precision;
205 position[steppingParameter] =
utility::max(lower, position[steppingParameter]);
206 position[steppingParameter] =
utility::min(upper, position[steppingParameter]);
208 return utility::abs<ConstantType>(oldPosAsConstant - utility::convertNumber<ConstantType>(position[steppingParameter]));
211template<
typename FunctionType,
typename ConstantType>
212ConstantType GradientDescentInstantiationSearcher<FunctionType, ConstantType>::stochasticGradientDescent(
213 std::map<VariableType<FunctionType>, CoefficientType<FunctionType>>& position) {
214 uint_fast64_t initialStateModel = model.getStates(
"init").getNextSetIndex(0);
216 ConstantType currentValue;
217 switch (this->synthesisTask->getBound().comparisonType) {
220 currentValue = -utility::infinity<ConstantType>();
224 currentValue = utility::infinity<ConstantType>();
229 uint64_t tinyChangeIterations = 0;
231 std::map<VariableType<FunctionType>, ConstantType> deltaVector;
233 std::vector<VariableType<FunctionType>> parameterEnumeration;
234 for (
auto parameter : this->parameters) {
235 parameterEnumeration.push_back(parameter);
238 utility::Stopwatch printUpdateStopwatch;
239 printUpdateStopwatch.start();
243 uint_fast64_t parameterNum = 0;
244 for (uint_fast64_t stepNum = 0;
true; ++stepNum) {
245 if (printUpdateStopwatch.getTimeInSeconds() >= 15) {
246 printUpdateStopwatch.restart();
250 std::vector<VariableType<FunctionType>> miniBatch;
251 for (uint_fast64_t i = parameterNum;
i < std::min((uint_fast64_t)parameterEnumeration.size(), parameterNum + miniBatchSize);
i++) {
252 miniBatch.push_back(parameterEnumeration[i]);
255 ConstantType oldValue = currentValue;
256 CoefficientType<FunctionType>
const precision = storm::utility::convertNumber<CoefficientType<FunctionType>>(
257 storm::settings::getModule<storm::settings::modules::GeneralSettings>().getPrecision());
260 std::map<VariableType<FunctionType>, CoefficientType<FunctionType>> nesterovPredictedPosition(position);
261 if (Nesterov* nesterov = boost::get<Nesterov>(&gradientDescentType)) {
262 CoefficientType<FunctionType>
const upperBound = (utility::one<CoefficientType<FunctionType>>() - precision);
263 for (
auto const& parameter : miniBatch) {
264 ConstantType
const addedTerm = nesterov->momentumTerm * nesterov->pastStep[parameter];
265 nesterovPredictedPosition[parameter] += storm::utility::convertNumber<CoefficientType<FunctionType>>(addedTerm);
266 nesterovPredictedPosition[parameter] =
utility::max(precision, nesterovPredictedPosition[parameter]);
267 nesterovPredictedPosition[parameter] =
utility::min(upperBound, nesterovPredictedPosition[parameter]);
272 for (
auto const& parameter : parameters) {
273 nesterovPredictedPosition[parameter] =
274 utility::one<CoefficientType<FunctionType>>() /
276 utility::convertNumber<CoefficientType<FunctionType>>(std::exp(-utility::convertNumber<double>(nesterovPredictedPosition[parameter]))));
290 bool stochasticPosition =
true;
291 for (
auto const& parameter : parameters) {
292 if (nesterovPredictedPosition[parameter] < 0 + precision || nesterovPredictedPosition[parameter] > 1 - precision) {
293 stochasticPosition =
false;
298 bool computeValue =
true;
300 if (!stochasticPosition) {
301 computeValue =
false;
306 std::unique_ptr<storm::modelchecker::CheckResult> intermediateResult = instantiationModelChecker->check(env, nesterovPredictedPosition);
307 std::vector<ConstantType> valueVector = intermediateResult->asExplicitQuantitativeCheckResult<ConstantType>().getValueVector();
308 if (Nesterov* nesterov = boost::get<Nesterov>(&gradientDescentType)) {
309 std::map<VariableType<FunctionType>, CoefficientType<FunctionType>> modelCheckPosition(position);
311 for (
auto const& parameter : parameters) {
312 modelCheckPosition[parameter] =
313 utility::one<CoefficientType<FunctionType>>() /
315 utility::convertNumber<CoefficientType<FunctionType>>(std::exp(-utility::convertNumber<double>(modelCheckPosition[parameter]))));
318 std::unique_ptr<storm::modelchecker::CheckResult> terminationResult = instantiationModelChecker->check(env, modelCheckPosition);
319 std::vector<ConstantType> terminationValueVector = terminationResult->asExplicitQuantitativeCheckResult<ConstantType>().getValueVector();
320 currentValue = terminationValueVector[initialStateModel];
322 currentValue = valueVector[initialStateModel];
325 if (synthesisTask->getBound().isSatisfied(currentValue) && stochasticPosition) {
329 for (
auto const& parameter : miniBatch) {
330 auto checkResult = derivativeEvaluationHelper->check(env, nesterovPredictedPosition, parameter, valueVector);
331 ConstantType delta = checkResult->getValueVector()[derivativeEvaluationHelper->getInitialState()];
336 deltaVector[parameter] = delta;
341 currentValue = utility::infinity<ConstantType>();
343 currentValue = -utility::infinity<ConstantType>();
349 VisualizationPoint point;
350 point.position = nesterovPredictedPosition;
351 point.value = currentValue;
352 walk.push_back(point);
359 for (
auto const& parameter : miniBatch) {
360 doStep(parameter, position, deltaVector, stepNum);
363 if (storm::utility::abs<ConstantType>(oldValue - currentValue) < terminationEpsilon) {
364 tinyChangeIterations += miniBatch.size();
365 if (tinyChangeIterations > parameterEnumeration.size()) {
369 tinyChangeIterations = 0;
373 parameterNum = parameterNum + miniBatchSize;
374 if (parameterNum >= parameterEnumeration.size()) {
379 STORM_LOG_WARN(
"Aborting Gradient Descent, returning non-optimal value.");
386template<
typename FunctionType,
typename ConstantType>
387std::pair<std::map<VariableType<FunctionType>, CoefficientType<FunctionType>>, ConstantType>
389 STORM_LOG_ASSERT(this->synthesisTask,
"Call setup before calling gradientDescent");
391 resetDynamicValues();
393 STORM_LOG_ASSERT(this->synthesisTask->isBoundSet(),
"Task does not involve a bound.");
396 ConstantType bestValue;
397 switch (this->synthesisTask->getBound().comparisonType) {
400 bestValue = -utility::infinity<ConstantType>();
404 bestValue = utility::infinity<ConstantType>();
408 std::random_device device;
409 std::default_random_engine engine(device());
410 std::uniform_real_distribution<> dist(0, 1);
411 bool initialGuess =
true;
416 STORM_PRINT_AND_LOG(
"Trying initial guess (p->0.5 for every parameter p or set start point)\n");
419 for (
auto const& param : this->parameters) {
421 logarithmicBarrierTerm = utility::convertNumber<ConstantType>(0.1);
423 point[param] = (*startPoint)[param];
425 point[param] = utility::convertNumber<CoefficientType<FunctionType>>(0.5 + 1e-6);
428 logarithmicBarrierTerm > utility::convertNumber<ConstantType>(0.00001)) {
431 logarithmicBarrierTerm = utility::convertNumber<ConstantType>(0.1);
432 point[param] = utility::convertNumber<CoefficientType<FunctionType>>(dist(engine));
435 initialGuess =
false;
439 stochasticWatch.start();
441 ConstantType prob = stochasticGradientDescent(point);
442 stochasticWatch.stop();
444 bool isFoundPointBetter =
false;
445 switch (this->synthesisTask->getBound().comparisonType) {
448 isFoundPointBetter = prob > bestValue;
452 isFoundPointBetter = prob < bestValue;
455 if (isFoundPointBetter) {
456 bestInstantiation = point;
460 if (synthesisTask->getBound().isSatisfied(bestValue)) {
467 logarithmicBarrierTerm = logarithmicBarrierTerm / 10;
468 STORM_PRINT_AND_LOG(
"Smaller term\n" << bestValue <<
"\n" << logarithmicBarrierTerm <<
"\n");
471 STORM_PRINT_AND_LOG(
"Sorry, couldn't satisfy the bound (yet). Best found value so far: " << bestValue <<
"\n");
478 for (
auto const& parameter : parameters) {
479 bestInstantiation[parameter] =
480 utility::one<CoefficientType<FunctionType>>() /
482 utility::convertNumber<CoefficientType<FunctionType>>(std::exp(-utility::convertNumber<double>(bestInstantiation[parameter]))));
486 return std::make_pair(bestInstantiation, bestValue);
489template<
typename FunctionType,
typename ConstantType>
491 if (Adam* adam = boost::get<Adam>(&gradientDescentType)) {
492 for (
auto const& parameter : this->parameters) {
493 adam->decayingStepAverage[parameter] = utility::zero<ConstantType>();
494 adam->decayingStepAverageSquared[parameter] = utility::zero<ConstantType>();
496 }
else if (RAdam* radam = boost::get<RAdam>(&gradientDescentType)) {
497 for (
auto const& parameter : this->parameters) {
498 radam->decayingStepAverage[parameter] = utility::zero<ConstantType>();
499 radam->decayingStepAverageSquared[parameter] = utility::zero<ConstantType>();
501 }
else if (RmsProp* rmsProp = boost::get<RmsProp>(&gradientDescentType)) {
502 for (
auto const& parameter : this->parameters) {
503 rmsProp->rootMeanSquare[parameter] = utility::zero<ConstantType>();
505 }
else if (Momentum* momentum = boost::get<Momentum>(&gradientDescentType)) {
506 for (
auto const& parameter : this->parameters) {
507 momentum->pastStep[parameter] = utility::zero<ConstantType>();
509 }
else if (Nesterov* nesterov = boost::get<Nesterov>(&gradientDescentType)) {
510 for (
auto const& parameter : this->parameters) {
511 nesterov->pastStep[parameter] = utility::zero<ConstantType>();
516template<
typename FunctionType,
typename ConstantType>
519 for (
auto s = walk.begin(); s != walk.end(); ++s) {
521 auto point = s->position;
522 for (
auto iter = point.begin(); iter != point.end(); ++iter) {
524 STORM_PRINT(
":" << utility::convertNumber<double>(iter->second) <<
",");
527 if (std::next(s) != walk.end()) {
533 STORM_PRINT(storm::utility::convertNumber<double>(walk.at(walk.size() - 1).value) <<
"\n");
536template<
typename FunctionType,
typename ConstantType>
537std::vector<typename GradientDescentInstantiationSearcher<FunctionType, ConstantType>::VisualizationPoint>
void printRunAsJson()
Print the previously done run as JSON.
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)
#define STORM_LOG_ERROR(message)
#define STORM_LOG_ASSERT(cond, message)
#define STORM_PRINT_AND_LOG(message)
#define STORM_PRINT(message)
Define the macros that print information and optionally also log it.
typename utility::parametric::VariableType< FunctionType >::type VariableType
typename utility::parametric::CoefficientType< FunctionType >::type CoefficientType
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 pow(ValueType const &value, int_fast64_t exponent)