Files
ensmallen/tests/callbacks_test.cpp
T
Barak A. Pearlmutter 176fbef576 32-bit safety in big random number
This patch fixes a compile-time warning and test case failure on
32-bit architectures, including i386.

    cd /<<PKGBUILDDIR>>/obj-i686-linux-gnu/tests && /usr/bin/c++   -I/<<PKGBUILDDIR>>/include  -g -O2 -fdebug-prefix-map=/<<PKGBUILDDIR>>=. -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fopenmp -Wall -Wpedantic -Wunused-parameter   -std=gnu++11 -o CMakeFiles/ensmallen_tests.dir/cmaes_test.cpp.o -c /<<PKGBUILDDIR>>/tests/cmaes_test.cpp
    /<<PKGBUILDDIR>>/tests/callbacks_test.cpp: In function ‘void ____C_A_T_C_H____T_E_S_T____46()’:
    /<<PKGBUILDDIR>>/tests/callbacks_test.cpp:397:28: warning: unsigned conversion from ‘long long int’ to ‘size_t’ {aka ‘unsigned int’} changes value from ‘10000000000’ to ‘1410065408’ [-Woverflow]
      397 |   StandardSGD s(0.0003, 1, 10000000000, -10);
	  |                            ^~~~~~~~~~~
    /<<PKGBUILDDIR>>/tests/callbacks_test.cpp: In function ‘void ____C_A_T_C_H____T_E_S_T____54()’:
    /<<PKGBUILDDIR>>/tests/callbacks_test.cpp:473:28: warning: unsigned conversion from ‘long long int’ to ‘size_t’ {aka ‘unsigned int’} changes value from ‘10000000000’ to ‘1410065408’ [-Woverflow]
      473 |   StandardSGD s(0.0003, 1, 10000000000, -100, true);
	  |                            ^~~~~~~~~~~

    ...

    Test project /<<PKGBUILDDIR>>/obj-i686-linux-gnu
	Start 1: ensmallen_tests
    1/1 Test #1: ensmallen_tests ..................***Failed  5163.40 sec

The SGD maxIterations parameter to the SGD routine is stored in a
size_t. Arguably inappropriate; it belongs in something guaranteed to
be at least 64 bits, perhaps long long int.

Be that as it may, a test routine was passing a number meant to be so
large it would not be exceeded, to test a mechanism that should
terminate the optimization early. The large number chosen was
10000000000, ten billion, which exceeds the maximum value of an
unsigned 32-bit integer of 4 billion plus change.

This patch changes the maxIterations parameter passed to two billion,
2000000000, which is both sufficiently large for the purposes here and
safely below the maximum value for a 32-bit signed integer.
2019-12-05 14:30:42 +00:00

484 lines
15 KiB
C++

/**
* @file callbacks_test.cpp
* @author Marcus Edel
*
* ensmallen is free software; you may redistribute it and/or modify it under
* the terms of the 3-clause BSD license. You should have received a copy of
* the 3-clause BSD license along with ensmallen. If not, see
* http://www.opensource.org/licenses/BSD-3-Clause for more information.
*/
#include <ensmallen.hpp>
#include "catch.hpp"
#include "test_function_tools.hpp"
using namespace ens;
using namespace ens::test;
using namespace ens::callbacks::traits;
/**
* Utility class with Evaluate(), Gradient(), BeginEpoch(), EndEpoch(),
* BeginOptimization(), EndOptimization(), EvaluateConstraint(),
* GradientConstraint(), StepTaken.
*/
class CompleteCallbackTestFunction
{
public:
CompleteCallbackTestFunction() :
calledEvaluate(false),
calledGradient(false),
calledBeginEpoch(false),
calledEndEpoch(false),
calledBeginOptimization(false),
calledEndOptimization(false),
calledEvaluateConstraint(false),
calledGradientConstraint(false),
calledStepTaken(false)
{ }
template<typename OptimizerType, typename FunctionType, typename MatType>
void Evaluate(OptimizerType& /* optimizer */,
FunctionType& /* function */,
const MatType& /* coordinates */,
const double /* objective */)
{ calledEvaluate = true; }
template<typename OptimizerType,
typename FunctionType,
typename MatType,
typename GradType>
void Gradient(OptimizerType& /* optimizer */,
FunctionType& /* function */,
const MatType& /* coordinates */,
GradType& /* objective */)
{ calledGradient = true; }
template<typename OptimizerType, typename FunctionType, typename MatType>
void BeginEpoch(OptimizerType& /* optimizer */,
FunctionType& /* function */,
const MatType& /* coordinates */,
const size_t /* epoch */,
const double /* objective */)
{ calledBeginEpoch = true; }
template<typename OptimizerType, typename FunctionType, typename MatType>
void EndEpoch(OptimizerType& /* optimizer */,
FunctionType& /* function */,
const MatType& /* coordinates */,
const size_t /* epoch */,
const double /* objective */)
{ calledEndEpoch = true; }
template<typename OptimizerType, typename FunctionType, typename MatType>
void BeginOptimization(OptimizerType& /* optimizer */,
FunctionType& /* function */,
MatType& /* coordinates */)
{ calledBeginOptimization = true; }
template<typename OptimizerType, typename FunctionType, typename MatType>
void EndOptimization(OptimizerType& /* optimizer */,
FunctionType& /* function */,
MatType& /* coordinates */)
{ calledEndOptimization = true; }
template<typename OptimizerType, typename FunctionType, typename MatType>
void EvaluateConstraint(OptimizerType& /* optimizer */,
FunctionType& /* function */,
const MatType& /* coordinates */,
const size_t /* constraint */,
const double /* constraintValue */)
{ calledEvaluateConstraint = true; }
template<typename OptimizerType,
typename FunctionType,
typename MatType,
typename GradType>
void GradientConstraint(OptimizerType& /* optimizer */,
FunctionType& /* function */,
const MatType& /* coordinates */,
const size_t /* constraint */,
GradType& /* gradient */)
{ calledGradientConstraint = true; }
template<typename OptimizerType, typename FunctionType, typename MatType>
void StepTaken(OptimizerType& /* optimizer */,
FunctionType& /* function */,
MatType& /* coordinates */)
{ calledStepTaken = true; }
bool calledEvaluate;
bool calledGradient;
bool calledBeginEpoch;
bool calledEndEpoch;
bool calledBeginOptimization;
bool calledEndOptimization;
bool calledEvaluateConstraint;
bool calledGradientConstraint;
bool calledStepTaken;
};
template<typename OptimizerType>
void CallbacksFullFunctionTest(OptimizerType& optimizer,
bool calledEvaluate,
bool calledGradient,
bool calledBeginEpoch,
bool calledEndEpoch,
bool calledBeginOptimization,
bool calledEndOptimization,
bool calledEvaluateConstraint,
bool calledGradientConstraint,
bool calledStepTaken)
{
arma::mat data, testData, shuffledData;
arma::Row<size_t> responses, testResponses, shuffledResponses;
LogisticRegressionTestData(data, testData, shuffledData,
responses, testResponses, shuffledResponses);
LogisticRegression<> lr(shuffledData, shuffledResponses, 0.5);
CompleteCallbackTestFunction cb;
arma::mat coordinates = lr.GetInitialPoint();
optimizer.Optimize(lr, coordinates, cb);
REQUIRE(cb.calledEvaluate == calledEvaluate);
REQUIRE(cb.calledGradient == calledGradient);
REQUIRE(cb.calledBeginEpoch == calledBeginEpoch);
REQUIRE(cb.calledEndEpoch == calledEndEpoch);
REQUIRE(cb.calledBeginOptimization == calledBeginOptimization);
REQUIRE(cb.calledEndOptimization == calledEndOptimization);
REQUIRE(cb.calledEvaluateConstraint == calledEvaluateConstraint);
REQUIRE(cb.calledGradientConstraint == calledGradientConstraint);
REQUIRE(cb.calledStepTaken == calledStepTaken);
}
/**
* Make sure we invoke all callbacks (AdaDelta).
*/
TEST_CASE("AdaDeltaCallbacksFullFunctionTest", "[CallbacksTest]")
{
AdaDelta optimizer(1.0, 1, 0.99, 1e-8, 2000, 1e-9, true);
CallbacksFullFunctionTest(optimizer, true, true, true, true, true, true,
false, false, true);
}
/**
* Make sure we invoke all callbacks (AdaGrad).
*/
TEST_CASE("AdaGradCallbacksFullFunctionTest", "[CallbacksTest]")
{
AdaGrad optimizer(0.99, 1, 1e-8, 2000, 1e-9, true);
CallbacksFullFunctionTest(optimizer, true, true, true, true, true, true,
false, false, true);
}
/**
* Make sure we invoke all callbacks (Adam).
*/
TEST_CASE("AdamCallbacksFullFunctionTest", "[CallbacksTest]")
{
Adam optimizer(0.5, 2, 0.7, 0.999, 1e-8, 2000, 1e-3, false);
CallbacksFullFunctionTest(optimizer, true, true, true, true, true, true,
false, false, true);
}
/**
* Make sure we invoke all callbacks (BigBatchSGD).
*/
TEST_CASE("BigBatchSGDCallbacksFullFunctionTest", "[CallbacksTest]")
{
BBS_BB optimizer(1, 0.01, 0.1, 2000, 1e-4);
CallbacksFullFunctionTest(optimizer, true, true, true, true, true, true,
false, false, true);
}
/**
* Make sure we invoke all callbacks (CMAES).
*/
TEST_CASE("CMAESCallbacksFullFunctionTest", "[CallbacksTest]")
{
CMAES<> optimizer(0, -1, 1, 32, 3, 1e-3);
CallbacksFullFunctionTest(optimizer, true, false, false, false, true, true,
false, false, true);
}
/**
* Make sure we invoke all callbacks (CNE).
*/
TEST_CASE("CNECallbacksFullFunctionTest", "[CallbacksTest]")
{
CNE optimizer(200, 6, 0.2, 0.2, 0.2, 1e-5);
CallbacksFullFunctionTest(optimizer, true, false, false, false, true, true,
false, false, true);
}
/**
* Make sure we invoke all callbacks (DE).
*/
TEST_CASE("DECallbacksFullFunctionTest", "[CallbacksTest]")
{
DE optimizer(200, 6, 0.6, 0.8, 1e-5);
CallbacksFullFunctionTest(optimizer, true, false, false, false, true, true,
false, false, true);
}
/**
* Make sure we invoke all callbacks (Eve).
*/
TEST_CASE("EveCallbacksFullFunctionTest", "[CallbacksTest]")
{
Eve optimizer(1e-3, 1, 0.9, 0.999, 0.999, 1e-8, 10000, 2000, 1e-9, true);
CallbacksFullFunctionTest(optimizer, true, true, true, true, true, true,
false, false, true);
}
/**
* Make sure we invoke all callbacks (FTML).
*/
TEST_CASE("FTMLCallbacksFullFunctionTest", "[CallbacksTest]")
{
FTML optimizer(0.001, 1, 0.9, 0.999, 1e-8, 2000, 1e-5, true);
CallbacksFullFunctionTest(optimizer, true, true, true, true, true, true,
false, false, true);
}
/**
* Make sure we invoke all callbacks (GradientDescent).
*/
TEST_CASE("GradientDescentCallbacksFullFunctionTest", "[CallbacksTest]")
{
GradientDescent optimizer(0.001, 3, 1e-15);
CallbacksFullFunctionTest(optimizer, true, true, false, false, true, true,
false, false, true);
}
/**
* Make sure we invoke all callbacks (IQN).
*/
TEST_CASE("IQNCallbacksFullFunctionTest", "[CallbacksTest]")
{
IQN optimizer(0.01, 1, 3, 1e-3);
CallbacksFullFunctionTest(optimizer, true, true, false, false, true, true,
false, false, true);
}
/**
* Make sure we invoke all callbacks (Katyusha).
*/
TEST_CASE("KatyushaCallbacksFullFunctionTest", "[CallbacksTest]")
{
Katyusha optimizer(1.0, 10.0, 1, 3, 0, 1e-10, true);
CallbacksFullFunctionTest(optimizer, true, true, false, false, true, true,
false, false, true);
}
/**
* Make sure we invoke all callbacks (SARAH).
*/
TEST_CASE("SARAHCallbacksFullFunctionTest", "[CallbacksTest]")
{
SARAH optimizer(0.01, 2, 3, 0, 1e-5, true);
CallbacksFullFunctionTest(optimizer, true, true, false, false, true, true,
false, false, true);
}
/**
* Make sure we invoke all callbacks (SCD).
*/
TEST_CASE("SCDCallbacksFullFunctionTest", "[CallbacksTest]")
{
SCD<> optimizer(0.4, 4);
CallbacksFullFunctionTest(optimizer, true, true, false, false, true, true,
false, false, true);
}
/**
* Make sure we invoke all callbacks (SGD).
*/
TEST_CASE("SGDCallbacksFullFunctionTest", "[CallbacksTest]")
{
StandardSGD optimizer(0.0003, 1, 2000, 1e-9, true);
CallbacksFullFunctionTest(optimizer, true, true, true, true, true, true,
false, false, true);
}
/**
* Make sure we invoke all callbacks (SGDR).
*/
TEST_CASE("SGDRCallbacksFullFunctionTest", "[CallbacksTest]")
{
SGDR<> optimizer(50, 2.0, 1, 0.01, 2000, 1e-3);
CallbacksFullFunctionTest(optimizer, true, true, true, true, true, true,
false, false, true);
}
/**
* Make sure we invoke all callbacks (SPALeRASGD).
*/
TEST_CASE("SPALeRASGDCallbacksFullFunctionTest", "[CallbacksTest]")
{
SPALeRASGD<> optimizer(0.05, 30, 2000, 1e-4);
CallbacksFullFunctionTest(optimizer, true, true, true, true, true, true,
false, false, true);
}
/**
* Make sure we invoke all callbacks (SPSA).
*/
TEST_CASE("SPSACallbacksFullFunctionTest", "[CallbacksTest]")
{
SPSA optimizer(0.1, 0.102, 0.16, 0.3, 10, 0);
CallbacksFullFunctionTest(optimizer, true, false, false, false, true, true,
false, false, true);
}
/**
* Make sure we invoke all callbacks (SVRG).
*/
TEST_CASE("SVRGCallbacksFullFunctionTest", "[CallbacksTest]")
{
SVRG optimizer(0.005, 2, 4, 0, 1e-5, true);
CallbacksFullFunctionTest(optimizer, true, true, false, false, true, true,
false, false, true);
}
/**
* Make sure we invoke all callbacks (ParallelSGD).
*/
TEST_CASE("ParallelSGDCallbacksFullFunctionTest", "[CallbacksTest]")
{
ConstantStep decayPolicy(0.4);
ParallelSGD<ConstantStep> optimizer(4, 2, 1e-5, true, decayPolicy);
CallbacksFullFunctionTest(optimizer, true, true, false, false, true, true,
false, false, true);
}
/**
* Make sure we invoke all callbacks (LBestPSO).
*/
TEST_CASE("LBestPSOCallbacksFullFunctionTest", "[CallbacksTest]")
{
LBestPSO optimizer;
CallbacksFullFunctionTest(optimizer, true, false, false, false, true, true,
false, false, true);
}
/**
* Make sure we invoke all callbacks (L_BFGS).
*/
TEST_CASE("L_BFGSCallbacksFullFunctionTest", "[CallbacksTest]")
{
L_BFGS optimizer(10, 4);
CallbacksFullFunctionTest(optimizer, true, true, false, false, true, true,
false, false, true);
}
/**
* Make sure we invoke all callbacks (SA).
*/
TEST_CASE("SACallbacksFullFunctionTest", "[CallbacksTest]")
{
ExponentialSchedule schedule;
SA<> optimizer(schedule, 10, 1000., 1000, 100, 1e-11, 3, 1.5, 0.3, 0.3);
CallbacksFullFunctionTest(optimizer, true, false, false, false, true, true,
false, false, true);
}
/**
* Make sure the EarlyStopAtMinLoss callback will stop the optimization process.
*/
TEST_CASE("EarlyStopAtMinLossCallbackTest", "[CallbacksTest]")
{
SGDTestFunction f;
arma::mat coordinates = f.GetInitialPoint();
// Instantiate the optimizer with a number of iterations that will take a
// long time to finish.
StandardSGD s(0.0003, 1, 2000000000, -10);
s.ExactObjective() = true;
// The optimization process should return in one second.
const double result = s.Optimize(f, coordinates, EarlyStopAtMinLoss(100));
REQUIRE(result == Approx(-1.0).epsilon(0.0005));
REQUIRE(coordinates(0) == Approx(0.0).margin(1e-3));
REQUIRE(coordinates(1) == Approx(0.0).margin(1e-7));
REQUIRE(coordinates(2) == Approx(0.0).margin(1e-7));
}
/**
* Make sure the PrintLoss callback will print the loss to the specified
* output stream.
*/
TEST_CASE("PrintLossCallbackTest", "[CallbacksTest]")
{
SGDTestFunction f;
arma::mat coordinates = f.GetInitialPoint();
StandardSGD s(0.0003, 1, 10, 1e-9, true);
std::stringstream stream;
s.Optimize(f, coordinates, PrintLoss(stream));
REQUIRE(stream.str().length() > 0);
}
/**
* Make sure the ProgressBar callback will show the progress on the specified
* output stream.
*/
TEST_CASE("ProgressBarCallbackTest", "[CallbacksTest]")
{
SGDTestFunction f;
arma::mat coordinates = f.GetInitialPoint();
StandardSGD s(0.0003, 1, 10, 1e-9, true);
std::stringstream stream;
s.Optimize(f, coordinates, ProgressBar(10, stream));
REQUIRE(stream.str().length() > 0);
}
/**
* Make sure the StoreBestCoordinates callback will store the best coordinates
* and objective.
*/
TEST_CASE("StoreBestCoordinatesCallbackTest", "[CallbacksTest]")
{
SGDTestFunction f;
arma::mat coordinates = f.GetInitialPoint();
StandardSGD s(0.0003, 1, 5000000, 1e-9, true);
StoreBestCoordinates<decltype(coordinates)> cb;
const double result = s.Optimize(f, coordinates, cb);
REQUIRE(cb.BestObjective() <= result);
REQUIRE(cb.BestObjective() == Approx(-1.0).epsilon(0.0005));
REQUIRE(cb.BestCoordinates()(0) == Approx(0.0).margin(1e-3));
REQUIRE(cb.BestCoordinates()(1) == Approx(0.0).margin(1e-7));
}
/**
* Make sure the TimerStop callback will stop the optimization process.
*/
TEST_CASE("TimerStopCallbackTest", "[CallbacksTest]")
{
SGDTestFunction f;
arma::mat coordinates = f.GetInitialPoint();
// Instantiate the optimizer with a number of iterations that will take a
// long time to finish.
StandardSGD s(0.0003, 1, 2000000000, -100, true);
arma::wall_clock timer;
timer.tic();
// The optimization process should return in one second.
s.Optimize(f, coordinates, TimerStop(0.5));
// Add some time to account for the function to return.
REQUIRE(timer.toc() < 2);
}