diff --git a/.appveyor.yml b/.appveyor.yml index 3d157b9ebf..dae15df614 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -30,10 +30,10 @@ build_script: - if not exist armadillo.tar.xz appveyor DownloadFile "http://sourceforge.net/projects/arma/files/armadillo-7.800.2.tar.xz" -FileName armadillo.tar.xz - 7z x armadillo.tar.xz -so | 7z x -si -ttar > nul - cd armadillo-7.800.2 && mkdir build && cd build - - cmake -G "Visual Studio 14 2015 Win64" -DBLAS_LIBRARY:FILEPATH="%APPVEYOR_BUILD_FOLDER%/OpenBLAS.0.2.14.1/lib/native/lib/x64/libopenblas.dll.a" -DLAPACK_LIBRARY:FILEPATH="%APPVEYOR_BUILD_FOLDER%/OpenBLAS.0.2.14.1/lib/native/lib/x64/libopenblas.dll.a" -DCMAKE_PREFIX:FILEPATH="%APPVEYOR_BUILD_FOLDER%/armadillo" -DBUILD_SHARED_LIBS=OFF .. + - cmake -G "Visual Studio 14 2015 Win64" -DBLAS_LIBRARY:FILEPATH="%APPVEYOR_BUILD_FOLDER%/OpenBLAS.0.2.14.1/lib/native/lib/x64/libopenblas.dll.a" -DLAPACK_LIBRARY:FILEPATH="%APPVEYOR_BUILD_FOLDER%/OpenBLAS.0.2.14.1/lib/native/lib/x64/libopenblas.dll.a" -DCMAKE_PREFIX:FILEPATH="%APPVEYOR_BUILD_FOLDER%/armadillo" -DBUILD_SHARED_LIBS=OFF -DCMAKE_BUILD_TYPE=Release .. - '"C:\Program Files (x86)\MSBuild\14.0\Bin\MSBuild.exe" "C:\projects\mlpack\armadillo-7.800.2\build\armadillo.sln" /m /verbosity:quiet /p:Configuration=Release;Platform=x64' - cd C:\projects\mlpack && mkdir build && cd build - - cmake -G "Visual Studio 14 2015 Win64" -DBLAS_LIBRARY:FILEPATH="%APPVEYOR_BUILD_FOLDER%/OpenBLAS.0.2.14.1/lib/native/lib/x64/libopenblas.dll.a" -DLAPACK_LIBRARY:FILEPATH="%APPVEYOR_BUILD_FOLDER%/OpenBLAS.0.2.14.1/lib/native/lib/x64/libopenblas.dll.a" -DARMADILLO_INCLUDE_DIR="C:/projects/mlpack/armadillo-7.800.2/include" -DARMADILLO_LIBRARY:FILEPATH="C:\projects\mlpack\armadillo-7.800.2\build\Debug\armadillo.lib" -DBOOST_INCLUDEDIR:PATH="C:\projects\mlpack\boost.1.60.0.0\lib\native\include" -DBOOST_LIBRARYDIR:PATH="C:\projects\mlpack\boost_libs" -DDEBUG=ON -DPROFILE=ON -DBUILD_PYTHON_BINDINGS=OFF .. + - cmake -G "Visual Studio 14 2015 Win64" -DBLAS_LIBRARY:FILEPATH="%APPVEYOR_BUILD_FOLDER%/OpenBLAS.0.2.14.1/lib/native/lib/x64/libopenblas.dll.a" -DLAPACK_LIBRARY:FILEPATH="%APPVEYOR_BUILD_FOLDER%/OpenBLAS.0.2.14.1/lib/native/lib/x64/libopenblas.dll.a" -DARMADILLO_INCLUDE_DIR="C:/projects/mlpack/armadillo-7.800.2/include" -DARMADILLO_LIBRARY:FILEPATH="C:\projects\mlpack\armadillo-7.800.2\build\Debug\armadillo.lib" -DBOOST_INCLUDEDIR:PATH="C:\projects\mlpack\boost.1.60.0.0\lib\native\include" -DBOOST_LIBRARYDIR:PATH="C:\projects\mlpack\boost_libs" -DDEBUG=OFF -DPROFILE=OFF -DBUILD_PYTHON_BINDINGS=OFF -DCMAKE_BUILD_TYPE=Release .. - '"C:\Program Files (x86)\MSBuild\14.0\Bin\MSBuild.exe" "C:\projects\mlpack\build\mlpack.sln" /m /verbosity:minimal /nologo /p:BuildInParallel=true /p:Configuration=Release;Platform=x64' - 7z a mlpack-windows-no-libs.zip "%APPVEYOR_BUILD_FOLDER%\build\Release\*.exe" - 7z a mlpack-windows.zip "%APPVEYOR_BUILD_FOLDER%\build\Release\*.*" "%APPVEYOR_BUILD_FOLDER%/OpenBLAS.0.2.14.1/lib/native/lib/x64/*.*" @@ -53,8 +53,13 @@ cache: - packages -> **\packages.config - armadillo.tar.xz -> appveyor.yaml -# All plans have maximum build job execution time of 60 minutes. But right, now -# the machine takes 30 minutes to build the code and at least 50 minutes to run -# all tests. -# test_script: - -# '"C:\projects\mlpack\build\Release\mlpack_test.exe" -p' +test_script: + # Copy all DLLs into the right place before running the test. + - ps: cp C:\projects\mlpack\boost_libs\*.* C:\projects\mlpack\build\ + - ps: cp C:\projects\mlpack\OpenBLAS.0.2.14.1\lib\native\bin\x64\*.* C:\projects\mlpack\build\ + - cd "%APPVEYOR_BUILD_FOLDER%/build/" + - Release\mlpack_test.exe --report_level=detailed --log_level=test_suite --log_format=XML > mlpack_test.xml & exit 0 + # Attempt to upload results to AppVeyor. + - ps: | + $wc = New-Object 'System.Net.WebClient' + $wc.UploadFile("https://ci.appveyor.com/api/testresults/xunit/$($env:APPVEYOR_JOB_ID)", (Resolve-Path .\mlpack_test.xml)) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8e59adf518..2c4998e43e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -64,6 +64,11 @@ endif() # specific. This list is a subset of MLPACK_LIBRARIES. set(COMPILER_SUPPORT_LIBRARIES "") +# If we are using MSVC, we need /bigobj. +if (MSVC) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /bigobj") +endif () + # If using clang, we have to link against libc++ depending on the # OS (at least on some systems). Further, gcc sometimes optimizes calls to # math.h functions, making -lm unnecessary with gcc, but it may still be @@ -142,8 +147,6 @@ if(DEBUG) add_definitions(-DDEBUG) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 -ftemplate-backtrace-limit=0") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -g -O0") - else() - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /bigobj") endif() # mlpack uses it's own mlpack::backtrace class based on Binary File Descriptor diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt index b29dded095..79e41b787c 100644 --- a/COPYRIGHT.txt +++ b/COPYRIGHT.txt @@ -87,6 +87,7 @@ Copyright: Copyright 2017, Manish Kumar Copyright 2017, Haritha Sreedharan Nair Copyright 2017&2018, Sourabh Varshney + Copyright 2018, Nikhil Goel License: BSD-3-clause All rights reserved. diff --git a/src/mlpack/bindings/cli/CMakeLists.txt b/src/mlpack/bindings/cli/CMakeLists.txt index 095fee3c1a..83c50292ee 100644 --- a/src/mlpack/bindings/cli/CMakeLists.txt +++ b/src/mlpack/bindings/cli/CMakeLists.txt @@ -5,7 +5,9 @@ set(SOURCES cli_option.hpp default_param.hpp default_param_impl.hpp + delete_allocated_memory.hpp end_program.hpp + get_allocated_memory.hpp get_param.hpp get_raw_param.hpp get_printable_param.hpp diff --git a/src/mlpack/bindings/cli/add_to_po.hpp b/src/mlpack/bindings/cli/add_to_po.hpp index 2251649a83..0c3a1d789b 100644 --- a/src/mlpack/bindings/cli/add_to_po.hpp +++ b/src/mlpack/bindings/cli/add_to_po.hpp @@ -89,13 +89,15 @@ void AddToPO(const util::ParamData& d, (boost::program_options::options_description*) output; // Generate the name to be given to boost::program_options. - const std::string mappedName = MapParameterName(d.name); + const std::string mappedName = + MapParameterName::type>(d.name); std::string boostName = (d.alias != '\0') ? mappedName + "," + std::string(1, d.alias) : mappedName; // Note that we have to add the option as type equal to the mapped type, not // the true type of the option. - AddToPO::type>(boostName, d.desc, *desc); + AddToPO::type>::type>( + boostName, d.desc, *desc); } } // namespace cli diff --git a/src/mlpack/bindings/cli/cli_option.hpp b/src/mlpack/bindings/cli/cli_option.hpp index 47233b29ec..7944039628 100644 --- a/src/mlpack/bindings/cli/cli_option.hpp +++ b/src/mlpack/bindings/cli/cli_option.hpp @@ -28,6 +28,8 @@ #include "set_param.hpp" #include "get_printable_param_name.hpp" #include "get_printable_param_value.hpp" +#include "get_allocated_memory.hpp" +#include "delete_allocated_memory.hpp" namespace mlpack { namespace bindings { @@ -88,19 +90,21 @@ class CLIOption data.cppType = cppName; // Apply default value. - if (std::is_same::type>::value) + if (std::is_same::type, + typename ParameterType::type>::type>::value) { data.value = boost::any(defaultValue); } else { - typename ParameterType::type tmp; - data.value = boost::any(std::tuple::type>( - defaultValue, tmp)); + typename ParameterType::type>::type tmp; + data.value = boost::any(std::tuple(defaultValue, tmp)); } const std::string tname = data.tname; - const std::string boostName = MapParameterName(identifier); + const std::string boostName = MapParameterName< + typename std::remove_pointer::type>(identifier); std::string progOptId = (alias[0] != '\0') ? boostName + "," + std::string(1, alias[0]) : boostName; @@ -152,6 +156,10 @@ class CLIOption &GetPrintableParamName; CLI::GetSingleton().functionMap[tname]["GetPrintableParamValue"] = &GetPrintableParamValue; + CLI::GetSingleton().functionMap[tname]["GetAllocatedMemory"] = + &GetAllocatedMemory; + CLI::GetSingleton().functionMap[tname]["DeleteAllocatedMemory"] = + &DeleteAllocatedMemory; } }; diff --git a/src/mlpack/bindings/cli/default_param.hpp b/src/mlpack/bindings/cli/default_param.hpp index 815ee8c3de..74347c9601 100644 --- a/src/mlpack/bindings/cli/default_param.hpp +++ b/src/mlpack/bindings/cli/default_param.hpp @@ -54,10 +54,19 @@ std::string DefaultParamImpl( const util::ParamData& data, const typename boost::enable_if_c< arma::is_arma_type::value || - data::HasSerialize::value || std::is_same>::value>::type* /* junk */ = 0); +/** + * Return the default value of a model option (this returns the default + * filename, or '' if the default is no file). + */ +template +std::string DefaultParamImpl( + const util::ParamData& data, + const typename boost::disable_if>::type* = 0, + const typename boost::enable_if>::type* = 0); + /** * Return the default value of an option. This is the function that will be * placed into the CLI functionMap. @@ -68,7 +77,7 @@ void DefaultParam(const util::ParamData& data, void* output) { std::string* outstr = (std::string*) output; - *outstr = DefaultParamImpl(data); + *outstr = DefaultParamImpl::type>(data); } } // namespace cli diff --git a/src/mlpack/bindings/cli/default_param_impl.hpp b/src/mlpack/bindings/cli/default_param_impl.hpp index ec8f6d4095..377b25ff16 100644 --- a/src/mlpack/bindings/cli/default_param_impl.hpp +++ b/src/mlpack/bindings/cli/default_param_impl.hpp @@ -70,7 +70,6 @@ std::string DefaultParamImpl( const util::ParamData& data, const typename boost::enable_if_c< arma::is_arma_type::value || - data::HasSerialize::value || std::is_same>::value>::type* /* junk */) { @@ -81,6 +80,24 @@ std::string DefaultParamImpl( return "'" + filename + "'"; } +/** + * Return the default value of a model option (this returns the default + * filename, or '' if the default is no file). + */ +template +std::string DefaultParamImpl( + const util::ParamData& data, + const typename boost::disable_if>::type* /* junk */, + const typename boost::enable_if>::type* /* junk */) +{ + // Get the filename and return it, or return an empty string. + typedef std::tuple TupleType; + const TupleType& tuple = *boost::any_cast(&data.value); + const std::string& filename = std::get<1>(tuple); + return "'" + filename + "'"; +} + + } // namespace cli } // namespace bindings } // namespace mlpack diff --git a/src/mlpack/bindings/cli/delete_allocated_memory.hpp b/src/mlpack/bindings/cli/delete_allocated_memory.hpp new file mode 100644 index 0000000000..edc1fcf861 --- /dev/null +++ b/src/mlpack/bindings/cli/delete_allocated_memory.hpp @@ -0,0 +1,57 @@ +/** + * @file delete_allocated_memory.hpp + * @author Ryan Curtin + * + * If any memory has been allocated by the parameter, delete it. + */ +#ifndef MLPACK_BINDINGS_CLI_DELETE_ALLOCATED_MEMORY_HPP +#define MLPACK_BINDINGS_CLI_DELETE_ALLOCATED_MEMORY_HPP + +#include + +namespace mlpack { +namespace bindings { +namespace cli { + +template +void DeleteAllocatedMemoryImpl( + const util::ParamData& /* d */, + const typename boost::disable_if>::type* = 0, + const typename boost::disable_if>::type* = 0) +{ + // Do nothing. +} + +template +void DeleteAllocatedMemoryImpl( + const util::ParamData& /* d */, + const typename boost::enable_if>::type* = 0) +{ + // Do nothing. +} + +template +void DeleteAllocatedMemoryImpl( + const util::ParamData& d, + const typename boost::disable_if>::type* = 0, + const typename boost::enable_if>::type* = 0) +{ + // Delete the allocated memory (hopefully we actually own it). + typedef std::tuple TupleType; + delete std::get<0>(*boost::any_cast(&d.value)); +} + +template +void DeleteAllocatedMemory( + const util::ParamData& d, + const void* /* input */, + void* /* output */) +{ + DeleteAllocatedMemoryImpl::type>(d); +} + +} // namespace cli +} // namespace bindings +} // namespace mlpack + +#endif diff --git a/src/mlpack/bindings/cli/end_program.hpp b/src/mlpack/bindings/cli/end_program.hpp index 4eed33438c..56a3809ebd 100644 --- a/src/mlpack/bindings/cli/end_program.hpp +++ b/src/mlpack/bindings/cli/end_program.hpp @@ -67,6 +67,37 @@ inline void EndProgram() CLI::GetSingleton().timer.PrintTimer(it2.first); } } + + // Lastly clean up any memory. If we are holding any pointers, then we "own" + // them. But we may hold the same pointer twice, so we have to be careful to + // not delete it multiple times. + std::unordered_map memoryAddresses; + it = parameters.begin(); + while (it != parameters.end()) + { + const util::ParamData& data = it->second; + + void* result; + CLI::GetSingleton().functionMap[data.tname]["GetAllocatedMemory"](data, + NULL, (void*) &result); + if (result != NULL && memoryAddresses.count(result) == 0) + memoryAddresses[result] = &data; + + ++it; + } + + // Now we have all the unique addresses that need to be deleted. + std::unordered_map::const_iterator it2; + it2 = memoryAddresses.begin(); + while (it2 != memoryAddresses.end()) + { + const util::ParamData& data = *(it2->second); + + CLI::GetSingleton().functionMap[data.tname]["DeleteAllocatedMemory"](data, + NULL, NULL); + + ++it2; + } } } // namespace cli diff --git a/src/mlpack/bindings/cli/get_allocated_memory.hpp b/src/mlpack/bindings/cli/get_allocated_memory.hpp new file mode 100644 index 0000000000..2dda83e212 --- /dev/null +++ b/src/mlpack/bindings/cli/get_allocated_memory.hpp @@ -0,0 +1,59 @@ +/** + * @file get_allocated_memory.hpp + * @author Ryan Curtin + * + * If the parameter has a type that may need to be deleted, return the address + * of that object. Otherwise return NULL. + */ +#ifndef MLPACK_BINDINGS_CLI_GET_ALLOCATED_MEMORY_HPP +#define MLPACK_BINDINGS_CLI_GET_ALLOCATED_MEMORY_HPP + +#include + +namespace mlpack { +namespace bindings { +namespace cli { + +template +void* GetAllocatedMemory( + const util::ParamData& /* d */, + const typename boost::disable_if>::type* = 0, + const typename boost::disable_if>::type* = 0) +{ + return NULL; +} + +template +void* GetAllocatedMemory( + const util::ParamData& /* d */, + const typename boost::enable_if>::type* = 0) +{ + return NULL; +} + +template +void* GetAllocatedMemory( + const util::ParamData& d, + const typename boost::disable_if>::type* = 0, + const typename boost::enable_if>::type* = 0) +{ + // Here we have a model, which is a tuple, and we need the address of the + // memory. + typedef std::tuple TupleType; + return std::get<0>(*boost::any_cast(&d.value)); +} + +template +void GetAllocatedMemory(const util::ParamData& d, + const void* /* input */, + void* output) +{ + *((void**) output) = + GetAllocatedMemory::type>(d); +} + +} // namespace cli +} // namespace bindings +} // namespace mlpack + +#endif diff --git a/src/mlpack/bindings/cli/get_param.hpp b/src/mlpack/bindings/cli/get_param.hpp index fca9c823fe..4ddfe7f72b 100644 --- a/src/mlpack/bindings/cli/get_param.hpp +++ b/src/mlpack/bindings/cli/get_param.hpp @@ -95,24 +95,24 @@ T& GetParam( * @param d ParamData object to get parameter value from. */ template -T& GetParam( +T*& GetParam( util::ParamData& d, const typename boost::disable_if>::type* = 0, const typename boost::enable_if>::type* = 0) { // If the model is an input model, we have to load it from file. 'value' // contains the filename. - typedef std::tuple TupleType; + typedef std::tuple TupleType; TupleType* tuple = boost::any_cast(&d.value); const std::string& value = std::get<1>(*tuple); - T& model = std::get<0>(*tuple); if (d.input && !d.loaded) { - data::Load(value, "model", model, true); + T* model = new T(); + data::Load(value, "model", *model, true); d.loaded = true; + std::get<0>(*tuple) = model; } - - return model; + return std::get<0>(*tuple); } /** @@ -127,7 +127,8 @@ template void GetParam(const util::ParamData& d, const void* /* input */, void* output) { // Cast to the correct type. - *((T**) output) = &GetParam(const_cast(d)); + *((T**) output) = &GetParam::type>( + const_cast(d)); } } // namespace cli diff --git a/src/mlpack/bindings/cli/get_printable_param.hpp b/src/mlpack/bindings/cli/get_printable_param.hpp index 2b082b338d..5688dc2209 100644 --- a/src/mlpack/bindings/cli/get_printable_param.hpp +++ b/src/mlpack/bindings/cli/get_printable_param.hpp @@ -37,16 +37,24 @@ std::string GetPrintableParam( const typename std::enable_if::value>::type* = 0); /** - * Print a matrix option (this just prints the filename). + * Print a matrix/tuple option (this just prints the filename). */ template std::string GetPrintableParam( const util::ParamData& data, const typename std::enable_if::value || - data::HasSerialize::value || std::is_same>::value>::type* = 0); +/** + * Print a model option (this just prints the filename). + */ +template +std::string GetPrintableParam( + const util::ParamData& data, + const typename boost::disable_if>::type* = 0, + const typename boost::enable_if>::type* = 0); + /** * Print an option into a std::string. This should print a short, one-line * representation of the object. The string will be stored in the output @@ -57,7 +65,8 @@ void GetPrintableParam(const util::ParamData& data, const void* /* input */, void* output) { - *((std::string*) output) = GetPrintableParam(data); + *((std::string*) output) = + GetPrintableParam::type>(data); } } // namespace cli diff --git a/src/mlpack/bindings/cli/get_printable_param_impl.hpp b/src/mlpack/bindings/cli/get_printable_param_impl.hpp index 0e55cb649a..6fb65bc48d 100644 --- a/src/mlpack/bindings/cli/get_printable_param_impl.hpp +++ b/src/mlpack/bindings/cli/get_printable_param_impl.hpp @@ -43,12 +43,11 @@ std::string GetPrintableParam( return oss.str(); } -//! Print a matrix/model/tuple option (this just prints the filename). +//! Print a matrix/tuple option (this just prints the filename). template std::string GetPrintableParam( const util::ParamData& data, const typename std::enable_if::value || - data::HasSerialize::value || std::is_same>::value>::type* /* junk */) { @@ -61,6 +60,22 @@ std::string GetPrintableParam( return oss.str(); } +//! Print a model option (this just prints the filename). +template +std::string GetPrintableParam( + const util::ParamData& data, + const typename boost::disable_if>::type* /* junk */, + const typename boost::enable_if>::type* /* junk */) +{ + // Extract the string from the tuple that's being held. + typedef std::tuple::type> TupleType; + const TupleType* tuple = boost::any_cast(&data.value); + + std::ostringstream oss; + oss << std::get<1>(*tuple); + return oss.str(); +} + } // namespace cli } // namespace bindings } // namespace mlpack diff --git a/src/mlpack/bindings/cli/get_printable_param_name.hpp b/src/mlpack/bindings/cli/get_printable_param_name.hpp index 12c7f92cfd..e323d2378a 100644 --- a/src/mlpack/bindings/cli/get_printable_param_name.hpp +++ b/src/mlpack/bindings/cli/get_printable_param_name.hpp @@ -64,7 +64,8 @@ void GetPrintableParamName( const void* /* input */, void* output) { - *((std::string*) output) = GetPrintableParamName(d); + *((std::string*) output) = + GetPrintableParamName::type>(d); } } // namespace cli diff --git a/src/mlpack/bindings/cli/get_printable_param_value.hpp b/src/mlpack/bindings/cli/get_printable_param_value.hpp index be29934d51..0110c0dfdc 100644 --- a/src/mlpack/bindings/cli/get_printable_param_value.hpp +++ b/src/mlpack/bindings/cli/get_printable_param_value.hpp @@ -68,7 +68,8 @@ void GetPrintableParamValue( const void* input, void* output) { - *((std::string*) output) = GetPrintableParamValue(d, + *((std::string*) output) = + GetPrintableParamValue::type>(d, *((std::string*) input)); } diff --git a/src/mlpack/bindings/cli/get_raw_param.hpp b/src/mlpack/bindings/cli/get_raw_param.hpp index ae73ca0d36..a397ffff54 100644 --- a/src/mlpack/bindings/cli/get_raw_param.hpp +++ b/src/mlpack/bindings/cli/get_raw_param.hpp @@ -40,15 +40,29 @@ T& GetRawParam( const typename boost::enable_if_c< arma::is_arma_type::value || std::is_same>::value || - data::HasSerialize::value>::type* = 0) + arma::mat>>::value>::type* = 0) { - // Don't load the matrix/model. + // Don't load the matrix. typedef std::tuple TupleType; T& value = std::get<0>(*boost::any_cast(&d.value)); return value; } +/** + * Return the name of a model parameter. + */ +template +T*& GetRawParam( + util::ParamData& d, + const typename boost::disable_if>::type* = 0, + const typename boost::enable_if>::type* = 0) +{ + // Don't load the model. + typedef std::tuple TupleType; + T*& value = std::get<0>(*boost::any_cast(&d.value)); + return value; +} + /** * Return a parameter casted to the given type. Type checking does not happen * here! @@ -63,7 +77,8 @@ void GetRawParam(const util::ParamData& d, void* output) { // Cast to the correct type. - *((T**) output) = &GetRawParam(const_cast(d)); + *((T**) output) = &GetRawParam::type>( + const_cast(d)); } } // namespace cli diff --git a/src/mlpack/bindings/cli/map_parameter_name.hpp b/src/mlpack/bindings/cli/map_parameter_name.hpp index f75f3ad4af..2d37d6c384 100644 --- a/src/mlpack/bindings/cli/map_parameter_name.hpp +++ b/src/mlpack/bindings/cli/map_parameter_name.hpp @@ -61,7 +61,8 @@ void MapParameterName(const util::ParamData& d, { // Store the mapped name in the output pointer, which is actually a string // pointer. - *((std::string*) output) = MapParameterName(d.name); + *((std::string*) output) = + MapParameterName::type>(d.name); } } // namespace cli diff --git a/src/mlpack/bindings/cli/output_param.hpp b/src/mlpack/bindings/cli/output_param.hpp index c4ce9c3b5d..4ed052d55f 100644 --- a/src/mlpack/bindings/cli/output_param.hpp +++ b/src/mlpack/bindings/cli/output_param.hpp @@ -70,7 +70,7 @@ void OutputParam(const util::ParamData& data, const void* /* input */, void* /* output */) { - OutputParamImpl(data); + OutputParamImpl::type>(data); } } // namespace cli diff --git a/src/mlpack/bindings/cli/output_param_impl.hpp b/src/mlpack/bindings/cli/output_param_impl.hpp index 3e30a66ff8..a85e55a941 100644 --- a/src/mlpack/bindings/cli/output_param_impl.hpp +++ b/src/mlpack/bindings/cli/output_param_impl.hpp @@ -55,10 +55,10 @@ void OutputParamImpl( if (output.n_elem > 0 && filename != "") { - if (arma::is_Row::value || arma::is_Col::value) - data::Save(filename, output, false); - else - data::Save(filename, output, false, !data.noTranspose); + if (arma::is_Row::value || arma::is_Col::value) + data::Save(filename, output, false); + else + data::Save(filename, output, false, !data.noTranspose); } } @@ -72,14 +72,14 @@ void OutputParamImpl( // The const cast is necessary here because Serialize() can't ever be marked // const. In this case we can assume it though, since we will be saving and // not loading. - typedef std::tuple TupleType; - T& output = const_cast(std::get<0>(*boost::any_cast( + typedef std::tuple TupleType; + T*& output = const_cast(std::get<0>(*boost::any_cast( &data.value))); const std::string& filename = std::get<1>(*boost::any_cast(&data.value)); if (filename != "") - data::Save(filename, "model", output); + data::Save(filename, "model", *output); } //! Output a mapped dataset. diff --git a/src/mlpack/bindings/cli/parse_command_line.hpp b/src/mlpack/bindings/cli/parse_command_line.hpp index 3e0b959eaf..b7f7cac0de 100644 --- a/src/mlpack/bindings/cli/parse_command_line.hpp +++ b/src/mlpack/bindings/cli/parse_command_line.hpp @@ -52,7 +52,7 @@ void ParseCommandLine(int argc, char** argv) boostNameMap[boostName] = d.name; } - // TODO: we have to mark somehow that we parsed. + // Mark that we did parsing. CLI::GetSingleton().didParse = true; // Parse the command line, then place the values in the right place. diff --git a/src/mlpack/bindings/cli/set_param.hpp b/src/mlpack/bindings/cli/set_param.hpp index fb1fc0efce..64103135c5 100644 --- a/src/mlpack/bindings/cli/set_param.hpp +++ b/src/mlpack/bindings/cli/set_param.hpp @@ -54,7 +54,6 @@ void SetParam( util::ParamData& d, const boost::any& value, const typename std::enable_if::value || - data::HasSerialize::value || std::is_same>::value>::type* = 0) { @@ -64,6 +63,23 @@ void SetParam( std::get<1>(tuple) = boost::any_cast(value); } +/** + * Set a serializable object. This sets the filename referring to the + * parameter. + */ +template +void SetParam( + util::ParamData& d, + const boost::any& value, + const typename boost::disable_if>::type* = 0, + const typename boost::enable_if>::type* = 0) +{ + // We're setting the string filename. + typedef std::tuple::type> TupleType; + TupleType& tuple = *boost::any_cast(&d.value); + std::get<1>(tuple) = boost::any_cast(value); +} + /** * Return a parameter casted to the given type. Type checking does not happen * here! @@ -75,7 +91,8 @@ void SetParam( template void SetParam(const util::ParamData& d, const void* input, void* /* output */) { - SetParam(const_cast(d), *((boost::any*) input)); + SetParam::type>( + const_cast(d), *((boost::any*) input)); } } // namespace cli diff --git a/src/mlpack/bindings/python/CMakeLists.txt b/src/mlpack/bindings/python/CMakeLists.txt index be49a1f238..88ec98a949 100644 --- a/src/mlpack/bindings/python/CMakeLists.txt +++ b/src/mlpack/bindings/python/CMakeLists.txt @@ -79,7 +79,6 @@ set(CYTHON_SOURCES mlpack/arma_util.hpp mlpack/cli.pxd mlpack/cli_util.hpp - mlpack/move.hpp mlpack/matrix_utils.py mlpack/serialization.hpp mlpack/serialization.pxd @@ -148,7 +147,6 @@ add_custom_command(TARGET python POST_BUILD mlpack/arma_util.hpp mlpack/cli.pxd mlpack/cli_util.hpp - mlpack/move.hpp mlpack/matrix_utils.py mlpack WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/src/mlpack/bindings/python/) diff --git a/src/mlpack/bindings/python/get_cython_type.hpp b/src/mlpack/bindings/python/get_cython_type.hpp index f5ae807abe..8e95dc2d9d 100644 --- a/src/mlpack/bindings/python/get_cython_type.hpp +++ b/src/mlpack/bindings/python/get_cython_type.hpp @@ -113,7 +113,7 @@ inline std::string GetCythonType( const typename boost::disable_if>::type* = 0, const typename boost::enable_if>::type* = 0) { - return d.cppType; + return d.cppType + "*"; } } // namespace python diff --git a/src/mlpack/bindings/python/get_printable_param.hpp b/src/mlpack/bindings/python/get_printable_param.hpp index b7258bc7c8..0c21dd5a61 100644 --- a/src/mlpack/bindings/python/get_printable_param.hpp +++ b/src/mlpack/bindings/python/get_printable_param.hpp @@ -73,7 +73,7 @@ std::string GetPrintableParam( const typename boost::enable_if>::type* = 0) { std::ostringstream oss; - oss << data.cppType << " model"; + oss << data.cppType << " model at " << boost::any_cast(data.value); return oss.str(); } @@ -110,7 +110,8 @@ void GetPrintableParam(const util::ParamData& data, const void* /* input */, void* output) { - *((std::string*) output) = GetPrintableParam(data); + *((std::string*) output) = + GetPrintableParam::type>(data); } } // namespace python diff --git a/src/mlpack/bindings/python/import_decl.hpp b/src/mlpack/bindings/python/import_decl.hpp index b638ecde42..52d1026340 100644 --- a/src/mlpack/bindings/python/import_decl.hpp +++ b/src/mlpack/bindings/python/import_decl.hpp @@ -79,7 +79,7 @@ void ImportDecl(const util::ParamData& d, const void* indent, void* /* output */) { - ImportDecl(d, *((size_t*) indent)); + ImportDecl::type>(d, *((size_t*) indent)); } } // namespace python diff --git a/src/mlpack/bindings/python/mlpack/arma_numpy.pxd b/src/mlpack/bindings/python/mlpack/arma_numpy.pxd index 167810cb68..a0e9b8547e 100644 --- a/src/mlpack/bindings/python/mlpack/arma_numpy.pxd +++ b/src/mlpack/bindings/python/mlpack/arma_numpy.pxd @@ -19,14 +19,15 @@ import numpy numpy.import_array() cimport arma +from libcpp cimport bool """ Convert a numpy ndarray to a matrix. """ -cdef arma.Mat[double]* numpy_to_mat_d(numpy.ndarray[numpy.double_t, ndim=2] X) \ - except + -cdef arma.Mat[size_t]* numpy_to_mat_s(numpy.ndarray[numpy.npy_intp, ndim=2] X) \ - except + +cdef arma.Mat[double]* numpy_to_mat_d(numpy.ndarray[numpy.double_t, ndim=2] X, \ + bool takeOwnership) except + +cdef arma.Mat[size_t]* numpy_to_mat_s(numpy.ndarray[numpy.npy_intp, ndim=2] X, \ + bool takeOwnership) except + """ Convert an Armadillo object to a numpy ndarray of the given type. @@ -39,10 +40,10 @@ cdef numpy.ndarray[numpy.npy_intp, ndim=2] mat_to_numpy_s(arma.Mat[size_t]& X) \ """ Convert a numpy one-dimensional ndarray to a row of the given type. """ -cdef arma.Row[double]* numpy_to_row_d(numpy.ndarray[numpy.double_t, ndim=1] X) \ - except + -cdef arma.Row[size_t]* numpy_to_row_s(numpy.ndarray[numpy.npy_intp, ndim=1] X) \ - except + +cdef arma.Row[double]* numpy_to_row_d(numpy.ndarray[numpy.double_t, ndim=1] X, \ + bool takeOwnership) except + +cdef arma.Row[size_t]* numpy_to_row_s(numpy.ndarray[numpy.npy_intp, ndim=1] X, \ + bool takeOwnership) except + """ Convert an Armadillo row vector to a one-dimensional numpy ndarray of the @@ -56,10 +57,10 @@ cdef numpy.ndarray[numpy.npy_intp, ndim=1] row_to_numpy_s(arma.Row[size_t]& X) \ """ Convert a numpy one-dimensional ndarray to a column vector of the given type. """ -cdef arma.Col[double]* numpy_to_col_d(numpy.ndarray[numpy.double_t, ndim=1] X) \ - except + -cdef arma.Col[size_t]* numpy_to_col_s(numpy.ndarray[numpy.npy_intp, ndim=1] X) \ - except + +cdef arma.Col[double]* numpy_to_col_d(numpy.ndarray[numpy.double_t, ndim=1] X, \ + bool takeOwnership) except + +cdef arma.Col[size_t]* numpy_to_col_s(numpy.ndarray[numpy.npy_intp, ndim=1] X, \ + bool takeOwnership) except + """ Convert an Armadillo column vector to a one-dimensional numpy ndarray of the diff --git a/src/mlpack/bindings/python/mlpack/arma_numpy.pyx b/src/mlpack/bindings/python/mlpack/arma_numpy.pyx index 8a550c40f2..dcf6908d94 100644 --- a/src/mlpack/bindings/python/mlpack/arma_numpy.pyx +++ b/src/mlpack/bindings/python/mlpack/arma_numpy.pyx @@ -24,6 +24,7 @@ import numpy numpy.import_array() cimport arma +from libcpp cimport bool cdef extern from "numpy/arrayobject.h": void PyArray_ENABLEFLAGS(numpy.ndarray arr, int flags) @@ -31,6 +32,7 @@ cdef extern from "numpy/arrayobject.h": cdef extern from "": void SetMemState[T](T& m, int state) + size_t GetMemState[T](T& m) double* GetMemory(arma.Mat[double]& m) double* GetMemory(arma.Col[double]& m) double* GetMemory(arma.Row[double]& m) @@ -38,38 +40,43 @@ cdef extern from "": size_t* GetMemory(arma.Col[size_t]& m) size_t* GetMemory(arma.Row[size_t]& m) -cdef arma.Mat[double]* numpy_to_mat_d(numpy.ndarray[numpy.double_t, ndim=2] X) \ - except +: +cdef arma.Mat[double]* numpy_to_mat_d(numpy.ndarray[numpy.double_t, ndim=2] X, \ + bool takeOwnership) except +: """ - Convert a numpy ndarray to a matrix. + Convert a numpy ndarray to a matrix. The memory will still be owned by numpy. """ if not (X.flags.c_contiguous or X.flags.owndata): # If needed, make a copy where we own the memory. X = X.copy(order="C") + takeOwnership = True - cdef arma.Mat[double]* m = new arma.Mat[double]( X.data, X.shape[1], X.shape[0], False, True) + cdef arma.Mat[double]* m = new arma.Mat[double]( X.data, X.shape[1],\ + X.shape[0], False, False) - # Transfer ownership to the Armadillo matrix. - PyArray_CLEARFLAGS(X, numpy.NPY_OWNDATA) - SetMemState[arma.Mat[double]](m[0], 0) + # Take ownership of the memory, if we need to. + if takeOwnership: + PyArray_CLEARFLAGS(X, numpy.NPY_OWNDATA) + SetMemState[arma.Mat[double]](m[0], 0) return m -cdef arma.Mat[size_t]* numpy_to_mat_s(numpy.ndarray[numpy.npy_intp, ndim=2] X) \ - except +: +cdef arma.Mat[size_t]* numpy_to_mat_s(numpy.ndarray[numpy.npy_intp, ndim=2] X, \ + bool takeOwnership) except +: """ - Convert a numpy ndarray to a matrix. + Convert a numpy ndarray to a matrix. The memory will still be owned by numpy. """ if not (X.flags.c_contiguous or X.flags.owndata): # If needed, make a copy where we own the memory. X = X.copy(order="C") + takeOwnership = True cdef arma.Mat[size_t]* m = new arma.Mat[size_t]( X.data, X.shape[1], - X.shape[0], False, True) + X.shape[0], False, False) - # Transfer ownership to the Armadillo matrix. - PyArray_CLEARFLAGS(X, numpy.NPY_OWNDATA) - SetMemState[arma.Mat[size_t]](m[0], 0) + # Take ownership of the memory, if we need to. + if takeOwnership: + PyArray_CLEARFLAGS(X, numpy.NPY_OWNDATA) + SetMemState[arma.Mat[size_t]](m[0], 0) return m @@ -85,9 +92,10 @@ cdef numpy.ndarray[numpy.double_t, ndim=2] mat_to_numpy_d(arma.Mat[double]& X) \ cdef numpy.ndarray[numpy.double_t, ndim=2] output = \ numpy.PyArray_SimpleNewFromData(2, &dims[0], numpy.NPY_DOUBLE, GetMemory(X)) - # Transfer memory ownership. - SetMemState[arma.Mat[double]](X, 1) - PyArray_ENABLEFLAGS(output, numpy.NPY_OWNDATA) + # Transfer memory ownership, if needed. + if GetMemState[arma.Mat[double]](X) == 0: + SetMemState[arma.Mat[double]](X, 1) + PyArray_ENABLEFLAGS(output, numpy.NPY_OWNDATA) return output @@ -103,45 +111,52 @@ cdef numpy.ndarray[numpy.npy_intp, ndim=2] mat_to_numpy_s(arma.Mat[size_t]& X) \ cdef numpy.ndarray[numpy.npy_intp, ndim=2] output = \ numpy.PyArray_SimpleNewFromData(2, &dims[0], numpy.NPY_INTP, GetMemory(X)) - # Transfer memory ownership. - SetMemState[arma.Mat[size_t]](X, 1) - PyArray_ENABLEFLAGS(output, numpy.NPY_OWNDATA) + # Transfer memory ownership, if needed. + if GetMemState[arma.Mat[size_t]](X) == 0: + SetMemState[arma.Mat[size_t]](X, 1) + PyArray_ENABLEFLAGS(output, numpy.NPY_OWNDATA) return output -cdef arma.Row[double]* numpy_to_row_d(numpy.ndarray[numpy.double_t, ndim=1] X) \ - except +: +cdef arma.Row[double]* numpy_to_row_d(numpy.ndarray[numpy.double_t, ndim=1] X, \ + bool takeOwnership) except +: """ - Convert a numpy one-dimensional ndarray to a row. + Convert a numpy one-dimensional ndarray to a row. The memory will still be + owned by numpy. """ if not (X.flags.c_contiguous or X.flags.owndata): # If needed, make a copy where we own the memory. X = X.copy(order="C") + takeOwnership = True cdef arma.Row[double]* m = new arma.Row[double]( X.data, X.shape[0], - False, True) + False, False) - # Transfer ownership to the Armadillo matrix. - PyArray_CLEARFLAGS(X, numpy.NPY_OWNDATA) - SetMemState[arma.Row[double]](m[0], 0) + # Transfer memory ownership, if needed. + if takeOwnership: + PyArray_CLEARFLAGS(X, numpy.NPY_OWNDATA) + SetMemState[arma.Row[double]](m[0], 0) return m -cdef arma.Row[size_t]* numpy_to_row_s(numpy.ndarray[numpy.npy_intp, ndim=1] X) \ - except +: +cdef arma.Row[size_t]* numpy_to_row_s(numpy.ndarray[numpy.npy_intp, ndim=1] X, \ + bool takeOwnership) except +: """ - Convert a numpy one-dimensional ndarray to a row. + Convert a numpy one-dimensional ndarray to a row. The memory will still be + owned by numpy. """ if not (X.flags.c_contiguous or X.flags.owndata): # If needed, make a copy where we own the memory. X = X.copy(order="C") + takeOwnership = True cdef arma.Row[size_t]* m = new arma.Row[size_t]( X.data, X.shape[0], - False, True) + False, False) - # Transfer ownership to the Armadillo matrix. - PyArray_CLEARFLAGS(X, numpy.NPY_OWNDATA) - SetMemState[arma.Row[size_t]](m[0], 0) + # Transfer memory ownership, if needed. + if takeOwnership: + PyArray_CLEARFLAGS(X, numpy.NPY_OWNDATA) + SetMemState[arma.Row[size_t]](m[0], 0) return m @@ -155,9 +170,10 @@ cdef numpy.ndarray[numpy.double_t, ndim=1] row_to_numpy_d(arma.Row[double]& X) \ cdef numpy.ndarray[numpy.double_t, ndim=1] output = \ numpy.PyArray_SimpleNewFromData(1, &dim, numpy.NPY_DOUBLE, GetMemory(X)) - # Transfer memory ownership. - SetMemState[arma.Row[double]](X, 1) - PyArray_ENABLEFLAGS(output, numpy.NPY_OWNDATA) + # Transfer memory ownership, if needed. + if GetMemState[arma.Row[double]](X) == 0: + SetMemState[arma.Row[double]](X, 1) + PyArray_ENABLEFLAGS(output, numpy.NPY_OWNDATA) return output @@ -171,45 +187,51 @@ cdef numpy.ndarray[numpy.npy_intp, ndim=1] row_to_numpy_s(arma.Row[size_t]& X) \ cdef numpy.ndarray[numpy.npy_intp, ndim=1] output = \ numpy.PyArray_SimpleNewFromData(1, &dim, numpy.NPY_INTP, GetMemory(X)) - # Transfer memory ownership. - SetMemState[arma.Row[size_t]](X, 1) - PyArray_ENABLEFLAGS(output, numpy.NPY_OWNDATA) + # Transfer memory ownership, if needed. + if GetMemState[arma.Row[size_t]](X) == 0: + SetMemState[arma.Row[size_t]](X, 1) + PyArray_ENABLEFLAGS(output, numpy.NPY_OWNDATA) return output -cdef arma.Col[double]* numpy_to_col_d(numpy.ndarray[numpy.double_t, ndim=1] X) \ - except +: +cdef arma.Col[double]* numpy_to_col_d(numpy.ndarray[numpy.double_t, ndim=1] X, \ + bool takeOwnership) except +: """ - Convert a numpy one-dimensional ndarray to a column vector. + Convert a numpy one-dimensional ndarray to a column vector. The memory will + still be owned by numpy. """ if not (X.flags.c_contiguous or X.flags.owndata): # If needed, make a copy where we own the memory. X = X.copy(order="C") + takeOwnership = True cdef arma.Col[double]* m = new arma.Col[double]( X.data, X.shape[0], False, True) - # Transfer ownership to the Armadillo matrix. - PyArray_CLEARFLAGS(X, numpy.NPY_OWNDATA) - SetMemState[arma.Col[double]](m[0], 0) + # Transfer memory ownership, if needed. + if takeOwnership: + PyArray_CLEARFLAGS(X, numpy.NPY_OWNDATA) + SetMemState[arma.Col[double]](m[0], 0) return m -cdef arma.Col[size_t]* numpy_to_col_s(numpy.ndarray[numpy.npy_intp, ndim=1] X) \ - except +: +cdef arma.Col[size_t]* numpy_to_col_s(numpy.ndarray[numpy.npy_intp, ndim=1] X, \ + bool takeOwnership) except +: """ - Convert a numpy one-dimensional ndarray to a column vector. + Convert a numpy one-dimensional ndarray to a column vector. The memory will + still be owned by numpy. """ if not (X.flags.c_contiguous or X.flags.owndata): # If needed, make a copy where we own the memory. X = X.copy(order="C") cdef arma.Col[size_t]* m = new arma.Col[size_t]( X.data, X.shape[0], - False, True) + False, False) - # Transfer ownership to the Armadillo matrix. - PyArray_CLEARFLAGS(X, numpy.NPY_OWNDATA) - SetMemState[arma.Col[size_t]](m[0], 0) + # Transfer memory ownership, if needed. + if takeOwnership: + PyArray_CLEARFLAGS(X, numpy.NPY_OWNDATA) + SetMemState[arma.Col[size_t]](m[0], 0) return m @@ -223,9 +245,10 @@ cdef numpy.ndarray[numpy.double_t, ndim=1] col_to_numpy_d(arma.Col[double]& X) \ cdef numpy.ndarray[numpy.double_t, ndim=1] output = \ numpy.PyArray_SimpleNewFromData(1, &dim, numpy.NPY_DOUBLE, GetMemory(X)) - # Transfer memory ownership. - SetMemState[arma.Col[double]](X, 1) - PyArray_ENABLEFLAGS(output, numpy.NPY_OWNDATA) + # Transfer memory ownership, if needed. + if GetMemState[arma.Col[double]](X) == 0: + SetMemState[arma.Col[double]](X, 1) + PyArray_ENABLEFLAGS(output, numpy.NPY_OWNDATA) return output @@ -239,8 +262,9 @@ cdef numpy.ndarray[numpy.npy_intp, ndim=1] col_to_numpy_s(arma.Col[size_t]& X) \ cdef numpy.ndarray[numpy.npy_intp, ndim=1] output = \ numpy.PyArray_SimpleNewFromData(1, &dim, numpy.NPY_INTP, GetMemory(X)) - # Transfer memory ownership. - SetMemState[arma.Col[size_t]](X, 1) - PyArray_ENABLEFLAGS(output, numpy.NPY_OWNDATA) + # Transfer memory ownership, if needed. + if GetMemState[arma.Col[size_t]](X) == 0: + SetMemState[arma.Col[size_t]](X, 1) + PyArray_ENABLEFLAGS(output, numpy.NPY_OWNDATA) return output diff --git a/src/mlpack/bindings/python/mlpack/arma_util.hpp b/src/mlpack/bindings/python/mlpack/arma_util.hpp index 7770877c3c..c94a51e67f 100644 --- a/src/mlpack/bindings/python/mlpack/arma_util.hpp +++ b/src/mlpack/bindings/python/mlpack/arma_util.hpp @@ -24,6 +24,20 @@ void SetMemState(T& t, int state) const_cast(t.mem_state) = state; } +/** + * Get the memory state of the given Armadillo object. + */ +template +size_t GetMemState(T& t) +{ + // Fake the memory state if we are using preallocated memory---since we will + // end up copying that memory, NumPy can own it. + if (t.mem && t.n_elem <= arma::arma_config::mat_prealloc) + return 0; + + return (size_t) t.mem_state; +} + /** * Return the matrix's allocated memory pointer, unless the matrix is using its * internal preallocated memory, in which case we copy that and return a diff --git a/src/mlpack/bindings/python/mlpack/cli.pxd b/src/mlpack/bindings/python/mlpack/cli.pxd index d081a774b0..20380817f5 100644 --- a/src/mlpack/bindings/python/mlpack/cli.pxd +++ b/src/mlpack/bindings/python/mlpack/cli.pxd @@ -20,6 +20,9 @@ cdef extern from "" namespace "mlpack" nogil: @staticmethod (T&) GetParam[T](string) nogil except + + @staticmethod + bool HasParam(string) nogil except + + @staticmethod void SetPassed(string) nogil except + @@ -37,16 +40,13 @@ cdef extern from "" namespace "mlpack" nogil: cdef extern from "" \ namespace "mlpack::util" nogil: - void SetParam[T](string, const T&) nogil except + - void SetParamWithInfo[T](string, const T&, const bool*) nogil except + + void SetParam[T](string, T&) nogil except + + void SetParamPtr[T](string, T*, bool) nogil except + + void SetParamWithInfo[T](string, T&, const bool*) nogil except + + (T*) GetParamPtr[T](string) nogil except + (T&) GetParamWithInfo[T](string) nogil except + void EnableVerbose() nogil except + void DisableVerbose() nogil except + void DisableBacktrace() nogil except + void ResetTimers() nogil except + void EnableTimers() nogil except + - -cdef extern from "" \ - namespace "mlpack::util" nogil: - void MoveFromPtr[T](T&, T*) nogil except + - void MoveToPtr[T](T*, T&) nogil except + diff --git a/src/mlpack/bindings/python/mlpack/cli_util.hpp b/src/mlpack/bindings/python/mlpack/cli_util.hpp index 657eb7b14a..c2873fcf36 100644 --- a/src/mlpack/bindings/python/mlpack/cli_util.hpp +++ b/src/mlpack/bindings/python/mlpack/cli_util.hpp @@ -29,9 +29,27 @@ namespace util { * @param value Value to set parameter to. */ template -inline void SetParam(const std::string& identifier, const T& value) +inline void SetParam(const std::string& identifier, T& value) { - CLI::GetParam(identifier) = value; + CLI::GetParam(identifier) = std::move(value); +} + +/** + * Set the parameter to the given value, given that the type is a pointer. + * + * This function exists to work around both Cython's lack of support for lvalue + * references and also its seeming lack of support for template pointer types. + * + * @param identifier Name of parameter. + * @param value Value to set parameter to. + * @param copy Whether or not the object should be copied. + */ +template +inline void SetParamPtr(const std::string& identifier, + T* value, + const bool copy) +{ + CLI::GetParam(identifier) = copy ? new T(*value) : value; } /** @@ -39,19 +57,20 @@ inline void SetParam(const std::string& identifier, const T& value) */ template inline void SetParamWithInfo(const std::string& identifier, - const T& matrix, + T& matrix, const bool* dims) { typedef typename std::tuple TupleType; typedef typename T::elem_type eT; // The true type of the parameter is std::tuple. - std::get<1>(CLI::GetParam(identifier)) = matrix; + const size_t dimensions = matrix.n_rows; + std::get<1>(CLI::GetParam(identifier)) = std::move(matrix); data::DatasetInfo& di = std::get<0>(CLI::GetParam(identifier)); - di = data::DatasetInfo(matrix.n_rows); + di = data::DatasetInfo(dimensions); bool hasCategoricals = false; - for (size_t i = 0; i < matrix.n_rows; ++i) + for (size_t i = 0; i < dimensions; ++i) { if (dims[i]) { @@ -63,9 +82,10 @@ inline void SetParamWithInfo(const std::string& identifier, // Do we need to find how many categories we have? if (hasCategoricals) { - arma::vec maxs = arma::max(matrix, 1); + arma::vec maxs = arma::max( + std::get<1>(CLI::GetParam(identifier)), 1); - for (size_t i = 0; i < matrix.n_rows; ++i) + for (size_t i = 0; i < dimensions; ++i) { if (dims[i]) { @@ -81,6 +101,16 @@ inline void SetParamWithInfo(const std::string& identifier, } } +/** + * Return a pointer. This function exists to work around Cython's seeming lack + * of support for template pointer types. + */ +template +T* GetParamPtr(const std::string& paramName) +{ + return CLI::GetParam(paramName); +} + /** * Return the matrix part of a matrix + dataset info parameter. */ diff --git a/src/mlpack/bindings/python/mlpack/matrix_utils.py b/src/mlpack/bindings/python/mlpack/matrix_utils.py index 04c9d4778d..58f39b61ed 100644 --- a/src/mlpack/bindings/python/mlpack/matrix_utils.py +++ b/src/mlpack/bindings/python/mlpack/matrix_utils.py @@ -37,7 +37,7 @@ try: except: buffer = memoryview -def to_matrix(x, dtype=np.double): +def to_matrix(x, dtype=np.double, copy=False): """ Given some array-like X, return a numpy ndarray of the same type. """ @@ -48,11 +48,14 @@ def to_matrix(x, dtype=np.double): raise TypeError("given argument is not array-like") if (isinstance(x, np.ndarray) and x.dtype == dtype and x.flags.c_contiguous): - return x + if copy: # Copy the matrix if required. + return x.copy("C"), True + else: + return x, False else: - return np.array(x, copy=True, dtype=dtype, order='C') + return np.array(x, copy=True, dtype=dtype, order='C'), True -def to_matrix_with_info(x, dtype): +def to_matrix_with_info(x, dtype, copy=False): """ Given some array-like X (which should be either a numpy ndarray or a pandas DataFrame, convert into a numpy matrix of the given dtype. @@ -66,7 +69,12 @@ def to_matrix_with_info(x, dtype): if isinstance(x, np.ndarray): # It is already an ndarray, so the vector of info is all 0s (all numeric). d = np.zeros([x.shape[1]], dtype=np.bool) - return (x, d) + + # Copy the matrix if needed. + if copy: + return (x.copy(order="C"), True, d) + else: + return (x, False, d) if isinstance(x, pd.DataFrame) or isinstance(x, pd.Series): # It's a pandas dataframe. So we need to see if any of the dtypes are @@ -79,8 +87,9 @@ def to_matrix_with_info(x, dtype): not np.dtype(str) in dtype_array and \ not np.dtype(unicode) in dtype_array: # We can just return the matrix as-is; it's all numeric. + t = to_matrix(x, dtype=dtype, copy=copy) d = np.zeros([x.shape[1]], dtype=np.bool) - return (to_matrix(x), d) + return (t[0], t[1], d) if np.dtype(str) in dtype_array or np.dtype(unicode) in dtype_array: raise TypeError('cannot convert matrices with string types') @@ -115,7 +124,10 @@ def to_matrix_with_info(x, dtype): catColumnIndices = [y.columns.get_loc(i) for i in catColumns] d[catColumnIndices] = 1 - return (to_matrix(y.apply(pd.to_numeric)), d) + # We'll have to force the second part of the tuple (whether or not to take + # ownership) to true. + t = to_matrix(y.apply(pd.to_numeric), dtype=dtype) + return (t[0], True, d) if isinstance(x, list): # Get the number of dimensions. @@ -126,7 +138,18 @@ def to_matrix_with_info(x, dtype): dims = len(x) d = np.zeros([dims]) - return (np.array(x, dtype=dtype), d) + out = np.array(x, dtype=dtype, copy=copy) # Try to avoid copy... + + # Since we don't have a great way to check if these are using the same + # memory location, we will probe manually (ugh). + oldval = x[0] + x[0] *= 2 + alias = False + if out[0] == x[0]: + alias = True + x[0] = oldval + + return (out, not alias, d) # If we got here, the type is not known. raise TypeError("given matrix is not a numpy ndarray or pandas DataFrame or "\ diff --git a/src/mlpack/bindings/python/mlpack/move.hpp b/src/mlpack/bindings/python/mlpack/move.hpp deleted file mode 100644 index b4a145b2d7..0000000000 --- a/src/mlpack/bindings/python/mlpack/move.hpp +++ /dev/null @@ -1,30 +0,0 @@ -/** - * @file move.hpp - * @author Ryan Curtin - * - * Utility function for Cython to use std::move. - */ -#ifndef MLPACK_BINDINGS_PYTHON_CYTHON_MOVE_HPP -#define MLPACK_BINDINGS_PYTHON_CYTHON_MOVE_HPP - -#include - -namespace mlpack { -namespace util { - -template -void MoveToPtr(T* dest, T& src) -{ - *(dest) = std::move(src); -} - -template -void MoveFromPtr(T& dest, T* src) -{ - dest = std::move(*src); -} - -} // namespace util -} // namespace mlpack - -#endif diff --git a/src/mlpack/bindings/python/print_class_defn.hpp b/src/mlpack/bindings/python/print_class_defn.hpp index 77148531b9..8bb4e0fdc2 100644 --- a/src/mlpack/bindings/python/print_class_defn.hpp +++ b/src/mlpack/bindings/python/print_class_defn.hpp @@ -108,7 +108,7 @@ void PrintClassDefn(const util::ParamData& d, const void* /* input */, void* /* output */) { - PrintClassDefn(d); + PrintClassDefn::type>(d); } } // namespace python diff --git a/src/mlpack/bindings/python/print_doc.hpp b/src/mlpack/bindings/python/print_doc.hpp index 08bd612da4..353e558a9a 100644 --- a/src/mlpack/bindings/python/print_doc.hpp +++ b/src/mlpack/bindings/python/print_doc.hpp @@ -39,7 +39,27 @@ void PrintDoc(const util::ParamData& d, oss << d.name << "_ ("; else oss << d.name << " ("; - oss << GetPythonType(d) << "): " << d.desc; + oss << GetPythonType::type>(d) << "): " + << d.desc; + + // Print a default, if possible. + if (!d.required) + { + if (d.cppType == "std::string") + { + oss << " Default value '" << boost::any_cast(d.value) + << "'."; + } + else if (d.cppType == "double") + { + oss << " Default value " << boost::any_cast(d.value) << "."; + } + else if (d.cppType == "int") + { + oss << " Default value " << boost::any_cast(d.value) << "."; + } + } + std::cout << util::HyphenateString(oss.str(), indent + 4); } diff --git a/src/mlpack/bindings/python/print_input_processing.hpp b/src/mlpack/bindings/python/print_input_processing.hpp index 533b81eb40..3cdf096c0e 100644 --- a/src/mlpack/bindings/python/print_input_processing.hpp +++ b/src/mlpack/bindings/python/print_input_processing.hpp @@ -31,6 +31,11 @@ void PrintInputProcessing( const typename boost::disable_if>>::type* = 0) { + // The copy_all_inputs parameter must be handled first, and therefore is + // outside the scope of this code. + if (d.name == "copy_all_inputs") + return; + const std::string prefix(indent, ' '); std::string def = "None"; @@ -104,7 +109,9 @@ void PrintInputProcessing( * * # Detect if the parameter was passed; set if so. * if param_name is not None: - * param_name_mat = arma_numpy.numpy_to_mat_d(param_name) + * param_name_tuple = to_matrix(param_name) + * param_name_mat = arma_numpy.numpy_to_mat_d(param_name_tuple[0], + * param_name_tuple[1]) * SetParam[mat]( 'param_name', dereference(param_name_mat)) * CLI.SetPassed( 'param_name') */ @@ -114,27 +121,33 @@ void PrintInputProcessing( { std::cout << prefix << "if " << d.name << " is not None:" << std::endl; + std::cout << prefix << " " << d.name << "_tuple = to_matrix(" << d.name + << ", dtype=" << GetNumpyType() << ", " + << "copy=CLI.HasParam('copy_all_inputs'))" << std::endl; std::cout << prefix << " " << d.name << "_mat = arma_numpy.numpy_to_" - << GetArmaType() << "_" << GetNumpyTypeChar() << "(to_matrix(" - << d.name << ", " << "dtype=" << GetNumpyType() - << "))" << std::endl; + << GetArmaType() << "_" << GetNumpyTypeChar() << "(" << d.name + << "_tuple[0], " << d.name << "_tuple[1])" << std::endl; std::cout << prefix << " SetParam[" << GetCythonType(d) << "]( '" << d.name << "', dereference(" << d.name << "_mat))" << std::endl; std::cout << prefix << " CLI.SetPassed( '" << d.name << "')" << std::endl; + std::cout << prefix << " del " << d.name << "_mat"; } else { + std::cout << prefix << d.name << "_tuple = to_matrix(" << d.name + << ", dtype=" << GetNumpyType() << ", " + << "copy=CLI.HasParam('copy_all_inputs'))" << std::endl; std::cout << prefix << d.name << "_mat = arma_numpy.numpy_to_" - << GetArmaType() << "_" << GetNumpyTypeChar() << "(to_matrix(" - << d.name << ", " << "dtype=" << GetNumpyType() - << "))" << std::endl; + << GetArmaType() << "_" << GetNumpyTypeChar() << "(" << d.name + << "_tuple[0], " << d.name << "_tuple[1])" << std::endl; std::cout << prefix << "SetParam[" << GetCythonType(d) << "]( '" << d.name << "', dereference(" << d.name << "_mat))" << std::endl; std::cout << prefix << "CLI.SetPassed( '" << d.name << "')" << std::endl; + std::cout << prefix << "del " << d.name << "_mat"; } std::cout << std::endl; } @@ -161,12 +174,12 @@ void PrintInputProcessing( * # Detect if the parameter was passed; set if so. * if param_name is not None: * try: - * MoveFromPtr[Model](CLI.GetParam[Model]('param_name'), - * ( param_name).modelptr) + * SetParamPtr[Model]('param_name', ( param_name).modelptr, + * CLI.HasParam('copy_all_inputs')) * except TypeError as e: * if type(param_name).__name__ == "ModelType": - * MoveFromPtr[Model](CLI.GetParam[Model]('param_name'), - * ( param_name).modelptr) + * SetParamPtr[Model]('param_name', ( param_name).modelptr, + * CLI.HasParam('copy_all_inputs')) * else: * raise e * CLI.SetPassed( 'param_name') @@ -177,15 +190,15 @@ void PrintInputProcessing( { std::cout << prefix << "if " << d.name << " is not None:" << std::endl; std::cout << prefix << " try:" << std::endl; - std::cout << prefix << " MoveFromPtr[" << strippedType - << "](CLI.GetParam[" << strippedType << "]('" << d.name << "'), (<" - << strippedType << "Type?> " << d.name << ").modelptr)" << std::endl; + std::cout << prefix << " SetParamPtr[" << strippedType << "]('" << d.name + << "', (<" << strippedType << "Type?> " << d.name << ").modelptr, " + << "CLI.HasParam('copy_all_inputs'))" << std::endl; std::cout << prefix << " except TypeError as e:" << std::endl; std::cout << prefix << " if type(" << d.name << ").__name__ == '" << strippedType << "Type':" << std::endl; - std::cout << prefix << " MoveFromPtr[" << strippedType - << "](CLI.GetParam[" << strippedType << "]('" << d.name << "'), (<" - << strippedType << "Type> " << d.name << ").modelptr)" << std::endl; + std::cout << prefix << " SetParamPtr[" << strippedType << "]('" + << d.name << "', (<" << strippedType << "Type> " << d.name + << ").modelptr, CLI.HasParam('copy_all_inputs'))" << std::endl; std::cout << prefix << " else:" << std::endl; std::cout << prefix << " raise e" << std::endl; std::cout << prefix << " CLI.SetPassed( '" << d.name << "')" @@ -194,15 +207,15 @@ void PrintInputProcessing( else { std::cout << prefix << "try:" << std::endl; - std::cout << prefix << " MoveFromPtr[" << strippedType << "](CLI.GetParam[" - << strippedType << "]('" << d.name << "'), (<" << strippedType - << "Type?> " << d.name << ").modelptr)" << std::endl; + std::cout << prefix << " SetParamPtr[" << strippedType << "]('" << d.name + << "', (<" << strippedType << "Type?> " << d.name << ").modelptr, " + << "CLI.HasParam('copy_all_inputs'))" << std::endl; std::cout << prefix << "except TypeError as e:" << std::endl; std::cout << prefix << " if type(" << d.name << ").__name__ == '" << strippedType << "Type':" << std::endl; - std::cout << prefix << " MoveFromPtr[" << strippedType - << "](CLI.GetParam[" << strippedType << "]('" << d.name << "'), (<" - << strippedType << "Type> " << d.name << ").modelptr)" << std::endl; + std::cout << prefix << " SetParamPtr[" << strippedType << "]('" << d.name + << "', (<" << strippedType << "Type> " << d.name << ").modelptr, " + << "CLI.HasParam('copy_all_inputs'))" << std::endl; std::cout << prefix << " else:" << std::endl; std::cout << prefix << " raise e" << std::endl; std::cout << prefix << "CLI.SetPassed( '" << d.name << "')" @@ -240,30 +253,34 @@ void PrintInputProcessing( { std::cout << prefix << "if " << d.name << " is not None:" << std::endl; std::cout << prefix << " " << d.name << "_tuple = to_matrix_with_info(" - << d.name << ", dtype=np.double)" << std::endl; + << d.name << ", dtype=np.double, copy=CLI.HasParam('copy_all_inputs'))" + << std::endl; std::cout << prefix << " " << d.name << "_mat = arma_numpy.numpy_to_mat_d(" - << d.name << "_tuple[0])" << std::endl; - std::cout << prefix << " " << d.name << "_dims = " << d.name << "_tuple[1]" + << d.name << "_tuple[0], " << d.name << "_tuple[1])" << std::endl; + std::cout << prefix << " " << d.name << "_dims = " << d.name << "_tuple[2]" << std::endl; std::cout << prefix << " SetParamWithInfo[arma.Mat[double]](" << " '" << d.name << "', dereference(" << d.name << "_mat), " << d.name << "_dims.data)" << std::endl; std::cout << prefix << " CLI.SetPassed( '" << d.name << "')" << std::endl; + std::cout << prefix << " del " << d.name << "_mat" << std::endl; } else { std::cout << prefix << d.name << "_tuple = to_matrix_with_info(" << d.name - << ", dtype=np.double)" << std::endl; + << ", dtype=np.double, copy=CLI.HasParam('copy_all_inputs'))" + << std::endl; std::cout << prefix << d.name << "_mat = arma_numpy.numpy_to_mat_d(" - << d.name << "_tuple[0])" << std::endl; - std::cout << prefix << d.name << "_dims = " << d.name << "_tuple[1]" + << d.name << "_tuple[0], " << d.name << "_tuple[1])" << std::endl; + std::cout << prefix << d.name << "_dims = " << d.name << "_tuple[2]" << std::endl; std::cout << prefix << "SetParamWithInfo[arma.Mat[double]](" << " '" << d.name << "', dereference(" << d.name << "_mat), " << d.name << "_dims.data)" << std::endl; std::cout << prefix << "CLI.SetPassed( '" << d.name << "')" << std::endl; + std::cout << prefix << "del " << d.name << "_mat" << std::endl; } std::cout << std::endl; } @@ -284,7 +301,8 @@ void PrintInputProcessing(const util::ParamData& d, const void* input, void* /* output */) { - PrintInputProcessing(d, *((size_t*) input)); + PrintInputProcessing::type>(d, + *((size_t*) input)); } } // namespace python diff --git a/src/mlpack/bindings/python/print_output_processing.hpp b/src/mlpack/bindings/python/print_output_processing.hpp index d20da67f4e..00833ceceb 100644 --- a/src/mlpack/bindings/python/print_output_processing.hpp +++ b/src/mlpack/bindings/python/print_output_processing.hpp @@ -180,13 +180,47 @@ void PrintOutputProcessing( * This gives us code like: * * result = ModelType() - * MoveToPtr[Model](( model).modelptr), - * CLI.GetParam[Model]('name')) + * ( result).modelptr = GetParamPtr[Model]('name') */ std::cout << prefix << "result = " << strippedType << "Type()" << std::endl; - std::cout << prefix << "MoveToPtr[" << strippedType << "]((<" - << strippedType << "Type?> result).modelptr, CLI.GetParam[" - << strippedType << "]('" << d.name << "'))" << std::endl; + std::cout << prefix << "(<" << strippedType << "Type?> result).modelptr = " + << "GetParamPtr[" << strippedType << "]('" << d.name << "')" + << std::endl; + + /** + * But we also have to check to ensure there aren't any input model + * parameters of the same type that could have the same model pointer. + * So we need to loop through all input parameters that have the same type, + * and double-check. + */ + std::map& parameters = CLI::Parameters(); + for (auto it = parameters.begin(); it != parameters.end(); ++it) + { + // Is it an input parameter of the same type? + const util::ParamData& data = it->second; + if (data.input && data.cppType == d.cppType && data.required) + { + std::cout << prefix << "if (<" << strippedType + << "Type> result).modelptr" << d.name << " == (<" << strippedType + << "Type> " << data.name << ").modelptr:" << std::endl; + std::cout << prefix << " (<" << strippedType + << "Type> result).modelptr = <" << strippedType << "*> 0" + << std::endl; + std::cout << prefix << " result = " << data.name << std::endl; + } + else if (data.input && data.cppType == d.cppType) + { + std::cout << prefix << "if " << data.name << " is not None:" + << std::endl; + std::cout << prefix << " if (<" << strippedType + << "Type> result).modelptr" << d.name << " == (<" << strippedType + << "Type> " << data.name << ").modelptr:" << std::endl; + std::cout << prefix << " (<" << strippedType + << "Type> result).modelptr = <" << strippedType << "*> 0" + << std::endl; + std::cout << prefix << " result = " << data.name << std::endl; + } + } } else { @@ -194,15 +228,50 @@ void PrintOutputProcessing( * This gives us code like: * * result['name'] = ModelType() - * MoveToPtr[Model*](( result['name']).modelptr), - * CLI.GetParam[Model]('name')) + * ( result['name']).modelptr = GetParamPtr[Model]('name')) */ std::cout << prefix << "result['" << d.name << "'] = " << strippedType << "Type()" << std::endl; - std::cout << prefix << "MoveToPtr[" << strippedType << "]((<" - << strippedType << "Type?>" << " result['" << d.name - << "']).modelptr, CLI.GetParam[" << strippedType << "]('" - << d.name << "'))" << std::endl; + std::cout << prefix << "(<" << strippedType << "Type?> result['" << d.name + << "']).modelptr = GetParamPtr[" << strippedType << "]('" << d.name + << "')" << std::endl; + + /** + * But we also have to check to ensure there aren't any input model + * parameters of the same type that could have the same model pointer. + * So we need to loop through all input parameters that have the same type, + * and double-check. + */ + std::map& parameters = CLI::Parameters(); + for (auto it = parameters.begin(); it != parameters.end(); ++it) + { + // Is it an input parameter of the same type? + const util::ParamData& data = it->second; + if (data.input && data.cppType == d.cppType && data.required) + { + std::cout << prefix << "if (<" << strippedType << "Type> result['" + << d.name << "']).modelptr == (<" << strippedType << "Type> " + << data.name << ").modelptr:" << std::endl; + std::cout << prefix << " (<" << strippedType << "Type> result['" + << d.name << "']).modelptr = <" << strippedType << "*> 0" + << std::endl; + std::cout << prefix << " result['" << d.name << "'] = " << data.name + << std::endl; + } + else if (data.input && data.cppType == d.cppType) + { + std::cout << prefix << "if " << data.name << " is not None:" + << std::endl; + std::cout << prefix << " if (<" << strippedType << "Type> result['" + << d.name << "']).modelptr == (<" << strippedType << "Type> " + << data.name << ").modelptr:" << std::endl; + std::cout << prefix << " (<" << strippedType << "Type> result['" + << d.name << "']).modelptr = <" << strippedType << "*> 0" + << std::endl; + std::cout << prefix << " result['" << d.name << "'] = " << data.name + << std::endl; + } + } } } @@ -227,7 +296,8 @@ void PrintOutputProcessing(const util::ParamData& d, { std::tuple* tuple = (std::tuple*) input; - PrintOutputProcessing(d, std::get<0>(*tuple), std::get<1>(*tuple)); + PrintOutputProcessing::type>(d, + std::get<0>(*tuple), std::get<1>(*tuple)); } } // namespace python diff --git a/src/mlpack/bindings/python/print_pyx.cpp b/src/mlpack/bindings/python/print_pyx.cpp index cb92389366..498bd07940 100644 --- a/src/mlpack/bindings/python/print_pyx.cpp +++ b/src/mlpack/bindings/python/print_pyx.cpp @@ -71,10 +71,10 @@ void PrintPYX(const ProgramDoc& programInfo, cout << "cimport arma" << endl; cout << "cimport arma_numpy" << endl; cout << "from cli cimport CLI" << endl; - cout << "from cli cimport SetParam, SetParamWithInfo" << endl; + cout << "from cli cimport SetParam, SetParamPtr, SetParamWithInfo, " + << "GetParamPtr" << endl; cout << "from cli cimport EnableVerbose, DisableVerbose, DisableBacktrace, " << "ResetTimers, EnableTimers" << endl; - cout << "from cli cimport MoveFromPtr, MoveToPtr" << endl; cout << "from matrix_utils import to_matrix, to_matrix_with_info" << endl; cout << "from serialization cimport SerializeIn, SerializeOut" << endl; cout << endl; @@ -177,7 +177,14 @@ void PrintPYX(const ProgramDoc& programInfo, cout << " DisableVerbose()" << endl; // Restore the parameters. - cout << " CLI.RestoreSettings(\"" << programInfo.programName << "\")"; + cout << " CLI.RestoreSettings(\"" << programInfo.programName << "\")" + << endl; + + // Determine whether or not we need to copy parameters. + cout << " if copy_all_inputs:" << endl; + cout << " SetParam[bool]( 'copy_all_inputs', " + << "copy_all_inputs)" << endl; + cout << " CLI.SetPassed( 'copy_all_inputs')" << endl; // Do any input processing. for (size_t i = 0; i < inputOptions.size(); ++i) diff --git a/src/mlpack/bindings/python/py_option.hpp b/src/mlpack/bindings/python/py_option.hpp index 60eaf3a1c4..b25be68a4b 100644 --- a/src/mlpack/bindings/python/py_option.hpp +++ b/src/mlpack/bindings/python/py_option.hpp @@ -58,8 +58,8 @@ class PyOption data.required = required; data.input = input; data.loaded = false; - // Only "verbose" will be persistent. - if (identifier == "verbose") + // Only "verbose" and "copy_all_inputs" will be persistent. + if (identifier == "verbose" || identifier == "copy_all_inputs") data.persistent = true; else data.persistent = false; diff --git a/src/mlpack/bindings/python/tests/dataset_info_test.py b/src/mlpack/bindings/python/tests/dataset_info_test.py index dd14d73071..e72c529d00 100644 --- a/src/mlpack/bindings/python/tests/dataset_info_test.py +++ b/src/mlpack/bindings/python/tests/dataset_info_test.py @@ -23,7 +23,7 @@ class TestToMatrix(unittest.TestCase): """ d = pd.DataFrame(np.random.randn(100, 4), columns=list('abcd')) - m = to_matrix(d) + m, _ = to_matrix(d) self.assertTrue(isinstance(m, np.ndarray)) self.assertEqual(m.shape[0], 100) @@ -40,7 +40,7 @@ class TestToMatrix(unittest.TestCase): """ d = pd.DataFrame({'a': range(5)}) - m = to_matrix(d) + m, _ = to_matrix(d) self.assertTrue(isinstance(m, np.ndarray)) self.assertEqual(m.shape[0], 5) @@ -58,7 +58,7 @@ class TestToMatrix(unittest.TestCase): self.assertEqual(d['a'].dtype, int) self.assertEqual(d['b'].dtype, np.dtype(np.double)) - m = to_matrix(d) + m, _ = to_matrix(d) self.assertTrue(isinstance(m, np.ndarray)) self.assertEqual(m.dtype, np.dtype(np.double)) @@ -79,7 +79,7 @@ class TestToMatrix(unittest.TestCase): [0.07, 0.08, 0.09], [0.10, 0.11, 0.12]] - m = to_matrix(a) + m, _ = to_matrix(a) self.assertTrue(isinstance(m, np.ndarray)) self.assertEqual(m.dtype, np.dtype(np.double)) @@ -100,7 +100,7 @@ class TestToMatrix(unittest.TestCase): [0.07, 0.08, 9], [0.10, 0.11, 12]] - m = to_matrix(a) + m, _ = to_matrix(a) self.assertTrue(isinstance(m, np.ndarray)) self.assertEqual(m.dtype, np.dtype(np.double)) @@ -116,7 +116,7 @@ class TestToMatrix(unittest.TestCase): Make sure we can convert a numpy matrix without copying anything. """ m1 = np.random.randn(100, 5) - m2 = to_matrix(m1) + m2, _ = to_matrix(m1) self.assertTrue(isinstance(m2, np.ndarray)) self.assertEqual(m2.dtype, np.dtype(np.double)) @@ -144,7 +144,7 @@ class TestToMatrixWithInfo(unittest.TestCase): """ d = pd.DataFrame(np.random.randn(100, 4), columns=list('abcd')) - m, dims = to_matrix_with_info(d, np.double) + m, _, dims = to_matrix_with_info(d, np.double) self.assertTrue(isinstance(m, np.ndarray)) self.assertEqual(m.shape[0], 100) @@ -167,7 +167,7 @@ class TestToMatrixWithInfo(unittest.TestCase): """ d = pd.DataFrame({'a': range(5)}) - m, dims = to_matrix_with_info(d, np.double) + m, _, dims = to_matrix_with_info(d, np.double) self.assertTrue(isinstance(m, np.ndarray)) self.assertEqual(m.shape[0], 5) @@ -188,7 +188,7 @@ class TestToMatrixWithInfo(unittest.TestCase): self.assertEqual(d['a'].dtype, int) self.assertEqual(d['b'].dtype, np.dtype(np.double)) - m, dims = to_matrix_with_info(d, np.double) + m, _, dims = to_matrix_with_info(d, np.double) self.assertTrue(isinstance(m, np.ndarray)) self.assertEqual(m.dtype, np.dtype(np.double)) @@ -213,7 +213,7 @@ class TestToMatrixWithInfo(unittest.TestCase): [0.07, 0.08, 0.09], [0.10, 0.11, 0.12]] - m, dims = to_matrix_with_info(a, np.double) + m, _, dims = to_matrix_with_info(a, np.double) self.assertTrue(isinstance(m, np.ndarray)) self.assertEqual(m.dtype, np.dtype(np.double)) @@ -239,7 +239,7 @@ class TestToMatrixWithInfo(unittest.TestCase): [0.07, 0.08, 9], [0.10, 0.11, 12]] - m, dims = to_matrix_with_info(a, np.double) + m, _, dims = to_matrix_with_info(a, np.double) self.assertTrue(isinstance(m, np.ndarray)) self.assertEqual(m.dtype, np.dtype(np.double)) @@ -260,7 +260,7 @@ class TestToMatrixWithInfo(unittest.TestCase): Make sure we can convert a numpy matrix without copying anything. """ m1 = np.random.randn(100, 5) - m2, dims = to_matrix_with_info(m1, np.double) + m2, _, dims = to_matrix_with_info(m1, np.double) self.assertTrue(isinstance(m2, np.ndarray)) self.assertEqual(m2.dtype, np.dtype(np.double)) @@ -284,7 +284,7 @@ class TestToMatrixWithInfo(unittest.TestCase): d = pd.DataFrame({"A": ["a", "b", "c", "a"] }) d["A"] = d["A"].astype('category') # Convert to categorical. - m, dims = to_matrix_with_info(d, np.double) + m, _, dims = to_matrix_with_info(d, np.double) self.assertTrue(isinstance(m, np.ndarray)) self.assertEqual(m.dtype, np.dtype(np.double)) diff --git a/src/mlpack/bindings/python/tests/test_python_binding.py b/src/mlpack/bindings/python/tests/test_python_binding.py index b72ecd34f2..bd10b83a2e 100644 --- a/src/mlpack/bindings/python/tests/test_python_binding.py +++ b/src/mlpack/bindings/python/tests/test_python_binding.py @@ -7,6 +7,7 @@ Test that passing types to Python bindings works successfully. import unittest import pandas as pd import numpy as np +import copy from mlpack.test_python_binding import test_python_binding @@ -94,11 +95,35 @@ class TestPythonBinding(unittest.TestCase): and the fifth forgotten. """ x = np.random.rand(100, 5); + z = copy.copy(x) output = test_python_binding(string_in='hello', int_in=12, double_in=4.0, - matrix_in=x) + matrix_in=z) + + self.assertEqual(output['matrix_out'].shape[0], 100) + self.assertEqual(output['matrix_out'].shape[1], 4) + self.assertEqual(output['matrix_out'].dtype, np.double) + for i in [0, 1, 3]: + for j in range(100): + self.assertEqual(x[j, i], output['matrix_out'][j, i]) + + for j in range(100): + self.assertEqual(2 * x[j, 2], output['matrix_out'][j, 2]) + + def testNumpyMatrixForceCopy(self): + """ + The matrix we pass in, we should get back with the third dimension doubled + and the fifth forgotten. + """ + x = np.random.rand(100, 5); + + output = test_python_binding(string_in='hello', + int_in=12, + double_in=4.0, + matrix_in=x, + copy_all_inputs=True) self.assertEqual(output['matrix_out'].shape[0], 100) self.assertEqual(output['matrix_out'].shape[1], 4) @@ -139,16 +164,71 @@ class TestPythonBinding(unittest.TestCase): self.assertEqual(output['matrix_out'][2, 2], 26) self.assertEqual(output['matrix_out'][2, 3], 14) + def testArraylikeMatrixForceCopy(self): + """ + Test that we can pass an arraylike matrix. + """ + x = [[1, 2, 3, 4, 5], + [6, 7, 8, 9, 10], + [11, 12, 13, 14, 15]] + + output = test_python_binding(string_in='hello', + int_in=12, + double_in=4.0, + matrix_in=x, + copy_all_inputs=True) + + self.assertEqual(output['matrix_out'].shape[0], 3) + self.assertEqual(output['matrix_out'].shape[1], 4) + self.assertEqual(len(x), 3) + self.assertEqual(len(x[0]), 5) + self.assertEqual(output['matrix_out'].dtype, np.double) + self.assertEqual(output['matrix_out'][0, 0], 1) + self.assertEqual(output['matrix_out'][0, 1], 2) + self.assertEqual(output['matrix_out'][0, 2], 6) + self.assertEqual(output['matrix_out'][0, 3], 4) + self.assertEqual(output['matrix_out'][1, 0], 6) + self.assertEqual(output['matrix_out'][1, 1], 7) + self.assertEqual(output['matrix_out'][1, 2], 16) + self.assertEqual(output['matrix_out'][1, 3], 9) + self.assertEqual(output['matrix_out'][2, 0], 11) + self.assertEqual(output['matrix_out'][2, 1], 12) + self.assertEqual(output['matrix_out'][2, 2], 26) + self.assertEqual(output['matrix_out'][2, 3], 14) + def testNumpyUmatrix(self): """ Same as testNumpyMatrix() but with an unsigned matrix. """ x = np.random.randint(0, high=500, size=[100, 5]) + z = copy.copy(x) + + output = test_python_binding(string_in='hello', + int_in=12, + double_in=4.0, + umatrix_in=z) + + self.assertEqual(output['umatrix_out'].shape[0], 100) + self.assertEqual(output['umatrix_out'].shape[1], 4) + self.assertEqual(output['umatrix_out'].dtype, np.long) + for i in [0, 1, 3]: + for j in range(100): + self.assertEqual(x[j, i], output['umatrix_out'][j, i]) + + for j in range(100): + self.assertEqual(2 * x[j, 2], output['umatrix_out'][j, 2]) + + def testNumpyUmatrixForceCopy(self): + """ + Same as testNumpyMatrix() but with an unsigned matrix. + """ + x = np.random.randint(0, high=500, size=[100, 5]) output = test_python_binding(string_in='hello', int_in=12, double_in=4.0, - umatrix_in=x) + umatrix_in=x, + copy_all_inputs=True) self.assertEqual(output['umatrix_out'].shape[0], 100) self.assertEqual(output['umatrix_out'].shape[1], 4) @@ -189,16 +269,67 @@ class TestPythonBinding(unittest.TestCase): self.assertEqual(output['umatrix_out'][2, 2], 26) self.assertEqual(output['umatrix_out'][2, 3], 14) + def testArraylikeUmatrixForceCopy(self): + """ + Test that we can pass an arraylike unsigned matrix. + """ + x = [[1, 2, 3, 4, 5], + [6, 7, 8, 9, 10], + [11, 12, 13, 14, 15]] + + output = test_python_binding(string_in='hello', + int_in=12, + double_in=4.0, + umatrix_in=x, + copy_all_inputs=True) + + self.assertEqual(output['umatrix_out'].shape[0], 3) + self.assertEqual(output['umatrix_out'].shape[1], 4) + self.assertEqual(len(x), 3) + self.assertEqual(len(x[0]), 5) + self.assertEqual(output['umatrix_out'].dtype, np.long) + self.assertEqual(output['umatrix_out'][0, 0], 1) + self.assertEqual(output['umatrix_out'][0, 1], 2) + self.assertEqual(output['umatrix_out'][0, 2], 6) + self.assertEqual(output['umatrix_out'][0, 3], 4) + self.assertEqual(output['umatrix_out'][1, 0], 6) + self.assertEqual(output['umatrix_out'][1, 1], 7) + self.assertEqual(output['umatrix_out'][1, 2], 16) + self.assertEqual(output['umatrix_out'][1, 3], 9) + self.assertEqual(output['umatrix_out'][2, 0], 11) + self.assertEqual(output['umatrix_out'][2, 1], 12) + self.assertEqual(output['umatrix_out'][2, 2], 26) + self.assertEqual(output['umatrix_out'][2, 3], 14) + def testCol(self): """ Test a column vector input parameter. """ x = np.random.rand(100) + z = copy.copy(x) + + output = test_python_binding(string_in='hello', + int_in=12, + double_in=4.0, + col_in=z) + + self.assertEqual(output['col_out'].shape[0], 100) + self.assertEqual(output['col_out'].dtype, np.double) + + for i in range(100): + self.assertEqual(output['col_out'][i], x[i] * 2) + + def testColForceCopy(self): + """ + Test a column vector input parameter. + """ + x = np.random.rand(100) output = test_python_binding(string_in='hello', int_in=12, double_in=4.0, - col_in=x) + col_in=x, + copy_all_inputs=True) self.assertEqual(output['col_out'].shape[0], 100) self.assertEqual(output['col_out'].dtype, np.double) @@ -211,11 +342,29 @@ class TestPythonBinding(unittest.TestCase): Test an unsigned column vector input parameter. """ x = np.random.randint(0, high=500, size=100) + z = copy.copy(x) output = test_python_binding(string_in='hello', int_in=12, double_in=4.0, - ucol_in=x) + ucol_in=z) + + self.assertEqual(output['ucol_out'].shape[0], 100) + self.assertEqual(output['ucol_out'].dtype, np.long) + for i in range(100): + self.assertEqual(output['ucol_out'][i], x[i] * 2) + + def testUcolForceCopy(self): + """ + Test an unsigned column vector input parameter. + """ + x = np.random.randint(0, high=500, size=100) + + output = test_python_binding(string_in='hello', + int_in=12, + double_in=4.0, + ucol_in=x, + copy_all_inputs=True) self.assertEqual(output['ucol_out'].shape[0], 100) self.assertEqual(output['ucol_out'].dtype, np.long) @@ -227,11 +376,30 @@ class TestPythonBinding(unittest.TestCase): Test a row vector input parameter. """ x = np.random.rand(100) + z = copy.copy(x) output = test_python_binding(string_in='hello', int_in=12, double_in=4.0, - row_in=x) + row_in=z) + + self.assertEqual(output['row_out'].shape[0], 100) + self.assertEqual(output['row_out'].dtype, np.double) + + for i in range(100): + self.assertEqual(output['row_out'][i], x[i] * 2) + + def testRowForceCopy(self): + """ + Test a row vector input parameter. + """ + x = np.random.rand(100) + + output = test_python_binding(string_in='hello', + int_in=12, + double_in=4.0, + row_in=x, + copy_all_inputs=True) self.assertEqual(output['row_out'].shape[0], 100) self.assertEqual(output['row_out'].dtype, np.double) @@ -244,11 +412,30 @@ class TestPythonBinding(unittest.TestCase): Test an unsigned row vector input parameter. """ x = np.random.randint(0, high=500, size=100) + z = copy.copy(x) output = test_python_binding(string_in='hello', int_in=12, double_in=4.0, - urow_in=x) + urow_in=z) + + self.assertEqual(output['urow_out'].shape[0], 100) + self.assertEqual(output['urow_out'].dtype, np.long) + + for i in range(100): + self.assertEqual(output['urow_out'][i], x[i] * 2) + + def testUrowForceCopy(self): + """ + Test an unsigned row vector input parameter. + """ + x = np.random.randint(0, high=500, size=100) + + output = test_python_binding(string_in='hello', + int_in=12, + double_in=4.0, + urow_in=x, + copy_all_inputs=True) self.assertEqual(output['urow_out'].shape[0], 100) self.assertEqual(output['urow_out'].dtype, np.long) @@ -261,11 +448,31 @@ class TestPythonBinding(unittest.TestCase): Test that we can pass a matrix with all numeric features. """ x = np.random.rand(100, 10) + z = copy.copy(x) output = test_python_binding(string_in='hello', int_in=12, double_in=4.0, - matrix_and_info_in=x) + matrix_and_info_in=z) + + self.assertEqual(output['matrix_and_info_out'].shape[0], 100) + self.assertEqual(output['matrix_and_info_out'].shape[1], 10) + + for i in range(10): + for j in range(100): + self.assertEqual(output['matrix_and_info_out'][j, i], x[j, i] * 2.0) + + def testMatrixAndInfoNumpyForceCopy(self): + """ + Test that we can pass a matrix with all numeric features. + """ + x = np.random.rand(100, 10) + + output = test_python_binding(string_in='hello', + int_in=12, + double_in=4.0, + matrix_and_info_in=x, + copy_all_inputs=True) self.assertEqual(output['matrix_and_info_out'].shape[0], 100) self.assertEqual(output['matrix_and_info_out'].shape[1], 10) @@ -281,11 +488,38 @@ class TestPythonBinding(unittest.TestCase): x = pd.DataFrame(np.random.rand(10, 4), columns=list('abcd')) x['e'] = pd.Series(['a', 'b', 'c', 'd', 'a', 'b', 'e', 'c', 'a', 'b'], dtype='category') + z = copy.copy(x) output = test_python_binding(string_in='hello', int_in=12, double_in=4.0, - matrix_and_info_in=x) + matrix_and_info_in=z) + + self.assertEqual(output['matrix_and_info_out'].shape[0], 10) + self.assertEqual(output['matrix_and_info_out'].shape[1], 5) + + cols = list('abcde') + + for i in range(4): + for j in range(10): + self.assertEqual(output['matrix_and_info_out'][j, i], z[cols[i]][j] * 2) + + for j in range(10): + self.assertEqual(output['matrix_and_info_out'][j, 4], z[cols[4]][j]) + + def testMatrixAndInfoPandasForceCopy(self): + """ + Test that we can pass a matrix with some categorical features. + """ + x = pd.DataFrame(np.random.rand(10, 4), columns=list('abcd')) + x['e'] = pd.Series(['a', 'b', 'c', 'd', 'a', 'b', 'e', 'c', 'a', 'b'], + dtype='category') + + output = test_python_binding(string_in='hello', + int_in=12, + double_in=4.0, + matrix_and_info_in=x, + copy_all_inputs=True) self.assertEqual(output['matrix_and_info_out'].shape[0], 10) self.assertEqual(output['matrix_and_info_out'].shape[1], 5) @@ -345,5 +579,29 @@ class TestPythonBinding(unittest.TestCase): self.assertEqual(output2['model_bw_out'], 20.0) + def testModelForceCopy(self): + """ + First create a GaussianKernel object, then send it back and make sure we get + the right double value. + """ + output = test_python_binding(string_in='hello', + int_in=12, + double_in=4.0, + build_model=True) + + output2 = test_python_binding(string_in='hello', + int_in=12, + double_in=4.0, + model_in=output['model_out'], + copy_all_inputs=True) + + output3 = test_python_binding(string_in='hello', + int_in=12, + double_in=4.0, + model_in=output['model_out']) + + self.assertEqual(output2['model_bw_out'], 20.0) + self.assertEqual(output3['model_bw_out'], 20.0) + if __name__ == '__main__': unittest.main() diff --git a/src/mlpack/bindings/python/tests/test_python_binding_main.cpp b/src/mlpack/bindings/python/tests/test_python_binding_main.cpp index 8be2f4947a..9af5a26693 100644 --- a/src/mlpack/bindings/python/tests/test_python_binding_main.cpp +++ b/src/mlpack/bindings/python/tests/test_python_binding_main.cpp @@ -172,13 +172,13 @@ static void mlpackMain() // If we got a request to build a model, then build it. if (CLI::HasParam("build_model")) { - CLI::GetParam("model_out") = GaussianKernel(10.0); + CLI::GetParam("model_out") = new GaussianKernel(10.0); } // If we got an input model, double the bandwidth and output that. if (CLI::HasParam("model_in")) { CLI::GetParam("model_bw_out") = - CLI::GetParam("model_in").Bandwidth() * 2.0; + CLI::GetParam("model_in")->Bandwidth() * 2.0; } } diff --git a/src/mlpack/bindings/tests/CMakeLists.txt b/src/mlpack/bindings/tests/CMakeLists.txt index 5955a0a514..70b9928f91 100644 --- a/src/mlpack/bindings/tests/CMakeLists.txt +++ b/src/mlpack/bindings/tests/CMakeLists.txt @@ -1,8 +1,12 @@ # Define the files we need to compile. # Anything not in this list will not be compiled into mlpack. set(SOURCES + clean_memory.hpp + clean_memory.cpp test_option.hpp ignore_check.hpp + delete_allocated_memory.hpp + get_allocated_memory.hpp get_param.hpp get_printable_param.hpp get_printable_param_impl.hpp diff --git a/src/mlpack/bindings/tests/clean_memory.cpp b/src/mlpack/bindings/tests/clean_memory.cpp new file mode 100644 index 0000000000..dd40020177 --- /dev/null +++ b/src/mlpack/bindings/tests/clean_memory.cpp @@ -0,0 +1,54 @@ +/** + * @file clean_memory.cpp + * @author Ryan Curtin + * + * Delete any pointers held by the CLI object. + */ +#include "clean_memory.hpp" + +#include + +namespace mlpack { +namespace bindings { +namespace tests { + +/** + * Delete any pointers held by the CLI object. + */ +void CleanMemory() +{ + // If we are holding any pointers, then we "own" them. But we may hold the + // same pointer twice, so we have to be careful to not delete it multiple + // times. + std::unordered_map memoryAddresses; + auto it = CLI::Parameters().begin(); + while (it != CLI::Parameters().end()) + { + const util::ParamData& data = it->second; + + void* result; + CLI::GetSingleton().functionMap[data.tname]["GetAllocatedMemory"](data, + NULL, (void*) &result); + if (result != NULL && memoryAddresses.count(result) == 0) + memoryAddresses[result] = &data; + + ++it; + } + + // Now we have all the unique addresses that need to be deleted. + std::unordered_map::const_iterator it2; + it2 = memoryAddresses.begin(); + while (it2 != memoryAddresses.end()) + { + const util::ParamData& data = *(it2->second); + + CLI::GetSingleton().functionMap[data.tname]["DeleteAllocatedMemory"](data, + NULL, NULL); + + ++it2; + } +} + +} // namespace tests +} // namespace bindings +} // namespace mlpack diff --git a/src/mlpack/bindings/tests/clean_memory.hpp b/src/mlpack/bindings/tests/clean_memory.hpp new file mode 100644 index 0000000000..1035b7f5cb --- /dev/null +++ b/src/mlpack/bindings/tests/clean_memory.hpp @@ -0,0 +1,24 @@ +/** + * @file clean_memory.hpp + * @author Ryan Curtin + * + * Delete any unique pointers that are held by the CLI object. This is similar + * to the code in end_program.hpp. + */ +#ifndef MLPACK_BINDINGS_TESTS_CLEAN_MEMORY_HPP +#define MLPACK_BINDINGS_TESTS_CLEAN_MEMORY_HPP + +namespace mlpack { +namespace bindings { +namespace tests { + +/** + * Delete any unique pointers that are held by the CLI object. + */ +void CleanMemory(); + +} // namespace tests +} // namespace bindings +} // namespace mlpack + +#endif diff --git a/src/mlpack/bindings/tests/delete_allocated_memory.hpp b/src/mlpack/bindings/tests/delete_allocated_memory.hpp new file mode 100644 index 0000000000..a0541a19e6 --- /dev/null +++ b/src/mlpack/bindings/tests/delete_allocated_memory.hpp @@ -0,0 +1,56 @@ +/** + * @file delete_allocated_memory.hpp + * @author Ryan Curtin + * + * If any memory has been allocated by the parameter, delete it. + */ +#ifndef MLPACK_BINDINGS_CLI_DELETE_ALLOCATED_MEMORY_HPP +#define MLPACK_BINDINGS_CLI_DELETE_ALLOCATED_MEMORY_HPP + +#include + +namespace mlpack { +namespace bindings { +namespace tests { + +template +void DeleteAllocatedMemoryImpl( + const util::ParamData& /* d */, + const typename boost::disable_if>::type* = 0, + const typename boost::disable_if>::type* = 0) +{ + // Do nothing. +} + +template +void DeleteAllocatedMemoryImpl( + const util::ParamData& /* d */, + const typename boost::enable_if>::type* = 0) +{ + // Do nothing. +} + +template +void DeleteAllocatedMemoryImpl( + const util::ParamData& d, + const typename boost::disable_if>::type* = 0, + const typename boost::enable_if>::type* = 0) +{ + // Delete the allocated memory (hopefully we actually own it). + delete *boost::any_cast(&d.value); +} + +template +void DeleteAllocatedMemory( + const util::ParamData& d, + const void* /* input */, + void* /* output */) +{ + DeleteAllocatedMemoryImpl::type>(d); +} + +} // namespace tests +} // namespace bindings +} // namespace mlpack + +#endif diff --git a/src/mlpack/bindings/tests/get_allocated_memory.hpp b/src/mlpack/bindings/tests/get_allocated_memory.hpp new file mode 100644 index 0000000000..82f47e1130 --- /dev/null +++ b/src/mlpack/bindings/tests/get_allocated_memory.hpp @@ -0,0 +1,57 @@ +/** + * @file get_allocated_memory.hpp + * @author Ryan Curtin + * + * If the parameter has a type that may need to be deleted, return the address + * of that object. Otherwise return NULL. + */ +#ifndef MLPACK_BINDINGS_CLI_GET_ALLOCATED_MEMORY_HPP +#define MLPACK_BINDINGS_CLI_GET_ALLOCATED_MEMORY_HPP + +#include + +namespace mlpack { +namespace bindings { +namespace tests { + +template +void* GetAllocatedMemory( + const util::ParamData& /* d */, + const typename boost::disable_if>::type* = 0, + const typename boost::disable_if>::type* = 0) +{ + return NULL; +} + +template +void* GetAllocatedMemory( + const util::ParamData& /* d */, + const typename boost::enable_if>::type* = 0) +{ + return NULL; +} + +template +void* GetAllocatedMemory( + const util::ParamData& d, + const typename boost::disable_if>::type* = 0, + const typename boost::enable_if>::type* = 0) +{ + // Here we have a model; return its memory location. + return *boost::any_cast(&d.value); +} + +template +void GetAllocatedMemory(const util::ParamData& d, + const void* /* input */, + void* output) +{ + *((void**) output) = + GetAllocatedMemory::type>(d); +} + +} // namespace tests +} // namespace bindings +} // namespace mlpack + +#endif diff --git a/src/mlpack/bindings/tests/get_printable_param.hpp b/src/mlpack/bindings/tests/get_printable_param.hpp index e95234549b..5720a0433c 100644 --- a/src/mlpack/bindings/tests/get_printable_param.hpp +++ b/src/mlpack/bindings/tests/get_printable_param.hpp @@ -72,7 +72,8 @@ void GetPrintableParam(const util::ParamData& data, const void* /* input */, void* output) { - *((std::string*) output) = GetPrintableParam(data); + *((std::string*) output) = + GetPrintableParam::type>(data); } } // namespace tests diff --git a/src/mlpack/bindings/tests/test_option.hpp b/src/mlpack/bindings/tests/test_option.hpp index 336d9b93db..32f2fd4222 100644 --- a/src/mlpack/bindings/tests/test_option.hpp +++ b/src/mlpack/bindings/tests/test_option.hpp @@ -18,6 +18,8 @@ #include #include "get_printable_param.hpp" #include "get_param.hpp" +#include "get_allocated_memory.hpp" +#include "delete_allocated_memory.hpp" namespace mlpack { namespace bindings { @@ -90,6 +92,10 @@ class TestOption CLI::GetSingleton().functionMap[tname]["GetPrintableParam"] = &GetPrintableParam; CLI::GetSingleton().functionMap[tname]["GetParam"] = &GetParam; + CLI::GetSingleton().functionMap[tname]["GetAllocatedMemory"] = + &GetAllocatedMemory; + CLI::GetSingleton().functionMap[tname]["DeleteAllocatedMemory"] = + &DeleteAllocatedMemory; CLI::Add(std::move(data)); diff --git a/src/mlpack/core.hpp b/src/mlpack/core.hpp index 902f5b02d1..918421a6e7 100644 --- a/src/mlpack/core.hpp +++ b/src/mlpack/core.hpp @@ -230,6 +230,7 @@ * - Manish Kumar * - Haritha Sreedharan Nair * - Sourabh Varshney + * - Nikhil Goel */ // First, include all of the prerequisites. diff --git a/src/mlpack/core/data/CMakeLists.txt b/src/mlpack/core/data/CMakeLists.txt index ba2fb682da..705ea5fa4e 100644 --- a/src/mlpack/core/data/CMakeLists.txt +++ b/src/mlpack/core/data/CMakeLists.txt @@ -6,6 +6,7 @@ set(SOURCES extension.hpp format.hpp has_serialize.hpp + is_naninf.hpp load_csv.hpp load_csv.cpp load.hpp diff --git a/src/mlpack/core/data/is_naninf.hpp b/src/mlpack/core/data/is_naninf.hpp new file mode 100644 index 0000000000..a307039a66 --- /dev/null +++ b/src/mlpack/core/data/is_naninf.hpp @@ -0,0 +1,67 @@ +/** + * @file is_naninf.hpp + * @author Ryan Curtin + * + * This is an adapted version of Conrad Sanderson's implementation of + * arma::diskio::convert_naninf() from Armadillo. It is here so as to avoid + * using Armadillo internal functionality. + */ +#ifndef MLPACK_CORE_DATA_HAS_NANINF_HPP +#define MLPACK_CORE_DATA_HAS_NANINF_HPP + +#include + +namespace mlpack { +namespace data { + +/** + * See if the token is a NaN or an Inf, and if so, set the value accordingly and + * return a boolean representing whether or not it is. + */ +template +inline bool IsNaNInf(T& val, const std::string& token) +{ + // See if the token represents a NaN or Inf. + if ((token.length() == 3) || (token.length() == 4)) + { + const bool neg = (token[0] == '-'); + const bool pos = (token[0] == '+'); + + const size_t offset = ((neg || pos) && (token.length() == 4)) ? 1 : 0; + + const std::string token2 = token.substr(offset, 3); + + if ((token2 == "inf") || (token2 == "Inf") || (token2 == "INF")) + { + if (std::numeric_limits::has_infinity) + { + val = (!neg) ? std::numeric_limits::infinity() : + -std::numeric_limits::infinity(); + } + else + { + val = (!neg) ? std::numeric_limits::max() : + -std::numeric_limits::max(); + } + + return true; + } + else if ((token2 == "nan") || (token2 == "Nan") || (token2 == "NaN") || + (token2 == "NAN") ) + { + if (std::numeric_limits::has_quiet_NaN) + val = std::numeric_limits::quiet_NaN(); + else + val = T(0); + + return true; + } + } + + return false; +} + +} // namespace data +} // namespace mlpack + +#endif diff --git a/src/mlpack/core/data/load_arff_impl.hpp b/src/mlpack/core/data/load_arff_impl.hpp index 578e07b048..b7d3549b42 100644 --- a/src/mlpack/core/data/load_arff_impl.hpp +++ b/src/mlpack/core/data/load_arff_impl.hpp @@ -16,6 +16,7 @@ #include "load_arff.hpp" #include +#include "is_naninf.hpp" namespace mlpack { namespace data { @@ -197,7 +198,7 @@ void LoadARFF(const std::string& filename, if (token.fail()) { // Check for NaN or inf. - if (!arma::diskio::convert_naninf(val, token.str())) + if (!IsNaNInf(val, token.str())) { // Okay, it's not NaN or inf. If it's '?', we issue a specific // error, otherwise we issue a general error. diff --git a/src/mlpack/core/optimizers/CMakeLists.txt b/src/mlpack/core/optimizers/CMakeLists.txt index 77d21caafe..87b84c63e1 100644 --- a/src/mlpack/core/optimizers/CMakeLists.txt +++ b/src/mlpack/core/optimizers/CMakeLists.txt @@ -11,6 +11,7 @@ set(DIRS iqn lbfgs line_search + problems proximal parallel_sgd rmsprop diff --git a/src/mlpack/core/optimizers/fw/constr_lpball.hpp b/src/mlpack/core/optimizers/fw/constr_lpball.hpp index a95063020d..5f5b3766f9 100644 --- a/src/mlpack/core/optimizers/fw/constr_lpball.hpp +++ b/src/mlpack/core/optimizers/fw/constr_lpball.hpp @@ -115,7 +115,7 @@ class ConstrLpBallSolver else s = arma::abs(v); - arma::uword k; + arma::uword k = 0; s.max(k); // k is the linear index of the largest element. s.zeros(); s(k) = - mlpack::math::Sign(v(k)); diff --git a/src/mlpack/core/optimizers/lbfgs/CMakeLists.txt b/src/mlpack/core/optimizers/lbfgs/CMakeLists.txt index f72f61f098..237f5ac579 100644 --- a/src/mlpack/core/optimizers/lbfgs/CMakeLists.txt +++ b/src/mlpack/core/optimizers/lbfgs/CMakeLists.txt @@ -2,8 +2,6 @@ set(SOURCES lbfgs_impl.hpp lbfgs.hpp lbfgs.cpp - test_functions.hpp - test_functions.cpp ) set(DIR_SRCS) diff --git a/src/mlpack/core/optimizers/lbfgs/test_functions.cpp b/src/mlpack/core/optimizers/lbfgs/test_functions.cpp deleted file mode 100644 index 2d0e0a3f50..0000000000 --- a/src/mlpack/core/optimizers/lbfgs/test_functions.cpp +++ /dev/null @@ -1,284 +0,0 @@ -/** - * @file test_functions.cpp - * @author Ryan Curtin - * - * Implementations of the test functions defined in test_functions.hpp. - * - * mlpack 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 mlpack. If not, see - * http://www.opensource.org/licenses/BSD-3-Clause for more information. - */ -#include "test_functions.hpp" - -using namespace mlpack::optimization::test; - -// -// RosenbrockFunction implementation -// - -RosenbrockFunction::RosenbrockFunction() -{ - initialPoint.set_size(2, 1); - initialPoint[0] = -1.2; - initialPoint[1] = 1; -} - -/** - * Calculate the objective function. - */ -double RosenbrockFunction::Evaluate(const arma::mat& coordinates) -{ - double x1 = coordinates[0]; - double x2 = coordinates[1]; - - double objective = /* f1(x) */ 100 * std::pow(x2 - std::pow(x1, 2), 2) + - /* f2(x) */ std::pow(1 - x1, 2); - - return objective; -} - -/** - * Calculate the gradient. - */ -void RosenbrockFunction::Gradient(const arma::mat& coordinates, - arma::mat& gradient) -{ - // f'_{x1}(x) = -2 (1 - x1) + 400 (x1^3 - (x2 x1)) - // f'_{x2}(x) = 200 (x2 - x1^2) - - double x1 = coordinates[0]; - double x2 = coordinates[1]; - - gradient.set_size(2, 1); - gradient[0] = -2 * (1 - x1) + 400 * (std::pow(x1, 3) - x2 * x1); - gradient[1] = 200 * (x2 - std::pow(x1, 2)); -} - -const arma::mat& RosenbrockFunction::GetInitialPoint() const -{ - return initialPoint; -} - -// -// WoodFunction implementation -// - -WoodFunction::WoodFunction() -{ - initialPoint.set_size(4, 1); - initialPoint[0] = -3; - initialPoint[1] = -1; - initialPoint[2] = -3; - initialPoint[3] = -1; -} - -/** - * Calculate the objective function. - */ -double WoodFunction::Evaluate(const arma::mat& coordinates) -{ - // For convenience; we assume these temporaries will be optimized out. - double x1 = coordinates[0]; - double x2 = coordinates[1]; - double x3 = coordinates[2]; - double x4 = coordinates[3]; - - double objective = /* f1(x) */ 100 * std::pow(x2 - std::pow(x1, 2), 2) + - /* f2(x) */ std::pow(1 - x1, 2) + - /* f3(x) */ 90 * std::pow(x4 - std::pow(x3, 2), 2) + - /* f4(x) */ std::pow(1 - x3, 2) + - /* f5(x) */ 10 * std::pow(x2 + x4 - 2, 2) + - /* f6(x) */ (1.0 / 10.0) * std::pow(x2 - x4, 2); - - return objective; -} - -/** - * Calculate the gradient. - */ -void WoodFunction::Gradient(const arma::mat& coordinates, - arma::mat& gradient) -{ - // For convenience; we assume these temporaries will be optimized out. - double x1 = coordinates[0]; - double x2 = coordinates[1]; - double x3 = coordinates[2]; - double x4 = coordinates[3]; - - // f'_{x1}(x) = 400 (x1^3 - x2 x1) - 2 (1 - x1) - // f'_{x2}(x) = 200 (x2 - x1^2) + 20 (x2 + x4 - 2) + (1 / 5) (x2 - x4) - // f'_{x3}(x) = 360 (x3^3 - x4 x3) - 2 (1 - x3) - // f'_{x4}(x) = 180 (x4 - x3^2) + 20 (x2 + x4 - 2) - (1 / 5) (x2 - x4) - gradient.set_size(4, 1); - gradient[0] = 400 * (std::pow(x1, 3) - x2 * x1) - 2 * (1 - x1); - gradient[1] = 200 * (x2 - std::pow(x1, 2)) + 20 * (x2 + x4 - 2) + - (1.0 / 5.0) * (x2 - x4); - gradient[2] = 360 * (std::pow(x3, 3) - x4 * x3) - 2 * (1 - x3); - gradient[3] = 180 * (x4 - std::pow(x3, 2)) + 20 * (x2 + x4 - 2) - - (1.0 / 5.0) * (x2 - x4); -} - -const arma::mat& WoodFunction::GetInitialPoint() const -{ - return initialPoint; -} - -// -// GeneralizedRosenbrockFunction implementation -// - -GeneralizedRosenbrockFunction::GeneralizedRosenbrockFunction(int n) : - n(n), - visitationOrder(arma::linspace>(0, n - 2, n - 1)) -{ - initialPoint.set_size(n, 1); - for (int i = 0; i < n; i++) // Set to [-1.2 1 -1.2 1 ...]. - { - if (i % 2 == 1) - initialPoint[i] = -1.2; - else - initialPoint[i] = 1; - } -} - -/** - * Shuffle the data points. - */ -void GeneralizedRosenbrockFunction::Shuffle() -{ - visitationOrder = arma::shuffle(arma::linspace>(0, n - 2, - n - 1)); -} - -/** - * Calculate the objective function. - */ -double GeneralizedRosenbrockFunction::Evaluate(const arma::mat& coordinates) - const -{ - double fval = 0; - for (int i = 0; i < (n - 1); i++) - { - fval += 100 * std::pow(std::pow(coordinates[i], 2) - - coordinates[i + 1], 2) + std::pow(1 - coordinates[i], 2); - } - - return fval; -} - -/** - * Calculate the gradient. - */ -void GeneralizedRosenbrockFunction::Gradient(const arma::mat& coordinates, - arma::mat& gradient) const -{ - gradient.set_size(n); - for (int i = 0; i < (n - 1); i++) - { - gradient[i] = 400 * (std::pow(coordinates[i], 3) - coordinates[i] * - coordinates[i + 1]) + 2 * (coordinates[i] - 1); - - if (i > 0) - gradient[i] += 200 * (coordinates[i] - std::pow(coordinates[i - 1], 2)); - } - - gradient[n - 1] = 200 * (coordinates[n - 1] - - std::pow(coordinates[n - 2], 2)); -} - -//! Calculate the objective function of one of the individual functions. -double GeneralizedRosenbrockFunction::Evaluate(const arma::mat& coordinates, - const size_t i, - const size_t batchSize) const -{ - double objective = 0.0; - for (size_t j = i; j < i + batchSize; ++j) - { - const size_t p = visitationOrder[j]; - objective += 100 * std::pow((std::pow(coordinates[p], 2) - - coordinates[p + 1]), 2) + std::pow(1 - coordinates[p], 2); - } - - return objective; -} - -//! Calculate the gradient of one of the individual functions. -void GeneralizedRosenbrockFunction::Gradient(const arma::mat& coordinates, - const size_t i, - arma::mat& gradient, - const size_t batchSize) const -{ - gradient.zeros(n); - - for (size_t j = i; j < i + batchSize; ++j) - { - const size_t p = visitationOrder[j]; - gradient[p] = 400 * (std::pow(coordinates[p], 3) - coordinates[p] * - coordinates[p + 1]) + 2 * (coordinates[p] - 1); - gradient[p + 1] = 200 * (coordinates[p + 1] - std::pow(coordinates[p], 2)); - } -} - -void GeneralizedRosenbrockFunction::Gradient(const arma::mat& coordinates, - const size_t i, - arma::sp_mat& gradient) const -{ - gradient.set_size(n); - - const size_t p = visitationOrder[i]; - - gradient[p] = 400 * (std::pow(coordinates[p], 3) - coordinates[p] * - coordinates[p + 1]) + 2 * (coordinates[p] - 1); - gradient[p + 1] = 200 * (coordinates[p + 1] - std::pow(coordinates[p], 2)); -} - -const arma::mat& GeneralizedRosenbrockFunction::GetInitialPoint() const -{ - return initialPoint; -} - -// -// RosenbrockWoodFunction implementation -// - -RosenbrockWoodFunction::RosenbrockWoodFunction() : rf(4), wf() -{ - initialPoint.set_size(4, 2); - initialPoint.col(0) = rf.GetInitialPoint(); - initialPoint.col(1) = wf.GetInitialPoint(); -} - -/** - * Calculate the objective function. - */ -double RosenbrockWoodFunction::Evaluate(const arma::mat& coordinates) -{ - double objective = rf.Evaluate(coordinates.col(0)) + - wf.Evaluate(coordinates.col(1)); - - return objective; -} - -/*** - * Calculate the gradient. - */ -void RosenbrockWoodFunction::Gradient(const arma::mat& coordinates, - arma::mat& gradient) -{ - gradient.set_size(4, 2); - - arma::vec grf(4); - arma::vec gwf(4); - - rf.Gradient(coordinates.col(0), grf); - wf.Gradient(coordinates.col(1), gwf); - - gradient.col(0) = grf; - gradient.col(1) = gwf; -} - -const arma::mat& RosenbrockWoodFunction::GetInitialPoint() const -{ - return initialPoint; -} diff --git a/src/mlpack/core/optimizers/lbfgs/test_functions.hpp b/src/mlpack/core/optimizers/lbfgs/test_functions.hpp deleted file mode 100644 index 04f442b5be..0000000000 --- a/src/mlpack/core/optimizers/lbfgs/test_functions.hpp +++ /dev/null @@ -1,174 +0,0 @@ -/** - * @file test_functions.hpp - * @author Ryan Curtin - * - * A collection of functions to test optimizers (in this case, L-BFGS). These - * come from the following paper: - * - * "Testing Unconstrained Optimization Software" - * Jorge J. Moré, Burton S. Garbow, and Kenneth E. Hillstrom. 1981. - * ACM Trans. Math. Softw. 7, 1 (March 1981), 17-41. - * http://portal.acm.org/citation.cfm?id=355934.355936 - * - * mlpack 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 mlpack. If not, see - * http://www.opensource.org/licenses/BSD-3-Clause for more information. - */ -#ifndef MLPACK_CORE_OPTIMIZERS_LBFGS_TEST_FUNCTIONS_HPP -#define MLPACK_CORE_OPTIMIZERS_LBFGS_TEST_FUNCTIONS_HPP - -#include - -// To fulfill the template policy class 'FunctionType', we must implement -// the following: -// -// FunctionType(); // constructor -// void Gradient(const arma::mat& coordinates, arma::mat& gradient); -// double Evaluate(const arma::mat& coordinates); -// const arma::mat& GetInitialPoint(); -// -// Note that we are using an arma::mat instead of the more intuitive and -// expected arma::vec. This is because L-BFGS will also optimize matrices. -// However, remember that an arma::vec is simply an (n x 1) arma::mat. You can -// use either internally but the L-BFGS method requires arma::mat& to be passed -// (C++ does not allow implicit reference casting to subclasses). - -namespace mlpack { -namespace optimization { -namespace test { - -/** - * The Rosenbrock function, defined by - * f(x) = f1(x) + f2(x) - * f1(x) = 100 (x2 - x1^2)^2 - * f2(x) = (1 - x1)^2 - * x_0 = [-1.2, 1] - * - * This should optimize to f(x) = 0, at x = [1, 1]. - * - * "An automatic method for finding the greatest or least value of a function." - * H.H. Rosenbrock. 1960. Comput. J. 3., 175-184. - */ -class RosenbrockFunction -{ - public: - RosenbrockFunction(); // initialize initial point - - double Evaluate(const arma::mat& coordinates); - void Gradient(const arma::mat& coordinates, arma::mat& gradient); - - const arma::mat& GetInitialPoint() const; - - private: - arma::mat initialPoint; -}; - -/** - * The Wood function, defined by - * f(x) = f1(x) + f2(x) + f3(x) + f4(x) + f5(x) + f6(x) - * f1(x) = 100 (x2 - x1^2)^2 - * f2(x) = (1 - x1)^2 - * f3(x) = 90 (x4 - x3^2)^2 - * f4(x) = (1 - x3)^2 - * f5(x) = 10 (x2 + x4 - 2)^2 - * f6(x) = (1 / 10) (x2 - x4)^2 - * x_0 = [-3, -1, -3, -1] - * - * This should optimize to f(x) = 0, at x = [1, 1, 1, 1]. - * - * "A comparative study of nonlinear programming codes." - * A.R. Colville. 1968. Rep. 320-2949, IBM N.Y. Scientific Center. - */ -class WoodFunction -{ - public: - WoodFunction(); // initialize initial point - - double Evaluate(const arma::mat& coordinates); - void Gradient(const arma::mat& coordinates, arma::mat& gradient); - - const arma::mat& GetInitialPoint() const; - - private: - arma::mat initialPoint; -}; - -/** - * The Generalized Rosenbrock function in n dimensions, defined by - * f(x) = sum_i^{n - 1} (f(i)(x)) - * f_i(x) = 100 * (x_i^2 - x_{i + 1})^2 + (1 - x_i)^2 - * x_0 = [-1.2, 1, -1.2, 1, ...] - * - * This should optimize to f(x) = 0, at x = [1, 1, 1, 1, ...]. - * - * This function can also be used for stochastic gradient descent (SGD) as a - * decomposable function (DecomposableFunctionType), so there are other - * overloads of Evaluate() and Gradient() implemented, as well as - * NumFunctions(). - * - * "An analysis of the behavior of a glass of genetic adaptive systems." - * K.A. De Jong. Ph.D. thesis, University of Michigan, 1975. - */ -class GeneralizedRosenbrockFunction -{ - public: - /*** - * Set the dimensionality of the extended Rosenbrock function. - * - * @param n Number of dimensions for the function. - */ - GeneralizedRosenbrockFunction(int n); - - void Shuffle(); - - double Evaluate(const arma::mat& coordinates) const; - void Gradient(const arma::mat& coordinates, arma::mat& gradient) const; - - size_t NumFunctions() const { return n - 1; } - double Evaluate(const arma::mat& coordinates, - const size_t i, - const size_t batchSize = 1) const; - void Gradient(const arma::mat& coordinates, - const size_t i, - arma::mat& gradient, - const size_t batchSize = 1) const; - - void Gradient(const arma::mat& coordinates, - const size_t i, - arma::sp_mat& gradient) const; - - const arma::mat& GetInitialPoint() const; - - private: - arma::mat initialPoint; - int n; // Dimensionality - arma::Row visitationOrder; // For shuffling. -}; - -/** - * The Generalized Rosenbrock function in 4 dimensions with the Wood Function in - * four dimensions. In this function we are actually optimizing a 2x4 matrix of - * coordinates, not a vector. - */ -class RosenbrockWoodFunction -{ - public: - RosenbrockWoodFunction(); // initialize initial point - - double Evaluate(const arma::mat& coordinates); - void Gradient(const arma::mat& coordinates, arma::mat& gradient); - - const arma::mat& GetInitialPoint() const; - - private: - arma::mat initialPoint; - GeneralizedRosenbrockFunction rf; - WoodFunction wf; -}; - -} // namespace test -} // namespace optimization -} // namespace mlpack - -#endif // MLPACK_CORE_OPTIMIZERS_LBFGS_TEST_FUNCTIONS_HPP diff --git a/src/mlpack/core/optimizers/problems/CMakeLists.txt b/src/mlpack/core/optimizers/problems/CMakeLists.txt new file mode 100644 index 0000000000..873fdff9c3 --- /dev/null +++ b/src/mlpack/core/optimizers/problems/CMakeLists.txt @@ -0,0 +1,43 @@ +set(SOURCES + booth_function.hpp + booth_function.cpp + bukin_function.hpp + bukin_function.cpp + colville_function.hpp + colville_function.cpp + drop_wave_function.hpp + drop_wave_function.cpp + easom_function.hpp + easom_function.cpp + eggholder_function.hpp + eggholder_function.cpp + generalized_rosenbrock_function.hpp + generalized_rosenbrock_function.cpp + matyas_function.hpp + matyas_function.cpp + mc_cormick_function.hpp + mc_cormick_function.cpp + rastrigin_function.hpp + rastrigin_function.cpp + rosenbrock_function.hpp + rosenbrock_wood_function.hpp + rosenbrock_wood_function.cpp + rosenbrock_function.cpp + schwefel_function.hpp + schwefel_function.cpp + sgd_test_function.hpp + sgd_test_function.cpp + sphere_function.hpp + sphere_function.cpp + styblinski_tang_function.hpp + styblinski_tang_function.cpp + wood_function.hpp + wood_function.cpp +) + +set(DIR_SRCS) +foreach(file ${SOURCES}) + set(DIR_SRCS ${DIR_SRCS} ${CMAKE_CURRENT_SOURCE_DIR}/${file}) +endforeach() + +set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE) diff --git a/src/mlpack/core/optimizers/problems/booth_function.cpp b/src/mlpack/core/optimizers/problems/booth_function.cpp new file mode 100644 index 0000000000..18f928948b --- /dev/null +++ b/src/mlpack/core/optimizers/problems/booth_function.cpp @@ -0,0 +1,58 @@ +/** + * @file booth_function.cpp + * @author Marcus Edel + * + * Implementation of the Booth function. + * + * mlpack 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 mlpack. If not, see + * http://www.opensource.org/licenses/BSD-3-Clause for more information. + */ +#include "booth_function.hpp" + +using namespace mlpack; +using namespace mlpack::optimization; +using namespace mlpack::optimization::test; + +BoothFunction::BoothFunction() { /* Nothing to do here */ } + +void BoothFunction::Shuffle() { /* Nothing to do here */ } + +double BoothFunction::Evaluate(const arma::mat& coordinates, + const size_t /* begin */, + const size_t /* batchSize */) const +{ + // For convenience; we assume these temporaries will be optimized out. + const double x1 = coordinates(0); + const double x2 = coordinates(1); + + const double objective = std::pow(x1 + 2 * x2 - 7, 2) + + std::pow(2 * x1 + x2 - 5, 2); + + return objective; +} + +double BoothFunction::Evaluate(const arma::mat& coordinates) const +{ + return Evaluate(coordinates, 0, NumFunctions()); +} + +void BoothFunction::Gradient(const arma::mat& coordinates, + const size_t /* begin */, + arma::mat& gradient, + const size_t /* batchSize */) const +{ + // For convenience; we assume these temporaries will be optimized out. + const double x1 = coordinates(0); + const double x2 = coordinates(1); + + gradient.set_size(2, 1); + gradient(0) = 10 * x1 + 8 * x2 - 34; + gradient(1) = 8 * x1 + 10 * x2 - 38; +} + +void BoothFunction::Gradient(const arma::mat& coordinates, arma::mat& gradient) +{ + Gradient(coordinates, 0, gradient, 1); +} diff --git a/src/mlpack/core/optimizers/problems/booth_function.hpp b/src/mlpack/core/optimizers/problems/booth_function.hpp new file mode 100644 index 0000000000..495099baa8 --- /dev/null +++ b/src/mlpack/core/optimizers/problems/booth_function.hpp @@ -0,0 +1,105 @@ +/** + * @file booth_function.hpp + * @author Marcus Edel + * + * Definition of the Booth function. + * + * mlpack 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 mlpack. If not, see + * http://www.opensource.org/licenses/BSD-3-Clause for more information. + */ +#ifndef MLPACK_CORE_OPTIMIZERS_PROBLEMS_BOOTH_FUNCTION_HPP +#define MLPACK_CORE_OPTIMIZERS_PROBLEMS_BOOTH_FUNCTION_HPP + +#include + +namespace mlpack { +namespace optimization { +namespace test { + +/** + * The Booth function, defined by + * + * \f[ + * f(x) = (x_1 + 2x_2 - 7)^2 + (2x_1 + x_2 - 5)^2 + * \f] + * + * This should optimize to f(x) = 0, at x = [1, 3]. + * + * For more information, please refer to: + * + * @code + * @article{Jamil2013, + * title = {A Literature Survey of Benchmark Functions For Global + * Optimization Problems}, + * author = {Momin Jamil and Xin{-}She Yang}, + * journal = {CoRR}, + * year = {2013}, + * url = {http://arxiv.org/abs/1308.4008} + * } + * @endcode + */ +class BoothFunction +{ + public: + //! Initialize the BoothFunction. + BoothFunction(); + + /** + * Shuffle the order of function visitation. This may be called by the + * optimizer. + */ + void Shuffle(); + + //! Return 1 (the number of functions). + size_t NumFunctions() const { return 1; } + + //! Get the starting point. + arma::mat GetInitialPoint() const { return arma::mat("-9; -9"); } + + /* + * Evaluate a function for a particular batch-size. + * + * @param coordinates The function coordinates. + * @param begin The first function. + * @param batchSize Number of points to process. + */ + double Evaluate(const arma::mat& coordinates, + const size_t begin, + const size_t batchSize) const; + + /* + * Evaluate a function with the given coordinates. + * + * @param coordinates The function coordinates. + */ + double Evaluate(const arma::mat& coordinates) const; + + /* + * Evaluate the gradient of a function for a particular batch-size. + * + * @param coordinates The function coordinates. + * @param begin The first function. + * @param gradient The function gradient. + * @param batchSize Number of points to process. + */ + void Gradient(const arma::mat& coordinates, + const size_t begin, + arma::mat& gradient, + const size_t batchSize) const; + + /* + * Evaluate the gradient of a function with the given coordinates. + * + * @param coordinates The function coordinates. + * @param gradient The function gradient. + */ + void Gradient(const arma::mat& coordinates, arma::mat& gradient); +}; + +} // namespace test +} // namespace optimization +} // namespace mlpack + +#endif // MLPACK_CORE_OPTIMIZERS_PROBLEMS_BOOTH_FUNCTION_HPP diff --git a/src/mlpack/core/optimizers/problems/bukin_function.cpp b/src/mlpack/core/optimizers/problems/bukin_function.cpp new file mode 100644 index 0000000000..fba6d32191 --- /dev/null +++ b/src/mlpack/core/optimizers/problems/bukin_function.cpp @@ -0,0 +1,62 @@ +/** + * @file bukin_function.cpp + * @author Marcus Edel + * + * Implementation of the Bukin function. + * + * mlpack 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 mlpack. If not, see + * http://www.opensource.org/licenses/BSD-3-Clause for more information. + */ +#include "bukin_function.hpp" + +using namespace mlpack; +using namespace mlpack::optimization; +using namespace mlpack::optimization::test; + +BukinFunction::BukinFunction(const double epsilon) : epsilon(epsilon) +{ /* Nothing to do here */ } + +void BukinFunction::Shuffle() { /* Nothing to do here */ } + +double BukinFunction::Evaluate(const arma::mat& coordinates, + const size_t /* begin */, + const size_t /* batchSize */) const +{ + // For convenience; we assume these temporaries will be optimized out. + const double x1 = coordinates(0); + const double x2 = coordinates(1); + + const double objective = 100 * std::sqrt(std::abs(x2 - 0.01 * + std::pow(x1, 2))) + 0.01 * std::abs(x1 + 10); + + return objective; +} + +double BukinFunction::Evaluate(const arma::mat& coordinates) const +{ + return Evaluate(coordinates, 0, NumFunctions()); +} + +void BukinFunction::Gradient(const arma::mat& coordinates, + const size_t /* begin */, + arma::mat& gradient, + const size_t /* batchSize */) const +{ + // For convenience; we assume these temporaries will be optimized out. + const double x1 = coordinates(0); + const double x2 = coordinates(1); + + gradient.set_size(2, 1); + gradient(0) = (0.01 * (x1 + 10.0)) / (std::abs(x1 + 10.0) + epsilon) - + (x1 * (x2 - 0.01 * std::pow(x1, 2))) / std::pow(std::abs(x2 - 0.01 * + std::pow(x1, 2)), 1.5); + gradient(1) = (50 * (x2 - 0.01 * std::pow(x1, 2))) / + std::pow(std::abs(x2 - 0.01 * std::pow(x1, 2)), 1.5); +} + +void BukinFunction::Gradient(const arma::mat& coordinates, arma::mat& gradient) +{ + Gradient(coordinates, 0, gradient, NumFunctions()); +} diff --git a/src/mlpack/core/optimizers/problems/bukin_function.hpp b/src/mlpack/core/optimizers/problems/bukin_function.hpp new file mode 100644 index 0000000000..5c14ebd5e6 --- /dev/null +++ b/src/mlpack/core/optimizers/problems/bukin_function.hpp @@ -0,0 +1,119 @@ +/** + * @file bukin_function.hpp + * @author Marcus Edel + * + * Definition of the Booth function. + * + * mlpack 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 mlpack. If not, see + * http://www.opensource.org/licenses/BSD-3-Clause for more information. + */ +#ifndef MLPACK_CORE_OPTIMIZERS_PROBLEMS_BUKIN_FUNCTION_HPP +#define MLPACK_CORE_OPTIMIZERS_PROBLEMS_BUKIN_FUNCTION_HPP + +#include + +namespace mlpack { +namespace optimization { +namespace test { + +/** + * The Bukin function, defined by + * + * \f[ + * f(x) = 100 * \sqrt(\left|x_2 - 0.01 * x_1^2 \right|) + + * 0.01 * \left|x_1 + 10 \right| + * \f] + * + * This should optimize to f(x) = 0, at x = [-10, 1]. + * + * For more information, please refer to: + * + * @code + * @article{Jamil2013, + * title = {A Literature Survey of Benchmark Functions For Global + * Optimization Problems}, + * author = {Momin Jamil and Xin{-}She Yang}, + * journal = {CoRR}, + * year = {2013}, + * url = {http://arxiv.org/abs/1308.4008} + * } + * @endcode + */ +class BukinFunction +{ + public: + /* + * Initialize the BukinFunction. + * + * @param epsilon Coefficient to avoid division by zero (numerical stability). + */ + BukinFunction(const double epsilon = 1e-8); + + /** + * Shuffle the order of function visitation. This may be called by the + * optimizer. + */ + void Shuffle(); + + //! Return 1 (the number of functions). + size_t NumFunctions() const { return 1; } + + //! Get the starting point. + arma::mat GetInitialPoint() const { return arma::mat("-10; -2.0"); } + + /* + * Evaluate a function for a particular batch-size. + * + * @param coordinates The function coordinates. + * @param begin The first function. + * @param batchSize Number of points to process. + */ + double Evaluate(const arma::mat& coordinates, + const size_t begin, + const size_t batchSize) const; + + /* + * Evaluate a function with the given coordinates. + * + * @param coordinates The function coordinates. + */ + double Evaluate(const arma::mat& coordinates) const; + + /* + * Evaluate the gradient of a function for a particular batch-size. + * + * @param coordinates The function coordinates. + * @param begin The first function. + * @param gradient The function gradient. + * @param batchSize Number of points to process. + */ + void Gradient(const arma::mat& coordinates, + const size_t begin, + arma::mat& gradient, + const size_t batchSize) const; + + /* + * Evaluate the gradient of a function with the given coordinates. + * + * @param coordinates The function coordinates. + * @param gradient The function gradient. + */ + void Gradient(const arma::mat& coordinates, arma::mat& gradient); + + //! Get the value used for numerical stability. + double Epsilon() const { return epsilon; } + //! Modify the value used for numerical stability. + double& Epsilon() { return epsilon; } + + private: + //! The value used for numerical stability. + double epsilon; +}; + +} // namespace test +} // namespace optimization +} // namespace mlpack + +#endif // MLPACK_CORE_OPTIMIZERS_PROBLEMS_BUKIN_FUNCTION_HPP diff --git a/src/mlpack/core/optimizers/problems/colville_function.cpp b/src/mlpack/core/optimizers/problems/colville_function.cpp new file mode 100644 index 0000000000..4ec8a14982 --- /dev/null +++ b/src/mlpack/core/optimizers/problems/colville_function.cpp @@ -0,0 +1,67 @@ +/** + * @file colville_function.cpp + * @author Marcus Edel + * + * Implementation of the Coville function. + * + * mlpack 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 mlpack. If not, see + * http://www.opensource.org/licenses/BSD-3-Clause for more information. + */ +#include "colville_function.hpp" + +using namespace mlpack; +using namespace mlpack::optimization; +using namespace mlpack::optimization::test; + +ColvilleFunction::ColvilleFunction() { /* Nothing to do here */ } + +void ColvilleFunction::Shuffle() { /* Nothing to do here */ } + +double ColvilleFunction::Evaluate(const arma::mat& coordinates, + const size_t /* begin */, + const size_t /* batchSize */) const +{ + // For convenience; we assume these temporaries will be optimized out. + const double x1 = coordinates(0); + const double x2 = coordinates(1); + const double x3 = coordinates(2); + const double x4 = coordinates(3); + + const double objective = 100 * std::pow(std::pow(x1, 2) - x2, 2) + + std::pow(x1 - 1, 2) + std::pow(x3 - 1, 2) + 90 * + std::pow(std::pow(x3, 2) - x4, 2) + 10.1 * (std::pow(x2 - 1, 2) + + std::pow(x4 - 1, 2)) + 19.8 * (x2 - 1) * (x4 - 1); + + return objective; +} + +double ColvilleFunction::Evaluate(const arma::mat& coordinates) const +{ + return Evaluate(coordinates, 0, NumFunctions()); +} + +void ColvilleFunction::Gradient(const arma::mat& coordinates, + const size_t /* begin */, + arma::mat& gradient, + const size_t /* batchSize */) const +{ + // For convenience; we assume these temporaries will be optimized out. + const double x1 = coordinates(0); + const double x2 = coordinates(1); + const double x3 = coordinates(2); + const double x4 = coordinates(3); + + gradient.set_size(4, 1); + gradient(0) = 2 * (200 * x1 * (std::pow(x1, 2) - x2) + x1 - 1); + gradient(1) = 19.8 * x4 - 200 * std::pow(x1, 2) + 220.2 * x2 - 40; + gradient(2) = 2 * (180 * x3 * (std::pow(x3, 2) - x4) + x3 - 1); + gradient(3) = 200.2 * x4 + 19.8 * x2 - 180 * std::pow(x3, 2) - 40; +} + +void ColvilleFunction::Gradient(const arma::mat& coordinates, + arma::mat& gradient) +{ + Gradient(coordinates, 0, gradient, NumFunctions()); +} diff --git a/src/mlpack/core/optimizers/problems/colville_function.hpp b/src/mlpack/core/optimizers/problems/colville_function.hpp new file mode 100644 index 0000000000..76d1f9b5c6 --- /dev/null +++ b/src/mlpack/core/optimizers/problems/colville_function.hpp @@ -0,0 +1,106 @@ +/** + * @file colville_function.hpp + * @author Marcus Edel + * + * Definition of the Colville function. + * + * mlpack 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 mlpack. If not, see + * http://www.opensource.org/licenses/BSD-3-Clause for more information. + */ +#ifndef MLPACK_CORE_OPTIMIZERS_PROBLEMS_COLVILLE_FUNCTION_HPP +#define MLPACK_CORE_OPTIMIZERS_PROBLEMS_COLVILLE_FUNCTION_HPP + +#include + +namespace mlpack { +namespace optimization { +namespace test { + +/** + * The Colville function, defined by + * + * \f[ + * f(x) = 100(x_1^2 - x_2)^2 + (x_1 - 1)^2 + (x_3 - 1)^2 + 90 * (x_3^2 - x_4)^2 + * + 10.1 * ((x_2-1)^2 + (x_4 - 1)^2) + 19.8 * (x_2 - 1) * (x_4 - 1) + * \f] + * + * This should optimize to f(x) = 0, at x = [1, 1, 1, 1]. + * + * For more information, please refer to: + * + * @code + * @article{Jamil2013, + * title = {A Literature Survey of Benchmark Functions For Global + * Optimization Problems}, + * author = {Momin Jamil and Xin{-}She Yang}, + * journal = {CoRR}, + * year = {2013}, + * url = {http://arxiv.org/abs/1308.4008} + * } + * @endcode + */ +class ColvilleFunction +{ + public: + //! Initialize the ColvilleFunction. + ColvilleFunction(); + + /** + * Shuffle the order of function visitation. This may be called by the + * optimizer. + */ + void Shuffle(); + + //! Return 1 (the number of functions). + size_t NumFunctions() const { return 1; } + + //! Get the starting point. + arma::mat GetInitialPoint() const { return arma::mat("-5; 3; 1; -9"); } + + /* + * Evaluate a function for a particular batch-size. + * + * @param coordinates The function coordinates. + * @param begin The first function. + * @param batchSize Number of points to process. + */ + double Evaluate(const arma::mat& coordinates, + const size_t begin, + const size_t batchSize) const; + + /* + * Evaluate a function with the given coordinates. + * + * @param coordinates The function coordinates. + */ + double Evaluate(const arma::mat& coordinates) const; + + /* + * Evaluate the gradient of a function for a particular batch-size. + * + * @param coordinates The function coordinates. + * @param begin The first function. + * @param gradient The function gradient. + * @param batchSize Number of points to process. + */ + void Gradient(const arma::mat& coordinates, + const size_t begin, + arma::mat& gradient, + const size_t batchSize) const; + + /* + * Evaluate the gradient of a function with the given coordinates. + * + * @param coordinates The function coordinates. + * @param gradient The function gradient. + */ + void Gradient(const arma::mat& coordinates, arma::mat& gradient); +}; + +} // namespace test +} // namespace optimization +} // namespace mlpack + +#endif // MLPACK_CORE_OPTIMIZERS_PROBLEMS_COLVILLE_FUNCTION_HPP diff --git a/src/mlpack/core/optimizers/problems/drop_wave_function.cpp b/src/mlpack/core/optimizers/problems/drop_wave_function.cpp new file mode 100644 index 0000000000..f69704468f --- /dev/null +++ b/src/mlpack/core/optimizers/problems/drop_wave_function.cpp @@ -0,0 +1,71 @@ +/** + * @file drop_wave_function.cpp + * @author Marcus Edel + * + * Implementation of the Drop-Wave function. + * + * mlpack 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 mlpack. If not, see + * http://www.opensource.org/licenses/BSD-3-Clause for more information. + */ +#include "drop_wave_function.hpp" + +using namespace mlpack; +using namespace mlpack::optimization; +using namespace mlpack::optimization::test; + +DropWaveFunction::DropWaveFunction() { /* Nothing to do here */ } + +void DropWaveFunction::Shuffle() { /* Nothing to do here */ } + +double DropWaveFunction::Evaluate(const arma::mat& coordinates, + const size_t /* begin */, + const size_t /* batchSize */) const +{ + // For convenience; we assume these temporaries will be optimized out. + const double x1 = coordinates(0); + const double x2 = coordinates(1); + + const double objective = -1.0 * (1.0 + std::cos(12.0 * + std::sqrt(std::pow(x1, 2) + std::pow(x2, 2)))) / + (0.5 * (std::pow(x1, 2) + std::pow(x2, 2)) + 2.0); + + return objective; +} + +double DropWaveFunction::Evaluate(const arma::mat& coordinates) const +{ + return Evaluate(coordinates, 0, 1); +} + +void DropWaveFunction::Gradient(const arma::mat& coordinates, + const size_t /* begin */, + arma::mat& gradient, + const size_t /* batchSize */) const +{ + // For convenience; we assume these temporaries will be optimized out. + const double x1 = coordinates(0); + const double x2 = coordinates(1); + + gradient.set_size(2, 1); + gradient(0) = (12.0 * x1 * std::sin(12.0 * std::sqrt(std::pow(x1, 2) + + std::pow(x2, 2)))) / (std::sqrt(std::pow(x1, 2) + std::pow(x2, 2)) * + (0.5 * (std::pow(x1, 2) + std::pow(x2, 2)) + 2)) - + (x1 * (-1.0 * std::cos(12.0 * std::sqrt(std::pow(x1, 2) + + std::pow(x2, 2))) -1.0)) / std::pow(0.5 * + (std::pow(x1, 2) + std::pow(x2, 2)) + 2, 2); + + gradient(1) = (12.0 * x2 * std::sin(12.0 * std::sqrt(std::pow(x1, 2) + + std::pow(x2, 2)))) / (std::sqrt(std::pow(x1, 2) + std::pow(x2, 2)) * + (0.5 * (std::pow(x1, 2) + std::pow(x2, 2)) + 2)) - + (x2 * (-1.0 * std::cos(12.0 * std::sqrt(std::pow(x1, 2) + + std::pow(x2, 2))) -1.0)) / std::pow(0.5 * + (std::pow(x1, 2) + std::pow(x2, 2)) + 2, 2); +} + +void DropWaveFunction::Gradient(const arma::mat& coordinates, + arma::mat& gradient) +{ + Gradient(coordinates, 0, gradient, 1); +} diff --git a/src/mlpack/core/optimizers/problems/drop_wave_function.hpp b/src/mlpack/core/optimizers/problems/drop_wave_function.hpp new file mode 100644 index 0000000000..dfa8f693a8 --- /dev/null +++ b/src/mlpack/core/optimizers/problems/drop_wave_function.hpp @@ -0,0 +1,105 @@ +/** + * @file drop_wave_function.hpp + * @author Marcus Edel + * + * Definition of the Drop-Wave function. + * + * mlpack 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 mlpack. If not, see + * http://www.opensource.org/licenses/BSD-3-Clause for more information. + */ +#ifndef MLPACK_CORE_OPTIMIZERS_PROBLEMS_DROP_WAVE_FUNCTION_HPP +#define MLPACK_CORE_OPTIMIZERS_PROBLEMS_DROP_WAVE_FUNCTION_HPP + +#include + +namespace mlpack { +namespace optimization { +namespace test { + +/** + * The Drop-Wave function, defined by + * + * \f[ + * f(x) = - (1 + \cos(12 * \sqrt(x_1^2 + x_2^2))) / (0.5 * (x_1^2 + x_2^2) + 2) + * \f] + * + * This should optimize to f(x) = 0, at x = [0, 0]. + * + * For more information, please refer to: + * + * @code + * @article{Jamil2013, + * title = {A Literature Survey of Benchmark Functions For Global + * Optimization Problems}, + * author = {Momin Jamil and Xin{-}She Yang}, + * journal = {CoRR}, + * year = {2013}, + * url = {http://arxiv.org/abs/1308.4008} + * } + * @endcode + */ +class DropWaveFunction +{ + public: + //! Initialize the DropWaveFunction. + DropWaveFunction(); + + /** + * Shuffle the order of function visitation. This may be called by the + * optimizer. + */ + void Shuffle(); + + //! Return 1 (the number of functions). + size_t NumFunctions() const { return 1; } + + //! Get the starting point. + arma::mat GetInitialPoint() const { return arma::mat("0.5; 0.5"); } + + /* + * Evaluate a function for a particular batch-size. + * + * @param coordinates The function coordinates. + * @param begin The first function. + * @param batchSize Number of points to process. + */ + double Evaluate(const arma::mat& coordinates, + const size_t begin, + const size_t batchSize) const; + + /* + * Evaluate a function with the given coordinates. + * + * @param coordinates The function coordinates. + */ + double Evaluate(const arma::mat& coordinates) const; + + /* + * Evaluate the gradient of a function for a particular batch-size. + * + * @param coordinates The function coordinates. + * @param begin The first function. + * @param gradient The function gradient. + * @param batchSize Number of points to process. + */ + void Gradient(const arma::mat& coordinates, + const size_t begin, + arma::mat& gradient, + const size_t batchSize) const; + + /* + * Evaluate the gradient of a function with the given coordinates. + * + * @param coordinates The function coordinates. + * @param gradient The function gradient. + */ + void Gradient(const arma::mat& coordinates, arma::mat& gradient); +}; + +} // namespace test +} // namespace optimization +} // namespace mlpack + +#endif // MLPACK_CORE_OPTIMIZERS_PROBLEMS_DROP_WAVE_FUNCTION_HPP diff --git a/src/mlpack/core/optimizers/problems/easom_function.cpp b/src/mlpack/core/optimizers/problems/easom_function.cpp new file mode 100644 index 0000000000..8427beb237 --- /dev/null +++ b/src/mlpack/core/optimizers/problems/easom_function.cpp @@ -0,0 +1,67 @@ +/** + * @file easom_function.cpp + * @author Marcus Edel + * + * Implementation of the Easom function. + * + * mlpack 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 mlpack. If not, see + * http://www.opensource.org/licenses/BSD-3-Clause for more information. + */ +#include "easom_function.hpp" + +using namespace mlpack; +using namespace mlpack::optimization; +using namespace mlpack::optimization::test; + +EasomFunction::EasomFunction() { /* Nothing to do here */ } + +void EasomFunction::Shuffle() { /* Nothing to do here */ } + +double EasomFunction::Evaluate(const arma::mat& coordinates, + const size_t /* begin */, + const size_t /* batchSize */) const +{ + // For convenience; we assume these temporaries will be optimized out. + const double x1 = coordinates(0); + const double x2 = coordinates(1); + + const double objective = -std::cos(x1) * std::cos(x2) * + std::exp(-1.0 * std::pow(x1 - M_PI, 2) - std::pow(x2 - M_PI, 2)); + + return objective; +} + +double EasomFunction::Evaluate(const arma::mat& coordinates) const +{ + return Evaluate(coordinates, 0, NumFunctions()); +} + +void EasomFunction::Gradient(const arma::mat& coordinates, + const size_t /* begin */, + arma::mat& gradient, + const size_t /* batchSize */) const +{ + // For convenience; we assume these temporaries will be optimized out. + const double x1 = coordinates(0); + const double x2 = coordinates(1); + + gradient.set_size(2, 1); + gradient(0) = 2 * (x1 - M_PI) * + std::exp(-1.0 * std::pow(x1 - M_PI, 2) - std::pow(x2 - M_PI, 2)) * + std::cos(x1) * std::cos(x2) + + std::exp(-1.0 * std::pow(x1 - M_PI, 2) - std::pow(x2 - M_PI, 2)) * + std::sin(x1) * std::cos(x2); + + gradient(1) = 2 * (x2 - M_PI) * + std::exp(-1.0 * std::pow(x1 - M_PI, 2) - std::pow(x2 - M_PI, 2)) * + std::cos(x1) * std::cos(x2) + + std::exp(-1.0 * std::pow(x1 - M_PI, 2) - std::pow(x2 - M_PI, 2)) * + std::cos(x1) * std::sin(x2); +} + +void EasomFunction::Gradient(const arma::mat& coordinates, arma::mat& gradient) +{ + Gradient(coordinates, 0, gradient, NumFunctions()); +} diff --git a/src/mlpack/core/optimizers/problems/easom_function.hpp b/src/mlpack/core/optimizers/problems/easom_function.hpp new file mode 100644 index 0000000000..e3b97a8b67 --- /dev/null +++ b/src/mlpack/core/optimizers/problems/easom_function.hpp @@ -0,0 +1,105 @@ +/** + * @file easom_function.hpp + * @author Marcus Edel + * + * Definition of the Booth function. + * + * mlpack 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 mlpack. If not, see + * http://www.opensource.org/licenses/BSD-3-Clause for more information. + */ +#ifndef MLPACK_CORE_OPTIMIZERS_PROBLEMS_EASOM_FUNCTION_HPP +#define MLPACK_CORE_OPTIMIZERS_PROBLEMS_EASOM_FUNCTION_HPP + +#include + +namespace mlpack { +namespace optimization { +namespace test { + +/** + * The Easom function, defined by + * + * \f[ + * f(x) = -1.0 * \cos(x_1) * \cos(x_2) * \exp(-(x_1 - \pi)^2 - (x_2 - \pi)^2) + * \f] + * + * This should optimize to f(x) = -1, at x = [3.14, 3.14]. + * + * For more information, please refer to: + * + * @code + * @article{Jamil2013, + * title = {A Literature Survey of Benchmark Functions For Global + * Optimization Problems}, + * author = {Momin Jamil and Xin{-}She Yang}, + * journal = {CoRR}, + * year = {2013}, + * url = {http://arxiv.org/abs/1308.4008} + * } + * @endcode + */ +class EasomFunction +{ + public: + //! Initialize the EasomFunction. + EasomFunction(); + + /** + * Shuffle the order of function visitation. This may be called by the + * optimizer. + */ + void Shuffle(); + + //! Return 1 (the number of functions). + size_t NumFunctions() const { return 1; } + + //! Get the starting point. + arma::mat GetInitialPoint() const { return arma::mat("-90.0; 90.0"); } + + /* + * Evaluate a function for a particular batch-size. + * + * @param coordinates The function coordinates. + * @param begin The first function. + * @param batchSize Number of points to process. + */ + double Evaluate(const arma::mat& coordinates, + const size_t begin, + const size_t batchSize) const; + + /* + * Evaluate a function with the given coordinates. + * + * @param coordinates The function coordinates. + */ + double Evaluate(const arma::mat& coordinates) const; + + /* + * Evaluate the gradient of a function for a particular batch-size. + * + * @param coordinates The function coordinates. + * @param begin The first function. + * @param gradient The function gradient. + * @param batchSize Number of points to process. + */ + void Gradient(const arma::mat& coordinates, + const size_t begin, + arma::mat& gradient, + const size_t batchSize) const; + + /* + * Evaluate the gradient of a function with the given coordinates. + * + * @param coordinates The function coordinates. + * @param gradient The function gradient. + */ + void Gradient(const arma::mat& coordinates, arma::mat& gradient); +}; + +} // namespace test +} // namespace optimization +} // namespace mlpack + +#endif // MLPACK_CORE_OPTIMIZERS_PROBLEMS_EASOM_FUNCTION_HPP diff --git a/src/mlpack/core/optimizers/problems/eggholder_function.cpp b/src/mlpack/core/optimizers/problems/eggholder_function.cpp new file mode 100644 index 0000000000..4349583201 --- /dev/null +++ b/src/mlpack/core/optimizers/problems/eggholder_function.cpp @@ -0,0 +1,71 @@ +/** + * @file eggholder_function.cpp + * @author Marcus Edel + * + * Implementation of the Eggholder function. + * + * mlpack 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 mlpack. If not, see + * http://www.opensource.org/licenses/BSD-3-Clause for more information. + */ +#include "eggholder_function.hpp" + +using namespace mlpack; +using namespace mlpack::optimization; +using namespace mlpack::optimization::test; + +EggholderFunction::EggholderFunction() { /* Nothing to do here */ } + +void EggholderFunction::Shuffle() { /* Nothing to do here */ } + +double EggholderFunction::Evaluate(const arma::mat& coordinates, + const size_t /* begin */, + const size_t /* batchSize */) const +{ + // For convenience; we assume these temporaries will be optimized out. + const double x1 = coordinates(0); + const double x2 = coordinates(1); + + const double objective = -1.0 * (x2 + 47) * std::sin(std::sqrt( + std::abs(x2 + x1 / 2 + 47))) - x1 * std::sin(std::sqrt( + std::abs(x1 - (x2 + 47)))); + + return objective; +} + +double EggholderFunction::Evaluate(const arma::mat& coordinates) const +{ + return Evaluate(coordinates, 0, NumFunctions()); +} + +void EggholderFunction::Gradient(const arma::mat& coordinates, + const size_t /* begin */, + arma::mat& gradient, + const size_t /* batchSize */) const +{ + // For convenience; we assume these temporaries will be optimized out. + const double x1 = coordinates(0); + const double x2 = coordinates(1); + + gradient.set_size(2, 1); + gradient(0) = -1.0 * std::sin(std::sqrt(std::abs(x1 - x2 - 47))) - + (x1 * (x1 - x2 - 47) * std::cos(std::sqrt(std::abs(x1 - x2 - 47)))) / + std::pow(2 * std::abs(x1 - x2 - 47), 1.5) - + ((x1 + 47) * (x1 / 2 + x2 + 47) * + std::cos(std::sqrt(std::abs(x1 / 2 + x2 + 47)))) / + (4 * std::pow(std::abs(x1 / 2 + x2 + 47), 1.5)); + + gradient(1) = -1.0 * std::sin(std::sqrt(std::abs(x1 / 2 + x2 + 47))) - + (x1 * (x1 - x2 - 47) * std::cos(std::sqrt(std::abs(x1 - x2 - 47)))) / + std::pow(2 * std::abs(x1 - x2 - 47), 1.5) - + ((x1 + 47) * (x1 / 2 + x2 + 47) * + std::cos(std::sqrt(std::abs(x1 / 2 + x2 + 47)))) / + (4 * std::pow(std::abs(x1 / 2 + x2 + 47), 1.5)); +} + +void EggholderFunction::Gradient(const arma::mat& coordinates, + arma::mat& gradient) +{ + Gradient(coordinates, 0, gradient, NumFunctions()); +} diff --git a/src/mlpack/core/optimizers/problems/eggholder_function.hpp b/src/mlpack/core/optimizers/problems/eggholder_function.hpp new file mode 100644 index 0000000000..093984a34a --- /dev/null +++ b/src/mlpack/core/optimizers/problems/eggholder_function.hpp @@ -0,0 +1,106 @@ +/** + * @file eggholder_function.hpp + * @author Marcus Edel + * + * Definition of the Eggholder function. + * + * mlpack 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 mlpack. If not, see + * http://www.opensource.org/licenses/BSD-3-Clause for more information. + */ +#ifndef MLPACK_CORE_OPTIMIZERS_PROBLEMS_EGGHOLDER_FUNCTION_HPP +#define MLPACK_CORE_OPTIMIZERS_PROBLEMS_EGGHOLDER_FUNCTION_HPP + +#include + +namespace mlpack { +namespace optimization { +namespace test { + +/** + * The Eggholder function, defined by + * + * \f[ + * f(x) = -(x_2 + 47) * \sin(\sqrt(\left|x_2 + x_1 / 2 + 47\right|)) - x_1 * + * \sin(\sqrt(\left|x_1-(x_2 + 47)\right|)) + * \f] + * + * This should optimize to f(x) = -959.6407, at x = [512, 404.2319]. + * + * For more information, please refer to: + * + * @code + * @article{Jamil2013, + * title = {A Literature Survey of Benchmark Functions For Global + * Optimization Problems}, + * author = {Momin Jamil and Xin{-}She Yang}, + * journal = {CoRR}, + * year = {2013}, + * url = {http://arxiv.org/abs/1308.4008} + * } + * @endcode + */ +class EggholderFunction +{ + public: + //! Initialize the EggholderFunction. + EggholderFunction(); + + /** + * Shuffle the order of function visitation. This may be called by the + * optimizer. + */ + void Shuffle(); + + //! Return 1 (the number of functions). + size_t NumFunctions() const { return 1; } + + //! Get the starting point. + arma::mat GetInitialPoint() const { return arma::mat("-333; -333"); } + + /* + * Evaluate a function for a particular batch-size. + * + * @param coordinates The function coordinates. + * @param begin The first function. + * @param batchSize Number of points to process. + */ + double Evaluate(const arma::mat& coordinates, + const size_t begin, + const size_t batchSize) const; + + /* + * Evaluate a function with the given coordinates. + * + * @param coordinates The function coordinates. + */ + double Evaluate(const arma::mat& coordinates) const; + + /* + * Evaluate the gradient of a function for a particular batch-size. + * + * @param coordinates The function coordinates. + * @param begin The first function. + * @param gradient The function gradient. + * @param batchSize Number of points to process. + */ + void Gradient(const arma::mat& coordinates, + const size_t begin, + arma::mat& gradient, + const size_t batchSize) const; + + /* + * Evaluate the gradient of a function with the given coordinates. + * + * @param coordinates The function coordinates. + * @param gradient The function gradient. + */ + void Gradient(const arma::mat& coordinates, arma::mat& gradient); +}; + +} // namespace test +} // namespace optimization +} // namespace mlpack + +#endif // MLPACK_CORE_OPTIMIZERS_PROBLEMS_EGGHOLDER_FUNCTION_HPP diff --git a/src/mlpack/core/optimizers/problems/generalized_rosenbrock_function.cpp b/src/mlpack/core/optimizers/problems/generalized_rosenbrock_function.cpp new file mode 100644 index 0000000000..6a75881619 --- /dev/null +++ b/src/mlpack/core/optimizers/problems/generalized_rosenbrock_function.cpp @@ -0,0 +1,115 @@ +/** + * @file generalized_rosenbrock_function.cpp + * @author Ryan Curtin + * @author Marcus Edel + * + * Implementation of the Generalized-Rosenbrock function. + * + * mlpack 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 mlpack. If not, see + * http://www.opensource.org/licenses/BSD-3-Clause for more information. + */ +#include "generalized_rosenbrock_function.hpp" + +using namespace mlpack; +using namespace mlpack::optimization; +using namespace mlpack::optimization::test; + +GeneralizedRosenbrockFunction::GeneralizedRosenbrockFunction(const size_t n) : + n(n), + visitationOrder(arma::linspace >(0, n - 1, n)) + +{ + initialPoint.set_size(n, 1); + for (size_t i = 0; i < n; i++) // Set to [-1.2 1 -1.2 1 ...]. + { + if (i % 2 == 1) + { + initialPoint(i) = -1.2; + } + else + { + initialPoint(i) = 1; + } + } +} + +void GeneralizedRosenbrockFunction::Shuffle() +{ + visitationOrder = arma::shuffle(arma::linspace>(0, n - 2, + n - 1)); +} + +double GeneralizedRosenbrockFunction::Evaluate(const arma::mat& coordinates, + const size_t begin, + const size_t batchSize) const +{ + double objective = 0.0; + for (size_t j = begin; j < begin + batchSize; ++j) + { + const size_t p = visitationOrder[j]; + objective += 100 * std::pow((std::pow(coordinates[p], 2) + - coordinates[p + 1]), 2) + std::pow(1 - coordinates[p], 2); + } + + return objective; +} + +double GeneralizedRosenbrockFunction::Evaluate(const arma::mat& coordinates) + const +{ + double fval = 0; + for (size_t i = 0; i < (n - 1); i++) + { + fval += 100 * std::pow(std::pow(coordinates[i], 2) - + coordinates[i + 1], 2) + std::pow(1 - coordinates[i], 2); + } + + return fval; +} + +void GeneralizedRosenbrockFunction::Gradient(const arma::mat& coordinates, + const size_t begin, + arma::mat& gradient, + const size_t batchSize) const +{ + gradient.zeros(n); + for (size_t j = begin; j < begin + batchSize; ++j) + { + const size_t p = visitationOrder[j]; + gradient[p] = 400 * (std::pow(coordinates[p], 3) - coordinates[p] * + coordinates[p + 1]) + 2 * (coordinates[p] - 1); + gradient[p + 1] = 200 * (coordinates[p + 1] - std::pow(coordinates[p], 2)); + } +} + +void GeneralizedRosenbrockFunction::Gradient(const arma::mat& coordinates, + arma::mat& gradient) const +{ + gradient.set_size(n); + for (size_t i = 0; i < (n - 1); i++) + { + gradient[i] = 400 * (std::pow(coordinates[i], 3) - coordinates[i] * + coordinates[i + 1]) + 2 * (coordinates[i] - 1); + + if (i > 0) + gradient[i] += 200 * (coordinates[i] - std::pow(coordinates[i - 1], 2)); + } + + gradient[n - 1] = 200 * (coordinates[n - 1] - + std::pow(coordinates[n - 2], 2)); +} + +void GeneralizedRosenbrockFunction::Gradient(const arma::mat& coordinates, + const size_t begin, + arma::sp_mat& gradient) const +{ + gradient.set_size(n); + + const size_t p = visitationOrder[begin]; + + gradient[p] = 400 * (std::pow(coordinates[p], 3) - coordinates[p] * + coordinates[p + 1]) + 2 * (coordinates[p] - 1); + gradient[p + 1] = 200 * (coordinates[p + 1] - std::pow(coordinates[p], 2)); +} diff --git a/src/mlpack/core/optimizers/problems/generalized_rosenbrock_function.hpp b/src/mlpack/core/optimizers/problems/generalized_rosenbrock_function.hpp new file mode 100644 index 0000000000..b541464ed2 --- /dev/null +++ b/src/mlpack/core/optimizers/problems/generalized_rosenbrock_function.hpp @@ -0,0 +1,135 @@ +/** + * @file generalized_rosenbrock_function.hpp + * @author Ryan Curtin + * @author Marcus Edel + * + * Definition of the Generalized Rosenbrock function. + * + * mlpack 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 mlpack. If not, see + * http://www.opensource.org/licenses/BSD-3-Clause for more information. + */ +#ifndef MLPACK_CORE_OPTIMIZERS_PROBLEMS_GENERALIZED_ROSENBROCK_FUNCTION_HPP +#define MLPACK_CORE_OPTIMIZERS_PROBLEMS_GENERALIZED_ROSENBROCK_FUNCTION_HPP + +#include + +namespace mlpack { +namespace optimization { +namespace test { + +/** + * The Generalized Rosenbrock function in n dimensions, defined by + * f(x) = sum_i^{n - 1} (f(i)(x)) + * f_i(x) = 100 * (x_i^2 - x_{i + 1})^2 + (1 - x_i)^2 + * x_0 = [-1.2, 1, -1.2, 1, ...] + * + * This should optimize to f(x) = 0, at x = [1, 1, 1, 1, ...]. + * + * This function can also be used for stochastic gradient descent (SGD) as a + * decomposable function (DecomposableFunctionType), so there are other + * overloads of Evaluate() and Gradient() implemented, as well as + * NumFunctions(). + * + * For more information, please refer to: + * + * @code + * @phdthesis{Jong1975, + * title = {Analysis of the behavior of a class of genetic adaptive + * systems}, + * author = {De Jong, Kenneth Alan}, + * school = {Queensland University of Technology}, + * year = {1975}, + * type = {{PhD} dissertation}, + * } + * @endcode + */ +class GeneralizedRosenbrockFunction +{ + public: + /* + * Initialize the GeneralizedRosenbrockFunction. + * + * @param n Number of dimensions for the function. + */ + GeneralizedRosenbrockFunction(const size_t n); + + /** + * Shuffle the order of function visitation. This may be called by the + * optimizer. + */ + void Shuffle(); + + //! Return 1 (the number of functions). + size_t NumFunctions() const { return n - 1; } + + //! Get the starting point. + const arma::mat& GetInitialPoint() const { return initialPoint;} + + /* + * Evaluate a function for a particular batch-size. + * + * @param coordinates The function coordinates. + * @param begin The first function. + * @param batchSize Number of points to process. + */ + double Evaluate(const arma::mat& coordinates, + const size_t begin, + const size_t batchSize = 1) const; + + /* + * Evaluate a function with the given coordinates. + * + * @param coordinates The function coordinates. + */ + double Evaluate(const arma::mat& coordinates) const; + + /* + * Evaluate the gradient of a function for a particular batch-size. + * + * @param coordinates The function coordinates. + * @param begin The first function. + * @param gradient The function gradient. + * @param batchSize Number of points to process. + */ + void Gradient(const arma::mat& coordinates, + const size_t begin, + arma::mat& gradient, + const size_t batchSize = 1) const; + + /* + * Evaluate the gradient of a function for a particular batch-size. + * + * @param coordinates The function coordinates. + * @param begin The first function. + * @param gradient The function gradient. + */ + void Gradient(const arma::mat& coordinates, + const size_t begin, + arma::sp_mat& gradient) const; + + /* + * Evaluate the gradient of a function with the given coordinates. + * + * @param coordinates The function coordinates. + * @param gradient The function gradient. + */ + void Gradient(const arma::mat& coordinates, arma::mat& gradient) const; + + private: + //! Locally-stored Initial point. + arma::mat initialPoint; + + //! //! Number of dimensions for the function. + size_t n; + + //! For shuffling. + arma::Row visitationOrder; +}; + +} // namespace test +} // namespace optimization +} // namespace mlpack + +#endif // MLPACK_CORE_OPTIMIZERS_PROBLEMS_GENERALIZED_ROSENBROCK_FUNCTION_HPP diff --git a/src/mlpack/core/optimizers/problems/matyas_function.cpp b/src/mlpack/core/optimizers/problems/matyas_function.cpp new file mode 100644 index 0000000000..6d4fef8de9 --- /dev/null +++ b/src/mlpack/core/optimizers/problems/matyas_function.cpp @@ -0,0 +1,58 @@ +/** + * @file matyas_function.cpp + * @author Marcus Edel + * + * Implementation of the Matyas function. + * + * mlpack 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 mlpack. If not, see + * http://www.opensource.org/licenses/BSD-3-Clause for more information. + */ +#include "matyas_function.hpp" + +using namespace mlpack; +using namespace mlpack::optimization; +using namespace mlpack::optimization::test; + +MatyasFunction::MatyasFunction() { /* Nothing to do here */ } + +void MatyasFunction::Shuffle() { /* Nothing to do here */ } + +double MatyasFunction::Evaluate(const arma::mat& coordinates, + const size_t /* begin */, + const size_t /* batchSize */) const +{ + // For convenience; we assume these temporaries will be optimized out. + const double x1 = coordinates(0); + const double x2 = coordinates(1); + + const double objective = 0.26 * (pow(x1, 2) + std::pow(x2, 2)) - + 0.48 * x1 * x2; + + return objective; +} + +double MatyasFunction::Evaluate(const arma::mat& coordinates) const +{ + return Evaluate(coordinates, 0, NumFunctions()); +} + +void MatyasFunction::Gradient(const arma::mat& coordinates, + const size_t /* begin */, + arma::mat& gradient, + const size_t /* batchSize */) const +{ + // For convenience; we assume these temporaries will be optimized out. + const double x1 = coordinates(0); + const double x2 = coordinates(1); + + gradient.set_size(2, 1); + gradient(0) = 0.52 * x1 - 48 * x2; + gradient(1) = 0.52 * x2 - 0.48 * x1; +} + +void MatyasFunction::Gradient(const arma::mat& coordinates, arma::mat& gradient) +{ + Gradient(coordinates, 0, gradient, NumFunctions()); +} diff --git a/src/mlpack/core/optimizers/problems/matyas_function.hpp b/src/mlpack/core/optimizers/problems/matyas_function.hpp new file mode 100644 index 0000000000..be5b5d35ad --- /dev/null +++ b/src/mlpack/core/optimizers/problems/matyas_function.hpp @@ -0,0 +1,105 @@ +/** + * @file matyas_function.hpp + * @author Marcus Edel + * + * Definition of the Matyas function. + * + * mlpack 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 mlpack. If not, see + * http://www.opensource.org/licenses/BSD-3-Clause for more information. + */ +#ifndef MLPACK_CORE_OPTIMIZERS_PROBLEMS_MATYAS_FUNCTION_HPP +#define MLPACK_CORE_OPTIMIZERS_PROBLEMS_MATYAS_FUNCTION_HPP + +#include + +namespace mlpack { +namespace optimization { +namespace test { + +/** + * The Matyas function, defined by + * + * \f[ + * f(x) = 0.26 * (x_1^2 + x_2^2) - 0.48 * x_1 * x_2 + * \f] + * + * This should optimize to f(x) = 0, at x = [0, 0]. + * + * For more information, please refer to: + * + * @code + * @article{Jamil2013, + * title = {A Literature Survey of Benchmark Functions For Global + * Optimization Problems}, + * author = {Momin Jamil and Xin{-}She Yang}, + * journal = {CoRR}, + * year = {2013}, + * url = {http://arxiv.org/abs/1308.4008} + * } + * @endcode + */ +class MatyasFunction +{ + public: + //! Initialize the MatyasFunction. + MatyasFunction(); + + /** + * Shuffle the order of function visitation. This may be called by the + * optimizer. + */ + void Shuffle(); + + //! Return 1 (the number of functions). + size_t NumFunctions() const { return 1; } + + //! Get the starting point. + arma::mat GetInitialPoint() const { return arma::mat("-3; 3"); } + + /* + * Evaluate a function for a particular batch-size. + * + * @param coordinates The function coordinates. + * @param begin The first function. + * @param batchSize Number of points to process. + */ + double Evaluate(const arma::mat& coordinates, + const size_t begin, + const size_t batchSize) const; + + /* + * Evaluate a function with the given coordinates. + * + * @param coordinates The function coordinates. + */ + double Evaluate(const arma::mat& coordinates) const; + + /* + * Evaluate the gradient of a function for a particular batch-size. + * + * @param coordinates The function coordinates. + * @param begin The first function. + * @param gradient The function gradient. + * @param batchSize Number of points to process. + */ + void Gradient(const arma::mat& coordinates, + const size_t begin, + arma::mat& gradient, + const size_t batchSize) const; + + /* + * Evaluate the gradient of a function with the given coordinates. + * + * @param coordinates The function coordinates. + * @param gradient The function gradient. + */ + void Gradient(const arma::mat& coordinates, arma::mat& gradient); +}; + +} // namespace test +} // namespace optimization +} // namespace mlpack + +#endif // MLPACK_CORE_OPTIMIZERS_PROBLEMS_MATYAS_FUNCTION_HPP diff --git a/src/mlpack/core/optimizers/problems/mc_cormick_function.cpp b/src/mlpack/core/optimizers/problems/mc_cormick_function.cpp new file mode 100644 index 0000000000..9ef4088d5c --- /dev/null +++ b/src/mlpack/core/optimizers/problems/mc_cormick_function.cpp @@ -0,0 +1,59 @@ +/** + * @file mc_cormick_function.cpp + * @author Marcus Edel + * + * Implementation of the McCormick function. + * + * mlpack 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 mlpack. If not, see + * http://www.opensource.org/licenses/BSD-3-Clause for more information. + */ +#include "mc_cormick_function.hpp" + +using namespace mlpack; +using namespace mlpack::optimization; +using namespace mlpack::optimization::test; + +McCormickFunction::McCormickFunction() { /* Nothing to do here */ } + +void McCormickFunction::Shuffle() { /* Nothing to do here */ } + +double McCormickFunction::Evaluate(const arma::mat& coordinates, + const size_t /* begin */, + const size_t /* batchSize */) const +{ + // For convenience; we assume these temporaries will be optimized out. + const double x1 = coordinates(0); + const double x2 = coordinates(1); + + const double objective = std::sin(x1 + x2) + std::pow(x1 - x2, 2) - + 1.5 * x1 + 2.5 * x2 + 1; + + return objective; +} + +double McCormickFunction::Evaluate(const arma::mat& coordinates) const +{ + return Evaluate(coordinates, 0, NumFunctions()); +} + +void McCormickFunction::Gradient(const arma::mat& coordinates, + const size_t /* begin */, + arma::mat& gradient, + const size_t /* batchSize */) const +{ + // For convenience; we assume these temporaries will be optimized out. + const double x1 = coordinates(0); + const double x2 = coordinates(1); + + gradient.set_size(2, 1); + gradient(0) = std::cos(x1 + x2) + 2 * x1 - 2 * x2 - 1.5; + gradient(1) = std::cos(x1 + x2) - 2 * x1 + 2 * x2 + 2.5; +} + +void McCormickFunction::Gradient(const arma::mat& coordinates, + arma::mat& gradient) +{ + Gradient(coordinates, 0, gradient, NumFunctions()); +} diff --git a/src/mlpack/core/optimizers/problems/mc_cormick_function.hpp b/src/mlpack/core/optimizers/problems/mc_cormick_function.hpp new file mode 100644 index 0000000000..0b91d22b71 --- /dev/null +++ b/src/mlpack/core/optimizers/problems/mc_cormick_function.hpp @@ -0,0 +1,105 @@ +/** + * @file mc_cormick_function.hpp + * @author Marcus Edel + * + * Definition of the McCormick function. + * + * mlpack 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 mlpack. If not, see + * http://www.opensource.org/licenses/BSD-3-Clause for more information. + */ +#ifndef MLPACK_CORE_OPTIMIZERS_PROBLEMS_MC_CORMICK_FUNCTION_HPP +#define MLPACK_CORE_OPTIMIZERS_PROBLEMS_MC_CORMICK_FUNCTION_HPP + +#include + +namespace mlpack { +namespace optimization { +namespace test { + +/** + * The McCormick function, defined by + * + * \f[ + * f(x) = \sin(x_1 + x_2) + (x_1 - x_2)^2 - 1.5 * x_1 + 2.5 * x_2 + 1 + * \f] + * + * This should optimize to f(x) = -1.9133, at x = [-0.54719, -1.54719]. + * + * For more information, please refer to: + * + * @code + * @article{Jamil2013, + * title = {A Literature Survey of Benchmark Functions For Global + * Optimization Problems}, + * author = {Momin Jamil and Xin{-}She Yang}, + * journal = {CoRR}, + * year = {2013}, + * url = {http://arxiv.org/abs/1308.4008} + * } + * @endcode + */ +class McCormickFunction +{ + public: + //! Initialize the McCormickFunction. + McCormickFunction(); + + /** + * Shuffle the order of function visitation. This may be called by the + * optimizer. + */ + void Shuffle(); + + //! Return 1 (the number of functions). + size_t NumFunctions() const { return 1; } + + //! Get the starting point. + arma::mat GetInitialPoint() const { return arma::mat("-2; 4"); } + + /* + * Evaluate a function for a particular batch-size. + * + * @param coordinates The function coordinates. + * @param begin The first function. + * @param batchSize Number of points to process. + */ + double Evaluate(const arma::mat& coordinates, + const size_t begin, + const size_t batchSize) const; + + /* + * Evaluate a function with the given coordinates. + * + * @param coordinates The function coordinates. + */ + double Evaluate(const arma::mat& coordinates) const; + + /* + * Evaluate the gradient of a function for a particular batch-size. + * + * @param coordinates The function coordinates. + * @param begin The first function. + * @param gradient The function gradient. + * @param batchSize Number of points to process. + */ + void Gradient(const arma::mat& coordinates, + const size_t begin, + arma::mat& gradient, + const size_t batchSize) const; + + /* + * Evaluate the gradient of a function with the given coordinates. + * + * @param coordinates The function coordinates. + * @param gradient The function gradient. + */ + void Gradient(const arma::mat& coordinates, arma::mat& gradient); +}; + +} // namespace test +} // namespace optimization +} // namespace mlpack + +#endif // MLPACK_CORE_OPTIMIZERS_PROBLEMS_MC_CORMICK_FUNCTION_HPP diff --git a/src/mlpack/core/optimizers/problems/rastrigin_function.cpp b/src/mlpack/core/optimizers/problems/rastrigin_function.cpp new file mode 100644 index 0000000000..65fa3bb85f --- /dev/null +++ b/src/mlpack/core/optimizers/problems/rastrigin_function.cpp @@ -0,0 +1,73 @@ +/** + * @file rastrigin_function.cpp + * @author Marcus Edel + * + * Implementation of the Rastrigin function. + * + * mlpack 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 mlpack. If not, see + * http://www.opensource.org/licenses/BSD-3-Clause for more information. + */ +#include "rastrigin_function.hpp" + +using namespace mlpack; +using namespace mlpack::optimization; +using namespace mlpack::optimization::test; + +RastriginFunction::RastriginFunction(const size_t n) : + n(n), + visitationOrder(arma::linspace >(0, n - 1, n)) + +{ + initialPoint.set_size(n, 1); + initialPoint.fill(-3); +} + +void RastriginFunction::Shuffle() +{ + visitationOrder = arma::shuffle( + arma::linspace >(0, n - 1, n)); +} + +double RastriginFunction::Evaluate(const arma::mat& coordinates, + const size_t begin, + const size_t batchSize) const +{ + double objective = 0.0; + for (size_t j = begin; j < begin + batchSize; ++j) + { + const size_t p = visitationOrder[j]; + objective += std::pow(coordinates(p), 2) - 10.0 * + std::cos(2.0 * M_PI * coordinates(p)); + } + objective += 10.0 * n; + + return objective; +} + +double RastriginFunction::Evaluate(const arma::mat& coordinates) const +{ + return Evaluate(coordinates, 0, NumFunctions()); +} + +void RastriginFunction::Gradient(const arma::mat& coordinates, + const size_t begin, + arma::mat& gradient, + const size_t batchSize) const +{ + gradient.zeros(n, 1); + + for (size_t j = begin; j < begin + batchSize; ++j) + { + const size_t p = visitationOrder[j]; + gradient(p) += (10.0 * n) * (2 * (coordinates(p) + 10.0 * M_PI * + std::sin(2.0 * M_PI * coordinates(p)))); + } +} + +void RastriginFunction::Gradient(const arma::mat& coordinates, + arma::mat& gradient) +{ + Gradient(coordinates, 0, gradient, NumFunctions()); +} diff --git a/src/mlpack/core/optimizers/problems/rastrigin_function.hpp b/src/mlpack/core/optimizers/problems/rastrigin_function.hpp new file mode 100644 index 0000000000..522a10dd50 --- /dev/null +++ b/src/mlpack/core/optimizers/problems/rastrigin_function.hpp @@ -0,0 +1,117 @@ +/** + * @file rastrigin_function.hpp + * @author Marcus Edel + * + * Definition of the Rastrigin function. + * + * mlpack 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 mlpack. If not, see + * http://www.opensource.org/licenses/BSD-3-Clause for more information. + */ +#ifndef MLPACK_CORE_OPTIMIZERS_PROBLEMS_RASTRIGIN_FUNCTION_HPP +#define MLPACK_CORE_OPTIMIZERS_PROBLEMS_RASTRIGIN_FUNCTION_HPP + +#include + +namespace mlpack { +namespace optimization { +namespace test { + +/** + * The Rastrigin function, defined by + * + * \f[ + * f(x) = 10 * d * \sum_{i=1}^{d} x_i^2 - 10 * \cos(2 * \pi * x_i) + * \f] + * + * This should optimize to f(x) = 0 + * at x = [0, ..., 0]. + * + * For more information, please refer to: + * + * @code + * @article{Jamil2013, + * title = {Systems of extremal control}, + * author = {Rastrigin, L. A.}, + * journal = {Mir}, + * year = {1974} + * } + * @endcode + */ +class RastriginFunction +{ + public: + /* + * Initialize the RastriginFunction. + * + * @param n Number of dimensions for the function. + */ + RastriginFunction(const size_t n); + + /** + * Shuffle the order of function visitation. This may be called by the + * optimizer. + */ + void Shuffle(); + + //! Return 1 (the number of functions). + size_t NumFunctions() const { return n; } + + //! Get the starting point. + arma::mat GetInitialPoint() const { return initialPoint; } + + /* + * Evaluate a function for a particular batch-size. + * + * @param coordinates The function coordinates. + * @param begin The first function. + * @param batchSize Number of points to process. + */ + double Evaluate(const arma::mat& coordinates, + const size_t begin, + const size_t batchSize) const; + + /* + * Evaluate a function with the given coordinates. + * + * @param coordinates The function coordinates. + */ + double Evaluate(const arma::mat& coordinates) const; + + /* + * Evaluate the gradient of a function for a particular batch-size. + * + * @param coordinates The function coordinates. + * @param begin The first function. + * @param gradient The function gradient. + * @param batchSize Number of points to process. + */ + void Gradient(const arma::mat& coordinates, + const size_t begin, + arma::mat& gradient, + const size_t batchSize) const; + + /* + * Evaluate the gradient of a function with the given coordinates. + * + * @param coordinates The function coordinates. + * @param gradient The function gradient. + */ + void Gradient(const arma::mat& coordinates, arma::mat& gradient); + private: + //! Number of dimensions for the function. + size_t n; + + //! For shuffling. + arma::Row visitationOrder; + + //! Initial starting point. + arma::mat initialPoint; +}; + +} // namespace test +} // namespace optimization +} // namespace mlpack + +#endif // MLPACK_CORE_OPTIMIZERS_PROBLEMS_RASTRIGIN_FUNCTION_HPP diff --git a/src/mlpack/core/optimizers/problems/rosenbrock_function.cpp b/src/mlpack/core/optimizers/problems/rosenbrock_function.cpp new file mode 100644 index 0000000000..360712c39d --- /dev/null +++ b/src/mlpack/core/optimizers/problems/rosenbrock_function.cpp @@ -0,0 +1,60 @@ +/** + * @file rosenbrock_function.cpp + * @author Ryan Curtin + * @author Marcus Edel + * + * Implementation of the Rosenbrock function. + * + * mlpack 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 mlpack. If not, see + * http://www.opensource.org/licenses/BSD-3-Clause for more information. + */ +#include "rosenbrock_function.hpp" + +using namespace mlpack; +using namespace mlpack::optimization; +using namespace mlpack::optimization::test; + +RosenbrockFunction::RosenbrockFunction() { /* Nothing to do here */ } + +void RosenbrockFunction::Shuffle() { /* Nothing to do here */ } + +double RosenbrockFunction::Evaluate(const arma::mat& coordinates, + const size_t /* begin */, + const size_t /* batchSize */) const +{ + // For convenience; we assume these temporaries will be optimized out. + const double x1 = coordinates(0); + const double x2 = coordinates(1); + + const double objective = /* f1(x) */ 100 * std::pow(x2 - std::pow(x1, 2), 2) + + /* f2(x) */ std::pow(1 - x1, 2); + + return objective; +} + +double RosenbrockFunction::Evaluate(const arma::mat& coordinates) const +{ + return Evaluate(coordinates, 0, NumFunctions()); +} + +void RosenbrockFunction::Gradient(const arma::mat& coordinates, + const size_t /* begin */, + arma::mat& gradient, + const size_t /* batchSize */) const +{ + // For convenience; we assume these temporaries will be optimized out. + const double x1 = coordinates(0); + const double x2 = coordinates(1); + + gradient.set_size(2, 1); + gradient(0) = -2 * (1 - x1) + 400 * (std::pow(x1, 3) - x2 * x1); + gradient(1) = 200 * (x2 - std::pow(x1, 2)); +} + +void RosenbrockFunction::Gradient(const arma::mat& coordinates, + arma::mat& gradient) +{ + Gradient(coordinates, 0, gradient, 1); +} diff --git a/src/mlpack/core/optimizers/problems/rosenbrock_function.hpp b/src/mlpack/core/optimizers/problems/rosenbrock_function.hpp new file mode 100644 index 0000000000..9079b10762 --- /dev/null +++ b/src/mlpack/core/optimizers/problems/rosenbrock_function.hpp @@ -0,0 +1,108 @@ +/** + * @file rosenbrock_function.hpp + * @author Ryan Curtin + * @author Marcus Edel + * + * Definition of the Rosenbrock function. + * + * mlpack 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 mlpack. If not, see + * http://www.opensource.org/licenses/BSD-3-Clause for more information. + */ +#ifndef MLPACK_CORE_OPTIMIZERS_PROBLEMS_ROSENBROCK_FUNCTION_HPP +#define MLPACK_CORE_OPTIMIZERS_PROBLEMS_ROSENBROCK_FUNCTION_HPP + +#include + +namespace mlpack { +namespace optimization { +namespace test { + +/** + * The Rosenbrock function, defined by: + * + * f(x) = f1(x) + f2(x) + * f1(x) = 100 (x2 - x1^2)^2 + * f2(x) = (1 - x1)^2 + * x_0 = [-1.2, 1] + * + * This should optimize to f(x) = 0, at x = [1, 1]. + * + * For more information, please refer to: + * + * @code + * @article{Rosenbrock1960, + * title = {An Automatic Method for Finding the Greatest or Least Value of a + * Function}, + * author = {Rosenbrock, H. H.}, + * journal = {The Computer Journal}, + * number = {3}, + * pages = {175--184}, + * year = {1960}, + * } + * @endcode + */ +class RosenbrockFunction +{ + public: + //! Initialize the RosenbrockFunction. + RosenbrockFunction(); + + /** + * Shuffle the order of function visitation. This may be called by the + * optimizer. + */ + void Shuffle(); + + //! Return 1 (the number of functions). + size_t NumFunctions() const { return 1; } + + //! Get the starting point. + arma::mat GetInitialPoint() const { return arma::mat("-1.2; 1"); } + + /* + * Evaluate a function for a particular batch-size. + * + * @param coordinates The function coordinates. + * @param begin The first function. + * @param batchSize Number of points to process. + */ + double Evaluate(const arma::mat& coordinates, + const size_t begin, + const size_t batchSize) const; + + /* + * Evaluate a function with the given coordinates. + * + * @param coordinates The function coordinates. + */ + double Evaluate(const arma::mat& coordinates) const; + + /* + * Evaluate the gradient of a function for a particular batch-size. + * + * @param coordinates The function coordinates. + * @param begin The first function. + * @param gradient The function gradient. + * @param batchSize Number of points to process. + */ + void Gradient(const arma::mat& coordinates, + const size_t begin, + arma::mat& gradient, + const size_t batchSize) const; + + /* + * Evaluate the gradient of a function with the given coordinates. + * + * @param coordinates The function coordinates. + * @param gradient The function gradient. + */ + void Gradient(const arma::mat& coordinates, arma::mat& gradient); +}; + +} // namespace test +} // namespace optimization +} // namespace mlpack + +#endif // MLPACK_CORE_OPTIMIZERS_PROBLEMS_ROSENBROCK_FUNCTION_HPP diff --git a/src/mlpack/core/optimizers/problems/rosenbrock_wood_function.cpp b/src/mlpack/core/optimizers/problems/rosenbrock_wood_function.cpp new file mode 100644 index 0000000000..4b62319d42 --- /dev/null +++ b/src/mlpack/core/optimizers/problems/rosenbrock_wood_function.cpp @@ -0,0 +1,64 @@ +/** + * @file rosenbrock_wood_function.cpp + * @author Ryan Curtin + * @author Marcus Edel + * + * Implementation of the Rosenbrock-Wood function. + * + * mlpack 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 mlpack. If not, see + * http://www.opensource.org/licenses/BSD-3-Clause for more information. + */ +#include "rosenbrock_wood_function.hpp" + +using namespace mlpack; +using namespace mlpack::optimization; +using namespace mlpack::optimization::test; + +RosenbrockWoodFunction::RosenbrockWoodFunction() : rf(4), wf() +{ + initialPoint.set_size(4, 2); + initialPoint.col(0) = rf.GetInitialPoint(); + initialPoint.col(1) = wf.GetInitialPoint(); +} + +void RosenbrockWoodFunction::Shuffle() { /* Nothing to do here */ } + +double RosenbrockWoodFunction::Evaluate(const arma::mat& coordinates, + const size_t /* begin */, + const size_t /* batchSize */) const +{ + const double objective = rf.Evaluate(coordinates.col(0)) + + wf.Evaluate(coordinates.col(1)); + + return objective; +} + +double RosenbrockWoodFunction::Evaluate(const arma::mat& coordinates) const +{ + return Evaluate(coordinates, 0, NumFunctions()); +} + +void RosenbrockWoodFunction::Gradient(const arma::mat& coordinates, + const size_t /* begin */, + arma::mat& gradient, + const size_t /* batchSize */) const +{ + gradient.set_size(4, 2); + + arma::vec grf(4); + arma::vec gwf(4); + + rf.Gradient(coordinates.col(0), grf); + wf.Gradient(coordinates.col(1), gwf); + + gradient.col(0) = grf; + gradient.col(1) = gwf; +} + +void RosenbrockWoodFunction::Gradient(const arma::mat& coordinates, + arma::mat& gradient) +{ + Gradient(coordinates, 0, gradient, 1); +} diff --git a/src/mlpack/core/optimizers/problems/rosenbrock_wood_function.hpp b/src/mlpack/core/optimizers/problems/rosenbrock_wood_function.hpp new file mode 100644 index 0000000000..34d7333f7e --- /dev/null +++ b/src/mlpack/core/optimizers/problems/rosenbrock_wood_function.hpp @@ -0,0 +1,101 @@ +/** + * @file rosenbrock_wood_function.hpp + * @author Ryan Curtin + * @author Marcus Edel + * + * Definition of the Rosenbrock-Wood function. + * + * mlpack 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 mlpack. If not, see + * http://www.opensource.org/licenses/BSD-3-Clause for more information. + */ +#ifndef MLPACK_CORE_OPTIMIZERS_PROBLEMS_ROSENBROCK_WOOD_FUNCTION_HPP +#define MLPACK_CORE_OPTIMIZERS_PROBLEMS_ROSENBROCK_WOOD_FUNCTION_HPP + +#include + +#include +#include + +namespace mlpack { +namespace optimization { +namespace test { + +/** + * The Generalized Rosenbrock function in 4 dimensions with the Wood Function in + * four dimensions. In this function we are actually optimizing a 2x4 matrix of + * coordinates, not a vector. + */ +class RosenbrockWoodFunction +{ + public: + //! Initialize the RosenbrockWoodFunction. + RosenbrockWoodFunction(); + + /** + * Shuffle the order of function visitation. This may be called by the + * optimizer. + */ + void Shuffle(); + + //! Return 1 (the number of functions). + size_t NumFunctions() const { return 1; } + + //! Get the starting point. + const arma::mat& GetInitialPoint() const { return initialPoint; } + + /* + * Evaluate a function for a particular batch-size. + * + * @param coordinates The function coordinates. + * @param begin The first function. + * @param batchSize Number of points to process. + */ + double Evaluate(const arma::mat& coordinates, + const size_t begin, + const size_t batchSize) const; + + /* + * Evaluate a function with the given coordinates. + * + * @param coordinates The function coordinates. + */ + double Evaluate(const arma::mat& coordinates) const; + + /* + * Evaluate the gradient of a function for a particular batch-size. + * + * @param coordinates The function coordinates. + * @param begin The first function. + * @param gradient The function gradient. + * @param batchSize Number of points to process. + */ + void Gradient(const arma::mat& coordinates, + const size_t begin, + arma::mat& gradient, + const size_t batchSize) const; + + /* + * Evaluate the gradient of a function with the given coordinates. + * + * @param coordinates The function coordinates. + * @param gradient The function gradient. + */ + void Gradient(const arma::mat& coordinates, arma::mat& gradient); + private: + //! Locally-stored initial point. + arma::mat initialPoint; + + //! Locally-stored Generalized-Rosenbrock function. + GeneralizedRosenbrockFunction rf; + + //! Locally-stored Wood function. + WoodFunction wf; +}; + +} // namespace test +} // namespace optimization +} // namespace mlpack + +#endif // MLPACK_CORE_OPTIMIZERS_PROBLEMS_ROSENBROCK_WOOD_FUNCTION_HPP diff --git a/src/mlpack/core/optimizers/problems/schwefel_function.cpp b/src/mlpack/core/optimizers/problems/schwefel_function.cpp new file mode 100644 index 0000000000..8e0ca9336c --- /dev/null +++ b/src/mlpack/core/optimizers/problems/schwefel_function.cpp @@ -0,0 +1,74 @@ +/** + * @file schwefel_function.cpp + * @author Marcus Edel + * + * Implementation of the Schwefel function. + * + * mlpack 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 mlpack. If not, see + * http://www.opensource.org/licenses/BSD-3-Clause for more information. + */ +#include "schwefel_function.hpp" + +using namespace mlpack; +using namespace mlpack::optimization; +using namespace mlpack::optimization::test; + +SchwefelFunction::SchwefelFunction(const size_t n) : + n(n), + visitationOrder(arma::linspace >(0, n - 1, n)) + +{ + initialPoint.set_size(n, 1); + initialPoint.fill(-300); +} + +void SchwefelFunction::Shuffle() +{ + visitationOrder = arma::shuffle( + arma::linspace >(0, n - 1, n)); +} + +double SchwefelFunction::Evaluate(const arma::mat& coordinates, + const size_t begin, + const size_t batchSize) const +{ + double objective = 0.0; + for (size_t j = begin; j < begin + batchSize; ++j) + { + const size_t p = visitationOrder[j]; + objective += coordinates(p) * std::sin(std::sqrt(std::abs(coordinates(p)))); + } + objective -= 418.9829 * batchSize; + + return objective; +} + +double SchwefelFunction::Evaluate(const arma::mat& coordinates) const +{ + return Evaluate(coordinates, 0, NumFunctions()); +} + +void SchwefelFunction::Gradient(const arma::mat& coordinates, + const size_t begin, + arma::mat& gradient, + const size_t batchSize) const +{ + gradient.zeros(n, 1); + + for (size_t j = begin; j < begin + batchSize; ++j) + { + const size_t p = visitationOrder[j]; + gradient(p) += (std::pow(coordinates(p), 2) * + std::cos(std::sqrt(std::abs(coordinates(p)))) / + (2 * std::pow(std::abs(coordinates(p)), 1.5)) + + std::sin(std::sqrt(std::abs(coordinates(p))))); + } +} + +void SchwefelFunction::Gradient(const arma::mat& coordinates, + arma::mat& gradient) +{ + Gradient(coordinates, 0, gradient, NumFunctions()); +} diff --git a/src/mlpack/core/optimizers/problems/schwefel_function.hpp b/src/mlpack/core/optimizers/problems/schwefel_function.hpp new file mode 100644 index 0000000000..e1e0dfdee1 --- /dev/null +++ b/src/mlpack/core/optimizers/problems/schwefel_function.hpp @@ -0,0 +1,117 @@ +/** + * @file schwefel_function.hpp + * @author Marcus Edel + * + * Definition of the Schwefel function. + * + * mlpack 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 mlpack. If not, see + * http://www.opensource.org/licenses/BSD-3-Clause for more information. + */ +#ifndef MLPACK_CORE_OPTIMIZERS_PROBLEMS_SCHWEFEL_FUNCTION_HPP +#define MLPACK_CORE_OPTIMIZERS_PROBLEMS_SCHWEFEL_FUNCTION_HPP + +#include + +namespace mlpack { +namespace optimization { +namespace test { + +/** + * The Schwefel function, defined by + * + * \f[ + * f(x) = 418.9829 * d * \sum_{i=1}^{d} x_i * \sin(\sqrt(\left|x\right|)) + * \f] + * + * This should optimize to f(x) = 0 + * at x = [420.9687, ..., 420.9687]. + * + * For more information, please refer to: + * + * @code + * @article{Jamil2013, + * title = {Systems of extremal control}, + * author = {Rastrigin, L. A.}, + * journal = {Mir}, + * year = {1974} + * } + * @endcode + */ +class SchwefelFunction +{ + public: + /* + * Initialize the SchwefelFunction. + * + * @param n Number of dimensions for the function. + */ + SchwefelFunction(const size_t n); + + /** + * Shuffle the order of function visitation. This may be called by the + * optimizer. + */ + void Shuffle(); + + //! Return 1 (the number of functions). + size_t NumFunctions() const { return n; } + + //! Get the starting point. + arma::mat GetInitialPoint() const { return initialPoint; } + + /* + * Evaluate a function for a particular batch-size. + * + * @param coordinates The function coordinates. + * @param begin The first function. + * @param batchSize Number of points to process. + */ + double Evaluate(const arma::mat& coordinates, + const size_t begin, + const size_t batchSize) const; + + /* + * Evaluate a function with the given coordinates. + * + * @param coordinates The function coordinates. + */ + double Evaluate(const arma::mat& coordinates) const; + + /* + * Evaluate the gradient of a function for a particular batch-size. + * + * @param coordinates The function coordinates. + * @param begin The first function. + * @param gradient The function gradient. + * @param batchSize Number of points to process. + */ + void Gradient(const arma::mat& coordinates, + const size_t begin, + arma::mat& gradient, + const size_t batchSize) const; + + /* + * Evaluate the gradient of a function with the given coordinates. + * + * @param coordinates The function coordinates. + * @param gradient The function gradient. + */ + void Gradient(const arma::mat& coordinates, arma::mat& gradient); + private: + //! Number of dimensions for the function. + size_t n; + + //! For shuffling. + arma::Row visitationOrder; + + //! Initial starting point. + arma::mat initialPoint; +}; + +} // namespace test +} // namespace optimization +} // namespace mlpack + +#endif // MLPACK_CORE_OPTIMIZERS_PROBLEMS_SCHWEFEL_FUNCTION_HPP diff --git a/src/mlpack/core/optimizers/sgd/test_function.cpp b/src/mlpack/core/optimizers/problems/sgd_test_function.cpp similarity index 97% rename from src/mlpack/core/optimizers/sgd/test_function.cpp rename to src/mlpack/core/optimizers/problems/sgd_test_function.cpp index 8415d98613..82d4862781 100644 --- a/src/mlpack/core/optimizers/sgd/test_function.cpp +++ b/src/mlpack/core/optimizers/problems/sgd_test_function.cpp @@ -1,5 +1,5 @@ /** - * @file test_function.cpp + * @file sgd_test_function.cpp * @author Ryan Curtin * * Implementation of very simple test function for stochastic gradient descent @@ -10,7 +10,7 @@ * 3-clause BSD license along with mlpack. If not, see * http://www.opensource.org/licenses/BSD-3-Clause for more information. */ -#include "test_function.hpp" +#include "sgd_test_function.hpp" using namespace mlpack; using namespace mlpack::optimization; diff --git a/src/mlpack/core/optimizers/sgd/test_function.hpp b/src/mlpack/core/optimizers/problems/sgd_test_function.hpp similarity index 89% rename from src/mlpack/core/optimizers/sgd/test_function.hpp rename to src/mlpack/core/optimizers/problems/sgd_test_function.hpp index 3a566e0ea6..bdfe7afdd7 100644 --- a/src/mlpack/core/optimizers/sgd/test_function.hpp +++ b/src/mlpack/core/optimizers/problems/sgd_test_function.hpp @@ -1,5 +1,5 @@ /** - * @file test_function.hpp + * @file sgd_test_function.hpp * @author Ryan Curtin * * Very simple test function for SGD. @@ -9,8 +9,8 @@ * 3-clause BSD license along with mlpack. If not, see * http://www.opensource.org/licenses/BSD-3-Clause for more information. */ -#ifndef MLPACK_CORE_OPTIMIZERS_SGD_TEST_FUNCTION_HPP -#define MLPACK_CORE_OPTIMIZERS_SGD_TEST_FUNCTION_HPP +#ifndef MLPACK_CORE_OPTIMIZERS_PROBLEMS_SGD_TEST_FUNCTION_HPP +#define MLPACK_CORE_OPTIMIZERS_PROBLEMS_SGD_TEST_FUNCTION_HPP #include @@ -45,7 +45,7 @@ class SGDTestFunction //! Evaluate a function. double Evaluate(const arma::mat& coordinates, const size_t i) const; - //! Evaluate a function for a particular batch-size + //! Evaluate a function for a particular batch-size. double Evaluate(const arma::mat& coordinates, const size_t begin, const size_t batchSize) const; diff --git a/src/mlpack/core/optimizers/problems/sphere_function.cpp b/src/mlpack/core/optimizers/problems/sphere_function.cpp new file mode 100644 index 0000000000..1b25a1b8f2 --- /dev/null +++ b/src/mlpack/core/optimizers/problems/sphere_function.cpp @@ -0,0 +1,76 @@ +/** + * @file sphere_function.cpp + * @author Marcus Edel + * + * Implementation of the Sphere function. + * + * mlpack 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 mlpack. If not, see + * http://www.opensource.org/licenses/BSD-3-Clause for more information. + */ +#include "sphere_function.hpp" + +using namespace mlpack; +using namespace mlpack::optimization; +using namespace mlpack::optimization::test; + +SphereFunction::SphereFunction(const size_t n) : + n(n), + visitationOrder(arma::linspace >(0, n - 1, n)) + +{ + initialPoint.set_size(n, 1); + + for (size_t i = 0; i < n; ++i) // Set to [-3.12 3.33 -3.12 3.33...]. + { + if (i % 2 == 1) + initialPoint(i) = 5; + else + initialPoint(i) = -5; + } +} + +void SphereFunction::Shuffle() +{ + visitationOrder = arma::shuffle( + arma::linspace >(0, n - 1, n)); +} + +double SphereFunction::Evaluate(const arma::mat& coordinates, + const size_t begin, + const size_t batchSize) const +{ + double objective = 0.0; + for (size_t j = begin; j < begin + batchSize; ++j) + { + const size_t p = visitationOrder[j]; + objective += std::pow(coordinates(p), 2); + } + + return objective; +} + +double SphereFunction::Evaluate(const arma::mat& coordinates) const +{ + return Evaluate(coordinates, 0, NumFunctions()); +} + +void SphereFunction::Gradient(const arma::mat& coordinates, + const size_t begin, + arma::mat& gradient, + const size_t batchSize) const +{ + gradient.zeros(n, 1); + + for (size_t j = begin; j < begin + batchSize; ++j) + { + const size_t p = visitationOrder[j]; + gradient(p) += 2.0 * coordinates[p]; + } +} + +void SphereFunction::Gradient(const arma::mat& coordinates, arma::mat& gradient) +{ + Gradient(coordinates, 0, gradient, NumFunctions()); +} diff --git a/src/mlpack/core/optimizers/problems/sphere_function.hpp b/src/mlpack/core/optimizers/problems/sphere_function.hpp new file mode 100644 index 0000000000..80b88a77b3 --- /dev/null +++ b/src/mlpack/core/optimizers/problems/sphere_function.hpp @@ -0,0 +1,118 @@ +/** + * @file sphere_function.hpp + * @author Marcus Edel + * + * Definition of the Sphere function. + * + * mlpack 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 mlpack. If not, see + * http://www.opensource.org/licenses/BSD-3-Clause for more information. + */ +#ifndef MLPACK_CORE_OPTIMIZERS_PROBLEMS_SPHERE_FUNCTION_HPP +#define MLPACK_CORE_OPTIMIZERS_PROBLEMS_SPHERE_FUNCTION_HPP + +#include + +namespace mlpack { +namespace optimization { +namespace test { + +/** + * The Sphere function, defined by + * + * \f[ + * f(x) = \sum_{i=1}^{d} x_i^2 + * \f] + * + * This should optimize to f(x) = 0, at x = [0, ..., 0]. + * + * For more information, please refer to: + * + * @code + * @article{Jamil2013, + * title = {A Literature Survey of Benchmark Functions For Global + * Optimization Problems}, + * author = {Momin Jamil and Xin{-}She Yang}, + * journal = {CoRR}, + * year = {2013}, + * url = {http://arxiv.org/abs/1308.4008} + * } + * @endcode + */ +class SphereFunction +{ + public: + /* + * Initialize the SphereFunction. + * + * @param n Number of dimensions for the function. + */ + SphereFunction(const size_t n); + + /** + * Shuffle the order of function visitation. This may be called by the + * optimizer. + */ + void Shuffle(); + + //! Return 1 (the number of functions). + size_t NumFunctions() const { return n; } + + //! Get the starting point. + arma::mat GetInitialPoint() const { return initialPoint; } + + /* + * Evaluate a function for a particular batch-size. + * + * @param coordinates The function coordinates. + * @param begin The first function. + * @param batchSize Number of points to process. + */ + double Evaluate(const arma::mat& coordinates, + const size_t begin, + const size_t batchSize) const; + + /* + * Evaluate a function with the given coordinates. + * + * @param coordinates The function coordinates. + */ + double Evaluate(const arma::mat& coordinates) const; + + /* + * Evaluate the gradient of a function for a particular batch-size. + * + * @param coordinates The function coordinates. + * @param begin The first function. + * @param gradient The function gradient. + * @param batchSize Number of points to process. + */ + void Gradient(const arma::mat& coordinates, + const size_t begin, + arma::mat& gradient, + const size_t batchSize) const; + + /* + * Evaluate the gradient of a function with the given coordinates. + * + * @param coordinates The function coordinates. + * @param gradient The function gradient. + */ + void Gradient(const arma::mat& coordinates, arma::mat& gradient); + private: + //! Number of dimensions for the function. + size_t n; + + //! For shuffling. + arma::Row visitationOrder; + + //! Initial starting point. + arma::mat initialPoint; +}; + +} // namespace test +} // namespace optimization +} // namespace mlpack + +#endif // MLPACK_CORE_OPTIMIZERS_PROBLEMS_SPHERE_FUNCTION_HPP diff --git a/src/mlpack/core/optimizers/problems/styblinski_tang_function.cpp b/src/mlpack/core/optimizers/problems/styblinski_tang_function.cpp new file mode 100644 index 0000000000..70473cece8 --- /dev/null +++ b/src/mlpack/core/optimizers/problems/styblinski_tang_function.cpp @@ -0,0 +1,73 @@ +/** + * @file styblinski_tang_function.cpp + * @author Marcus Edel + * + * Implementation of the Styblinski-Tang function. + * + * mlpack 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 mlpack. If not, see + * http://www.opensource.org/licenses/BSD-3-Clause for more information. + */ +#include "styblinski_tang_function.hpp" + +using namespace mlpack; +using namespace mlpack::optimization; +using namespace mlpack::optimization::test; + +StyblinskiTangFunction::StyblinskiTangFunction(const size_t n) : + n(n), + visitationOrder(arma::linspace >(0, n - 1, n)) + +{ + initialPoint.set_size(n, 1); + initialPoint.fill(-5); +} + +void StyblinskiTangFunction::Shuffle() +{ + visitationOrder = arma::shuffle( + arma::linspace >(0, n - 1, n)); +} + +double StyblinskiTangFunction::Evaluate(const arma::mat& coordinates, + const size_t begin, + const size_t batchSize) const +{ + double objective = 0.0; + for (size_t j = begin; j < begin + batchSize; ++j) + { + const size_t p = visitationOrder[j]; + objective += std::pow(coordinates(p), 4) - 16 * + std::pow(coordinates(p), 2) + 5 * coordinates(p); + } + objective /= 2; + + return objective; +} + +double StyblinskiTangFunction::Evaluate(const arma::mat& coordinates) const +{ + return Evaluate(coordinates, 0, NumFunctions()); +} + +void StyblinskiTangFunction::Gradient(const arma::mat& coordinates, + const size_t begin, + arma::mat& gradient, + const size_t batchSize) const +{ + gradient.zeros(n, 1); + + for (size_t j = begin; j < begin + batchSize; ++j) + { + const size_t p = visitationOrder[j]; + gradient(p) += 0.5 * (4 * std::pow(coordinates(p), 3) - + 32.0 * coordinates(p) + 5.0); + } +} + +void StyblinskiTangFunction::Gradient(const arma::mat& coordinates, + arma::mat& gradient) +{ + Gradient(coordinates, 0, gradient, NumFunctions()); +} diff --git a/src/mlpack/core/optimizers/problems/styblinski_tang_function.hpp b/src/mlpack/core/optimizers/problems/styblinski_tang_function.hpp new file mode 100644 index 0000000000..4e52f266f5 --- /dev/null +++ b/src/mlpack/core/optimizers/problems/styblinski_tang_function.hpp @@ -0,0 +1,119 @@ +/** + * @file styblinski_tang_function.hpp + * @author Marcus Edel + * + * Definition of the Styblinski-Tang function. + * + * mlpack 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 mlpack. If not, see + * http://www.opensource.org/licenses/BSD-3-Clause for more information. + */ +#ifndef MLPACK_CORE_OPTIMIZERS_PROBLEMS_STYBLINSKI_TANG_FUNCTION_HPP +#define MLPACK_CORE_OPTIMIZERS_PROBLEMS_STYBLINSKI_TANG_FUNCTION_HPP + +#include + +namespace mlpack { +namespace optimization { +namespace test { + +/** + * The Styblinski-Tang function, defined by + * + * \f[ + * f(x) = 0.5 * \sum_{i=1}^{d} x_i^4 - 16_i^2+5x_i + * \f] + * + * This should optimize to f(x) = -39.16599 * d + * at x = [-2.903534, ..., -2.903534]. + * + * For more information, please refer to: + * + * @code + * @article{Jamil2013, + * title = {A Literature Survey of Benchmark Functions For Global + * Optimization Problems}, + * author = {Momin Jamil and Xin{-}She Yang}, + * journal = {CoRR}, + * year = {2013}, + * url = {http://arxiv.org/abs/1308.4008} + * } + * @endcode + */ +class StyblinskiTangFunction +{ + public: + /* + * Initialize the StyblinskiTangFunction. + * + * @param n Number of dimensions for the function. + */ + StyblinskiTangFunction(const size_t n); + + /** + * Shuffle the order of function visitation. This may be called by the + * optimizer. + */ + void Shuffle(); + + //! Return 1 (the number of functions). + size_t NumFunctions() const { return n; } + + //! Get the starting point. + arma::mat GetInitialPoint() const { return initialPoint; } + + /* + * Evaluate a function for a particular batch-size. + * + * @param coordinates The function coordinates. + * @param begin The first function. + * @param batchSize Number of points to process. + */ + double Evaluate(const arma::mat& coordinates, + const size_t begin, + const size_t batchSize) const; + + /* + * Evaluate a function with the given coordinates. + * + * @param coordinates The function coordinates. + */ + double Evaluate(const arma::mat& coordinates) const; + + /* + * Evaluate the gradient of a function for a particular batch-size. + * + * @param coordinates The function coordinates. + * @param begin The first function. + * @param gradient The function gradient. + * @param batchSize Number of points to process. + */ + void Gradient(const arma::mat& coordinates, + const size_t begin, + arma::mat& gradient, + const size_t batchSize) const; + + /* + * Evaluate the gradient of a function with the given coordinates. + * + * @param coordinates The function coordinates. + * @param gradient The function gradient. + */ + void Gradient(const arma::mat& coordinates, arma::mat& gradient); + private: + //! Number of dimensions for the function. + size_t n; + + //! For shuffling. + arma::Row visitationOrder; + + //! Initial starting point. + arma::mat initialPoint; +}; + +} // namespace test +} // namespace optimization +} // namespace mlpack + +#endif // MLPACK_CORE_OPTIMIZERS_PROBLEMS_STYBLINSKI_TANG_FUNCTION_HPP diff --git a/src/mlpack/core/optimizers/problems/wood_function.cpp b/src/mlpack/core/optimizers/problems/wood_function.cpp new file mode 100644 index 0000000000..62f9c823f2 --- /dev/null +++ b/src/mlpack/core/optimizers/problems/wood_function.cpp @@ -0,0 +1,72 @@ +/** + * @file wood_function.cpp + * @author Ryan Curtin + * @author Marcus Edel + * + * Implementation of the Wood function. + * + * mlpack 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 mlpack. If not, see + * http://www.opensource.org/licenses/BSD-3-Clause for more information. + */ +#include "wood_function.hpp" + +using namespace mlpack; +using namespace mlpack::optimization; +using namespace mlpack::optimization::test; + +WoodFunction::WoodFunction() { /* Nothing to do here */ } + +void WoodFunction::Shuffle() { /* Nothing to do here */ } + +double WoodFunction::Evaluate(const arma::mat& coordinates, + const size_t /* begin */, + const size_t /* batchSize */) const +{ + // For convenience; we assume these temporaries will be optimized out. + const double x1 = coordinates(0); + const double x2 = coordinates(1); + const double x3 = coordinates(2); + const double x4 = coordinates(3); + + const double objective = /* f1(x) */ 100 * std::pow(x2 - std::pow(x1, 2), 2) + + /* f2(x) */ std::pow(1 - x1, 2) + + /* f3(x) */ 90 * std::pow(x4 - std::pow(x3, 2), 2) + + /* f4(x) */ std::pow(1 - x3, 2) + + /* f5(x) */ 10 * std::pow(x2 + x4 - 2, 2) + + /* f6(x) */ (1.0 / 10.0) * std::pow(x2 - x4, 2); + + return objective; +} + +double WoodFunction::Evaluate(const arma::mat& coordinates) const +{ + return Evaluate(coordinates, 0, NumFunctions()); +} + +void WoodFunction::Gradient(const arma::mat& coordinates, + const size_t /* begin */, + arma::mat& gradient, + const size_t /* batchSize */) const +{ + // For convenience; we assume these temporaries will be optimized out. + const double x1 = coordinates(0); + const double x2 = coordinates(1); + const double x3 = coordinates(2); + const double x4 = coordinates(3); + + gradient.set_size(4, 1); + gradient(0) = 400 * (std::pow(x1, 3) - x2 * x1) - 2 * (1 - x1); + gradient(1) = 200 * (x2 - std::pow(x1, 2)) + 20 * (x2 + x4 - 2) + + (1.0 / 5.0) * (x2 - x4); + gradient(2) = 360 * (std::pow(x3, 3) - x4 * x3) - 2 * (1 - x3); + gradient(3) = 180 * (x4 - std::pow(x3, 2)) + 20 * (x2 + x4 - 2) - + (1.0 / 5.0) * (x2 - x4); +} + +void WoodFunction::Gradient(const arma::mat& coordinates, arma::mat& gradient) + const +{ + Gradient(coordinates, 0, gradient, 1); +} diff --git a/src/mlpack/core/optimizers/problems/wood_function.hpp b/src/mlpack/core/optimizers/problems/wood_function.hpp new file mode 100644 index 0000000000..c8fb443b1b --- /dev/null +++ b/src/mlpack/core/optimizers/problems/wood_function.hpp @@ -0,0 +1,112 @@ +/** + * @file wood_function.hpp + * @author Ryan Curtin + * @author Marcus Edel + * + * Definition of the Wood function. + * + * mlpack 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 mlpack. If not, see + * http://www.opensource.org/licenses/BSD-3-Clause for more information. + */ +#ifndef MLPACK_CORE_OPTIMIZERS_PROBLEMS_WOOD_FUNCTION_HPP +#define MLPACK_CORE_OPTIMIZERS_PROBLEMS_WOOD_FUNCTION_HPP + +#include + +namespace mlpack { +namespace optimization { +namespace test { + +/** + * The Wood function, defined by + * f(x) = f1(x) + f2(x) + f3(x) + f4(x) + f5(x) + f6(x) + * f1(x) = 100 (x2 - x1^2)^2 + * f2(x) = (1 - x1)^2 + * f3(x) = 90 (x4 - x3^2)^2 + * f4(x) = (1 - x3)^2 + * f5(x) = 10 (x2 + x4 - 2)^2 + * f6(x) = (1 / 10) (x2 - x4)^2 + * x_0 = [-3, -1, -3, -1] + * + * This should optimize to f(x) = 0, at x = [1, 1, 1, 1]. + * + * For more information, please refer to: + * + * @code + * @article{Grippo1989, + * title = {A truncated Newton method with nonmonotone line search for + * unconstrained optimization}, + * author = {Grippo, L. and Lampariello, F. and Lucidi, S.}, + * journal = {Journal of Optimization Theory and Applications}, + * year = {1989}, + * volume = {60}, + * number = {3}, + * pages = {401--419}, + * } + * @endcode + */ +class WoodFunction +{ + public: + //! Initialize the WoodFunction. + WoodFunction(); + + /** + * Shuffle the order of function visitation. This may be called by the + * optimizer. + */ + void Shuffle(); + + //! Return 1 (the number of functions). + size_t NumFunctions() const { return 1; } + + //! Get the starting point. + arma::mat GetInitialPoint() const { return arma::mat("-3; -1; -3; -1"); } + + /* + * Evaluate a function for a particular batch-size. + * + * @param coordinates The function coordinates. + * @param begin The first function. + * @param batchSize Number of points to process. + */ + double Evaluate(const arma::mat& coordinates, + const size_t begin, + const size_t batchSize) const; + + /* + * Evaluate a function with the given coordinates. + * + * @param coordinates The function coordinates. + */ + double Evaluate(const arma::mat& coordinates) const; + + /* + * Evaluate the gradient of a function for a particular batch-size. + * + * @param coordinates The function coordinates. + * @param begin The first function. + * @param gradient The function gradient. + * @param batchSize Number of points to process. + */ + void Gradient(const arma::mat& coordinates, + const size_t begin, + arma::mat& gradient, + const size_t batchSize) const; + + /* + * Evaluate the gradient of a function with the given coordinates. + * + * @param coordinates The function coordinates. + * @param gradient The function gradient. + */ + void Gradient(const arma::mat& coordinates, arma::mat& gradient) const; +}; + +} // namespace test +} // namespace optimization +} // namespace mlpack + +#endif // MLPACK_CORE_OPTIMIZERS_PROBLEMS_WOOD_FUNCTION_HPP diff --git a/src/mlpack/core/optimizers/sgd/CMakeLists.txt b/src/mlpack/core/optimizers/sgd/CMakeLists.txt index 1916ec0fdc..343d024d90 100644 --- a/src/mlpack/core/optimizers/sgd/CMakeLists.txt +++ b/src/mlpack/core/optimizers/sgd/CMakeLists.txt @@ -5,8 +5,6 @@ set(SOURCES update_policies/vanilla_update.hpp sgd.hpp sgd_impl.hpp - test_function.hpp - test_function.cpp ) set(DIR_SRCS) diff --git a/src/mlpack/core/util/mlpack_main.hpp b/src/mlpack/core/util/mlpack_main.hpp index a26a5c6ff7..36df283031 100644 --- a/src/mlpack/core/util/mlpack_main.hpp +++ b/src/mlpack/core/util/mlpack_main.hpp @@ -74,6 +74,7 @@ int main(int argc, char** argv) #include #include +#include // These functions will do nothing. #define PRINT_PARAM_STRING(A) std::string(" ") @@ -138,6 +139,10 @@ static const std::string testName = ""; PARAM_FLAG("verbose", "Display informational messages and the full list of " "parameters and timers at the end of execution.", "v"); +PARAM_FLAG("copy_all_inputs", "If specified, all input parameters will be deep" + " copied before the method is run. This is useful for debugging problems " + "where the input parameters are being modified by the algorithm, but can " + "slow down the code.", ""); // Nothing else needs to be defined---the binding will use mlpackMain() as-is. diff --git a/src/mlpack/core/util/param.hpp b/src/mlpack/core/util/param.hpp index 2683d59b77..b812887fbd 100644 --- a/src/mlpack/core/util/param.hpp +++ b/src/mlpack/core/util/param.hpp @@ -1058,9 +1058,9 @@ using DatasetInfo = DatasetMapper; // There are no uses of required models, so that is not an option to this // macro (it would be easy to add). #define PARAM_MODEL(TYPE, ID, DESC, ALIAS, REQ, IN) \ - static mlpack::util::Option \ + static mlpack::util::Option \ JOIN(cli_option_dummy_model_, __COUNTER__) \ - (TYPE(), ID, DESC, ALIAS, #TYPE, REQ, IN, false, testName); + (nullptr, ID, DESC, ALIAS, #TYPE, REQ, IN, false, testName); #else // We have to do some really bizarre stuff since __COUNTER__ isn't defined. I // don't think we can absolutely guarantee success, but it should be "good @@ -1113,9 +1113,9 @@ using DatasetInfo = DatasetMapper; !TRANS, testName); #define PARAM_MODEL(TYPE, ID, DESC, ALIAS, REQ, IN) \ - static mlpack::util::Option \ + static mlpack::util::Option \ JOIN(JOIN(cli_option_dummy_object_model_, __LINE__), opt) \ - (TYPE(), ID, DESC, ALIAS, #TYPE, REQ, IN, false, \ + (nullptr, ID, DESC, ALIAS, #TYPE, REQ, IN, false, \ testName); #endif diff --git a/src/mlpack/methods/adaboost/adaboost_main.cpp b/src/mlpack/methods/adaboost/adaboost_main.cpp index e5209efb46..2634ff08a1 100644 --- a/src/mlpack/methods/adaboost/adaboost_main.cpp +++ b/src/mlpack/methods/adaboost/adaboost_main.cpp @@ -145,10 +145,11 @@ static void mlpackMain() ReportIgnoredParam({{ "test", false }}, "output"); - AdaBoostModel m; + AdaBoostModel* m; if (CLI::HasParam("training")) { mat trainingData = std::move(CLI::GetParam("training")); + m = new AdaBoostModel(); // Load labels. arma::Row labelsIn; @@ -172,28 +173,28 @@ static void mlpackMain() Row labels; // Normalize the labels. - data::NormalizeLabels(labelsIn, labels, m.Mappings()); + data::NormalizeLabels(labelsIn, labels, m->Mappings()); // Get other training parameters. const double tolerance = CLI::GetParam("tolerance"); const size_t iterations = (size_t) CLI::GetParam("iterations"); const string weakLearner = CLI::GetParam("weak_learner"); if (weakLearner == "decision_stump") - m.WeakLearnerType() = AdaBoostModel::WeakLearnerTypes::DECISION_STUMP; + m->WeakLearnerType() = AdaBoostModel::WeakLearnerTypes::DECISION_STUMP; else if (weakLearner == "perceptron") - m.WeakLearnerType() = AdaBoostModel::WeakLearnerTypes::PERCEPTRON; + m->WeakLearnerType() = AdaBoostModel::WeakLearnerTypes::PERCEPTRON; - const size_t numClasses = m.Mappings().n_elem; + const size_t numClasses = m->Mappings().n_elem; Log::Info << numClasses << " classes in dataset." << endl; Timer::Start("adaboost_training"); - m.Train(trainingData, labels, numClasses, iterations, tolerance); + m->Train(trainingData, labels, numClasses, iterations, tolerance); Timer::Stop("adaboost_training"); } else { // We have a specified input model. - m = std::move(CLI::GetParam("input_model")); + m = CLI::GetParam("input_model"); } // Perform classification, if desired. @@ -201,24 +202,21 @@ static void mlpackMain() { mat testingData = std::move(CLI::GetParam("test")); - if (testingData.n_rows != m.Dimensionality()) + if (testingData.n_rows != m->Dimensionality()) Log::Fatal << "Test data dimensionality (" << testingData.n_rows << ") " << "must be the same as the model dimensionality (" - << m.Dimensionality() << ")!" << endl; + << m->Dimensionality() << ")!" << endl; Row predictedLabels(testingData.n_cols); Timer::Start("adaboost_classification"); - m.Classify(testingData, predictedLabels); + m->Classify(testingData, predictedLabels); Timer::Stop("adaboost_classification"); Row results; - data::RevertLabels(predictedLabels, m.Mappings(), results); + data::RevertLabels(predictedLabels, m->Mappings(), results); - if (CLI::HasParam("output")) - CLI::GetParam>("output") = std::move(results); + CLI::GetParam>("output") = std::move(results); } - // Should we save the model, too? - if (CLI::HasParam("output_model")) - CLI::GetParam("output_model") = std::move(m); + CLI::GetParam("output_model") = m; } diff --git a/src/mlpack/methods/ann/layer/CMakeLists.txt b/src/mlpack/methods/ann/layer/CMakeLists.txt index 4729b2265c..50aea14eaf 100644 --- a/src/mlpack/methods/ann/layer/CMakeLists.txt +++ b/src/mlpack/methods/ann/layer/CMakeLists.txt @@ -65,6 +65,8 @@ set(SOURCES recurrent_attention_impl.hpp reinforce_normal.hpp reinforce_normal_impl.hpp + sigmoid_cross_entropy_error.hpp + sigmoid_cross_entropy_error_impl.hpp select.hpp select_impl.hpp sequential.hpp diff --git a/src/mlpack/methods/ann/layer/layer_types.hpp b/src/mlpack/methods/ann/layer/layer_types.hpp index ddf377a84d..327c1f94b0 100644 --- a/src/mlpack/methods/ann/layer/layer_types.hpp +++ b/src/mlpack/methods/ann/layer/layer_types.hpp @@ -33,6 +33,7 @@ #include #include #include +#include #include // Convolution modules. @@ -116,6 +117,7 @@ using LayerTypes = boost::variant< Recurrent*, RecurrentAttention*, ReinforceNormal*, + SigmoidCrossEntropyError*, Select*, Sequential*, VRClassReward* diff --git a/src/mlpack/methods/ann/layer/log_softmax_impl.hpp b/src/mlpack/methods/ann/layer/log_softmax_impl.hpp index 8e50fdf4c7..d4ca9c53ae 100644 --- a/src/mlpack/methods/ann/layer/log_softmax_impl.hpp +++ b/src/mlpack/methods/ann/layer/log_softmax_impl.hpp @@ -32,8 +32,8 @@ void LogSoftMax::Forward( arma::mat maxInput = arma::repmat(arma::max(input), input.n_rows, 1); output = (maxInput - input); - // Approximation of the hyperbolic tangent. The acuracy however is - // about 0.00001 lower as using tanh. Credits go to Leon Bottou. + // Approximation of the base-e exponential function. The acuracy however is + // about 0.00001 lower as using exp. Credits go to Leon Bottou. output.transform([](double x) { //! Fast approximation of exp(-x) for x positive. diff --git a/src/mlpack/methods/ann/layer/sigmoid_cross_entropy_error.hpp b/src/mlpack/methods/ann/layer/sigmoid_cross_entropy_error.hpp new file mode 100644 index 0000000000..f0dd42dfa1 --- /dev/null +++ b/src/mlpack/methods/ann/layer/sigmoid_cross_entropy_error.hpp @@ -0,0 +1,118 @@ +/** + * @file sigmoid_cross_entropy_error.hpp + * @author Kris Singh and Shikhar Jaiswal + * + * Definition of the cross-entropy with logit performance function. + * + * mlpack 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 mlpack. If not, see + * http://www.opensource.org/licenses/BSD-3-Clause for more information. + */ +#ifndef MLPACK_METHODS_ANN_LAYER_SIGMOID_CROSS_ENTROPY_ERROR_HPP +#define MLPACK_METHODS_ANN_LAYER_SIGMOID_CROSS_ENTROPY_ERROR_HPP + +#include + +namespace mlpack { +namespace ann /** Artificial Neural Network. */ { + +/** + * The SigmoidCrossEntropyError performance function measures the network's + * performance according to the cross-entropy function between the input and + * target distributions. This function calculates the cross entropy + * given the real values instead of providing the sigmoid activations. + * The function uses this equivalent formulation: + * \f$max(x, 0) - x * z + \log(1 + e^{-|x|})\f$ + * where x = input and z = target. + * + * For more information, see the following paper. + * + * @code + * @article{1702.05659, + * title={On Loss Functions for Deep Neural Networks in Classification}, + * author={Katarzyna Janocha, Wojciech Marian Czarnecki}, + * url = {http://arxiv.org/abs/1702.05659}, + * journal = {CoRR}, + * eprint={arXiv:1702.05659}, + * year={2017} + * } + * @endcode + * + * @tparam InputDataType Type of the input data (arma::colvec, arma::mat, + * arma::sp_mat or arma::cube). + * @tparam OutputDataType Type of the output data (arma::colvec, arma::mat, + * arma::sp_mat or arma::cube). + */ +template < + typename InputDataType = arma::mat, + typename OutputDataType = arma::mat +> +class SigmoidCrossEntropyError +{ + public: + /** + * Create the SigmoidCrossEntropyError object. + */ + SigmoidCrossEntropyError(); + + /* + * Computes the Sigmoid CrossEntropy Error functions. + * + * @param input Input data used for evaluating the specified function. + * @param output Resulting output activation. + */ + template + inline double Forward(const arma::Mat&& input, + const arma::Mat&& target); + /** + * Ordinary feed backward pass of a neural network. + * + * @param input The propagated input activation. + * @param target The target vector. + * @param output The calculated error. + */ + template + inline void Backward(const arma::Mat&& input, + const arma::Mat&& target, + arma::Mat&& output); + + //! Get the input parameter. + InputDataType& InputParameter() const { return inputParameter; } + //! Modify the input parameter. + InputDataType& InputParameter() { return inputParameter; } + + //! Get the output parameter. + OutputDataType& OutputParameter() const { return outputParameter; } + //! Modify the output parameter. + OutputDataType& OutputParameter() { return outputParameter; } + + //! Get the delta. + OutputDataType& Delta() const { return delta; } + //! Modify the delta. + OutputDataType& Delta() { return delta; } + + /** + * Serialize the layer. + */ + template + void serialize(Archive& ar, const unsigned int /* version */); + + private: + //! Locally-stored delta object. + OutputDataType delta; + + //! Locally-stored input parameter object. + InputDataType inputParameter; + + //! Locally-stored output parameter object. + OutputDataType outputParameter; +}; // class SigmoidCrossEntropy + +} // namespace ann +} // namespace mlpack + +// Include implementation. +#include "sigmoid_cross_entropy_error_impl.hpp" + +#endif diff --git a/src/mlpack/methods/ann/layer/sigmoid_cross_entropy_error_impl.hpp b/src/mlpack/methods/ann/layer/sigmoid_cross_entropy_error_impl.hpp new file mode 100644 index 0000000000..67af3710f6 --- /dev/null +++ b/src/mlpack/methods/ann/layer/sigmoid_cross_entropy_error_impl.hpp @@ -0,0 +1,65 @@ +/** + * @file sigmoid_cross_entropy_error_impl.hpp + * @author Kris Singh and Shikhar Jaiswal + * + * Implementation of the sigmoid cross entropy error performance function. + * + * mlpack 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 mlpack. If not, see + * http://www.opensource.org/licenses/BSD-3-Clause for more information. + */ +#ifndef MLPACK_METHODS_ANN_LAYER_SIGMOID_CROSS_ENTROPY_ERROR_IMPL_HPP +#define MLPACK_METHODS_ANN_LAYER_SIGMOID_CROSS_ENTROPY_ERROR_IMPL_HPP + +// In case it hasn't yet been included. +#include "sigmoid_cross_entropy_error.hpp" +#include + +namespace mlpack { +namespace ann /** Artificial Neural Network. */ { + +template +SigmoidCrossEntropyError +::SigmoidCrossEntropyError() +{ + // Nothing to do here. +} + +template +template +inline double SigmoidCrossEntropyError::Forward( + const arma::Mat&& input, const arma::Mat&& target) +{ + double maximum = 0; + for (size_t i = 0; i < input.n_elem; ++i) + { + maximum += std::max(input[i], 0.0) + + std::log(1 + std::exp(-std::abs(input[i]))); + } + return maximum - arma::accu(input % target); +} + +template +template +inline void SigmoidCrossEntropyError::Backward( + const arma::Mat&& input, + const arma::Mat&& target, + arma::Mat&& output) +{ + output = 1.0 / (1.0 + arma::exp(-input)) - target; +} + +template +template +void SigmoidCrossEntropyError::serialize( + Archive& /* ar */, + const unsigned int /* version */) +{ + // Nothing to do here +} + +} // namespace ann +} // namespace mlpack + +#endif diff --git a/src/mlpack/methods/approx_kfn/approx_kfn_main.cpp b/src/mlpack/methods/approx_kfn/approx_kfn_main.cpp index f73008583d..030fb90132 100644 --- a/src/mlpack/methods/approx_kfn/approx_kfn_main.cpp +++ b/src/mlpack/methods/approx_kfn/approx_kfn_main.cpp @@ -190,11 +190,12 @@ static void mlpackMain() } // Do the building of a model, if necessary. - ApproxKFNModel m; + ApproxKFNModel* m; arma::mat referenceSet; // This may be used at query time. if (CLI::HasParam("reference")) { referenceSet = std::move(CLI::GetParam("reference")); + m = new ApproxKFNModel(); const size_t numTables = (size_t) CLI::GetParam("num_tables"); const size_t numProjections = @@ -205,16 +206,16 @@ static void mlpackMain() { Timer::Start("drusilla_select_construct"); Log::Info << "Building DrusillaSelect model..." << endl; - m.type = 0; - m.ds = DrusillaSelect<>(referenceSet, numTables, numProjections); + m->type = 0; + m->ds = DrusillaSelect<>(referenceSet, numTables, numProjections); Timer::Stop("drusilla_select_construct"); } else { Timer::Start("qdafn_construct"); Log::Info << "Building QDAFN model..." << endl; - m.type = 1; - m.qdafn = QDAFN<>(referenceSet, numTables, numProjections); + m->type = 1; + m->qdafn = QDAFN<>(referenceSet, numTables, numProjections); Timer::Stop("qdafn_construct"); } Log::Info << "Model built." << endl; @@ -222,7 +223,7 @@ static void mlpackMain() else { // We must load the model from what was passed. - m = std::move(CLI::GetParam("input_model")); + m = CLI::GetParam("input_model"); } // Now, do we need to do any queries? @@ -238,12 +239,12 @@ static void mlpackMain() if (CLI::HasParam("query")) querySet = std::move(CLI::GetParam("query")); - if (m.type == 0) + if (m->type == 0) { Timer::Start("drusilla_select_search"); Log::Info << "Searching for " << k << " furthest neighbors with " << "DrusillaSelect..." << endl; - m.ds.Search(set, k, neighbors, distances); + m->ds.Search(set, k, neighbors, distances); Timer::Stop("drusilla_select_search"); } else @@ -251,7 +252,7 @@ static void mlpackMain() Timer::Start("qdafn_search"); Log::Info << "Searching for " << k << " furthest neighbors with " << "QDAFN..." << endl; - m.qdafn.Search(set, k, neighbors, distances); + m->qdafn.Search(set, k, neighbors, distances); Timer::Stop("qdafn_search"); } Log::Info << "Search complete." << endl; @@ -288,13 +289,9 @@ static void mlpackMain() } // Save results, if desired. - if (CLI::HasParam("neighbors")) - CLI::GetParam>("neighbors") = std::move(neighbors); - if (CLI::HasParam("distances")) - CLI::GetParam("distances") = std::move(distances); + CLI::GetParam>("neighbors") = std::move(neighbors); + CLI::GetParam("distances") = std::move(distances); } - // Should we save the model? - if (CLI::HasParam("output_model")) - CLI::GetParam("output_model") = std::move(m); + CLI::GetParam("output_model") = m; } diff --git a/src/mlpack/methods/approx_kfn/drusilla_select_impl.hpp b/src/mlpack/methods/approx_kfn/drusilla_select_impl.hpp index cfe646d749..4b02aaa713 100644 --- a/src/mlpack/methods/approx_kfn/drusilla_select_impl.hpp +++ b/src/mlpack/methods/approx_kfn/drusilla_select_impl.hpp @@ -95,7 +95,7 @@ void DrusillaSelect::Train( for (size_t i = 0; i < l; ++i) { // Pick best index. - arma::uword maxIndex; + arma::uword maxIndex = 0; norms.max(maxIndex); arma::vec line(refCopy.col(maxIndex) / arma::norm(refCopy.col(maxIndex))); diff --git a/src/mlpack/methods/cf/cf_main.cpp b/src/mlpack/methods/cf/cf_main.cpp index 2fedefb4ed..f09280dacd 100644 --- a/src/mlpack/methods/cf/cf_main.cpp +++ b/src/mlpack/methods/cf/cf_main.cpp @@ -115,7 +115,7 @@ PARAM_INT_IN("recommendations", "Number of recommendations to generate for each" PARAM_INT_IN("seed", "Set the random seed (0 uses std::time(NULL)).", "s", 0); -void ComputeRecommendations(CF& cf, +void ComputeRecommendations(CF* cf, const size_t numRecs, arma::Mat& recommendations) { @@ -132,16 +132,16 @@ void ComputeRecommendations(CF& cf, Log::Info << "Generating recommendations for " << users.n_elem << " users." << endl; - cf.GetRecommendations(numRecs, recommendations, users.row(0).t()); + cf->GetRecommendations(numRecs, recommendations, users.row(0).t()); } else { Log::Info << "Generating recommendations for all users." << endl; - cf.GetRecommendations(numRecs, recommendations); + cf->GetRecommendations(numRecs, recommendations); } } -void ComputeRMSE(CF& cf) +void ComputeRMSE(CF* cf) { // Now, compute each test point. arma::mat testData = std::move(CLI::GetParam("test")); @@ -156,7 +156,7 @@ void ComputeRMSE(CF& cf) // Now compute the RMSE. arma::vec predictions; - cf.Predict(combinations, predictions); + cf->Predict(combinations, predictions); // Compute the root of the sum of the squared errors, divide by the number of // points to get the RMSE. It turns out this is just the L2-norm divided by @@ -168,7 +168,7 @@ void ComputeRMSE(CF& cf) Log::Info << "RMSE is " << rmse << "." << endl; } -void PerformAction(CF& c) +void PerformAction(CF* c) { if (CLI::HasParam("query") || CLI::HasParam("all_user_recommendations")) { @@ -180,15 +180,13 @@ void PerformAction(CF& c) ComputeRecommendations(c, numRecs, recommendations); // Save the output. - if (CLI::HasParam("output")) - CLI::GetParam>("output") = recommendations; + CLI::GetParam>("output") = recommendations; } if (CLI::HasParam("test")) ComputeRMSE(c); - if (CLI::HasParam("output_model")) - CLI::GetParam("output_model") = std::move(c); + CLI::GetParam("output_model") = c; } template @@ -198,7 +196,7 @@ void PerformAction(Factorizer&& factorizer, { // Parameters for generating the CF object. const size_t neighborhood = (size_t) CLI::GetParam("neighborhood"); - CF c(dataset, factorizer, neighborhood, rank); + CF* c = new CF(dataset, factorizer, neighborhood, rank); PerformAction(c); } @@ -323,7 +321,7 @@ static void mlpackMain() else { // Load an input model. - CF c = std::move(CLI::GetParam("input_model")); + CF* c = std::move(CLI::GetParam("input_model")); PerformAction(c); } diff --git a/src/mlpack/methods/decision_stump/decision_stump_main.cpp b/src/mlpack/methods/decision_stump/decision_stump_main.cpp index 2bfe230043..aa4d9a67d8 100644 --- a/src/mlpack/methods/decision_stump/decision_stump_main.cpp +++ b/src/mlpack/methods/decision_stump/decision_stump_main.cpp @@ -116,9 +116,10 @@ static void mlpackMain() ReportIgnoredParam({{ "test", false }}, "predictions"); // We must either load a model, or train a new stump. - DSModel model; + DSModel* model; if (CLI::HasParam("training")) { + model = new DSModel(); mat trainingData = std::move(CLI::GetParam("training")); // Load labels, if necessary. @@ -140,18 +141,18 @@ static void mlpackMain() // Normalize the labels. Row labels; - data::NormalizeLabels(labelsIn, labels, model.mappings); + data::NormalizeLabels(labelsIn, labels, model->mappings); const size_t bucketSize = CLI::GetParam("bucket_size"); const size_t classes = labels.max() + 1; Timer::Start("training"); - model.stump.Train(trainingData, labels, classes, bucketSize); + model->stump.Train(trainingData, labels, classes, bucketSize); Timer::Stop("training"); } else { - model = std::move(CLI::GetParam("input_model")); + model = CLI::GetParam("input_model"); } // Now, do we need to do any testing? @@ -160,21 +161,21 @@ static void mlpackMain() // Load the test file. mat testingData = std::move(CLI::GetParam("test")); - if (testingData.n_rows <= model.stump.SplitDimension()) + if (testingData.n_rows <= model->stump.SplitDimension()) Log::Fatal << "Test data dimensionality (" << testingData.n_rows << ") " << "is too low; the trained stump requires at least " - << model.stump.SplitDimension() << " dimensions!" << endl; + << model->stump.SplitDimension() << " dimensions!" << endl; Row predictedLabels(testingData.n_cols); Timer::Start("testing"); - model.stump.Classify(testingData, predictedLabels); + model->stump.Classify(testingData, predictedLabels); Timer::Stop("testing"); // Denormalize predicted labels, if we want to save them. if (CLI::HasParam("predictions")) { Row actualLabels; - data::RevertLabels(predictedLabels, model.mappings, actualLabels); + data::RevertLabels(predictedLabels, model->mappings, actualLabels); // Save the predicted labels as output. CLI::GetParam>("predictions") = std::move(actualLabels); @@ -182,6 +183,5 @@ static void mlpackMain() } // Save the model, if desired. - if (CLI::HasParam("output_model")) - CLI::GetParam("output_model") = std::move(model); + CLI::GetParam("output_model") = model; } diff --git a/src/mlpack/methods/decision_tree/decision_tree_main.cpp b/src/mlpack/methods/decision_tree/decision_tree_main.cpp index e86f34042e..e4ee8fcedc 100644 --- a/src/mlpack/methods/decision_tree/decision_tree_main.cpp +++ b/src/mlpack/methods/decision_tree/decision_tree_main.cpp @@ -137,13 +137,14 @@ static void mlpackMain() "leaf size must be positive"); // Load the model or build the tree. - DecisionTreeModel model; + DecisionTreeModel* model; arma::mat trainingSet; arma::Row labels; if (CLI::HasParam("training")) { - model.info = std::move(std::get<0>(CLI::GetParam("training"))); + model = new DecisionTreeModel(); + model->info = std::move(std::get<0>(CLI::GetParam("training"))); trainingSet = std::move(std::get<1>(CLI::GetParam("training"))); if (CLI::HasParam("labels")) { @@ -169,12 +170,12 @@ static void mlpackMain() { arma::Row weights = std::move(CLI::GetParam>("weights")); - model.tree = DecisionTree<>(trainingSet, model.info, labels, + model->tree = DecisionTree<>(trainingSet, model->info, labels, numClasses, weights, minLeafSize); } else { - model.tree = DecisionTree<>(trainingSet, model.info, labels, + model->tree = DecisionTree<>(trainingSet, model->info, labels, numClasses, minLeafSize); } @@ -184,7 +185,7 @@ static void mlpackMain() arma::Row predictions; arma::mat probabilities; - model.tree.Classify(trainingSet, predictions, probabilities); + model->tree.Classify(trainingSet, predictions, probabilities); size_t correct = 0; for (size_t i = 0; i < trainingSet.n_cols; ++i) @@ -199,19 +200,19 @@ static void mlpackMain() } else { - model = std::move(CLI::GetParam("input_model")); + model = CLI::GetParam("input_model"); } // Do we need to get predictions? if (CLI::HasParam("test")) { - std::get<0>(CLI::GetRawParam("test")) = model.info; + std::get<0>(CLI::GetRawParam("test")) = model->info; arma::mat testPoints = std::get<1>(CLI::GetParam("test")); arma::Row predictions; arma::mat probabilities; - model.tree.Classify(testPoints, predictions, probabilities); + model->tree.Classify(testPoints, predictions, probabilities); // Do we need to calculate accuracy? if (CLI::HasParam("test_labels")) @@ -231,13 +232,10 @@ static void mlpackMain() } // Do we need to save outputs? - if (CLI::HasParam("predictions")) - CLI::GetParam>("predictions") = std::move(predictions); - if (CLI::HasParam("probabilities")) - CLI::GetParam("probabilities") = std::move(probabilities); + CLI::GetParam>("predictions") = predictions; + CLI::GetParam("probabilities") = probabilities; } // Do we need to save the model? - if (CLI::HasParam("output_model")) - CLI::GetParam("output_model") = std::move(model); + CLI::GetParam("output_model") = model; } diff --git a/src/mlpack/methods/det/det_main.cpp b/src/mlpack/methods/det/det_main.cpp index 29ecd56c99..a9cb4ec47c 100644 --- a/src/mlpack/methods/det/det_main.cpp +++ b/src/mlpack/methods/det/det_main.cpp @@ -168,7 +168,7 @@ static void mlpackMain() } else { - tree = &CLI::GetParam>("input_model"); + tree = CLI::GetParam*>("input_model"); } // Compute the density at the provided test points and output the density in @@ -213,8 +213,7 @@ static void mlpackMain() if (!ofs.is_open()) { Log::Warn << "Unable to open file '" << tagFile - << "' to save tag membership info." - << std::endl; + << "' to save tag membership info." << std::endl; } else if (CLI::HasParam("path_format")) { @@ -231,7 +230,7 @@ static void mlpackMain() else { Log::Warn << "Unknown path format specified: '" << pathFormat - << "'. Valid are: lr | lr-id | id-lr. Defaults to 'lr'." << endl; + << "'. Valid are: lr | lr-id | id-lr. Defaults to 'lr'." << endl; theFormat = PathCacher::FormatLR; } @@ -284,10 +283,5 @@ static void mlpackMain() } // Save the model, if desired. - if (CLI::HasParam("output_model")) - CLI::GetParam>("output_model") = std::move(*tree); - - // Clean up memory, if we need to. - if (!CLI::HasParam("input_model") && !CLI::HasParam("output_model")) - delete tree; + CLI::GetParam*>("output_model") = tree; } diff --git a/src/mlpack/methods/fastmks/fastmks_main.cpp b/src/mlpack/methods/fastmks/fastmks_main.cpp index f3b7333ebe..1d8e7e7fb6 100644 --- a/src/mlpack/methods/fastmks/fastmks_main.cpp +++ b/src/mlpack/methods/fastmks/fastmks_main.cpp @@ -111,10 +111,11 @@ static void mlpackMain() // Naive mode overrides single mode. ReportIgnoredParam({{ "naive", true }}, "single"); - FastMKSModel model; + FastMKSModel* model; arma::mat referenceData; if (CLI::HasParam("reference")) { + model = new FastMKSModel(); referenceData = std::move(CLI::GetParam("reference")); Log::Info << "Loaded reference data (" << referenceData.n_rows << " x " @@ -137,55 +138,55 @@ static void mlpackMain() if (kernelType == "linear") { LinearKernel lk; - model.KernelType() = FastMKSModel::LINEAR_KERNEL; - model.BuildModel(referenceData, lk, single, naive, base); + model->KernelType() = FastMKSModel::LINEAR_KERNEL; + model->BuildModel(referenceData, lk, single, naive, base); } else if (kernelType == "polynomial") { PolynomialKernel pk(degree, offset); - model.KernelType() = FastMKSModel::POLYNOMIAL_KERNEL; - model.BuildModel(referenceData, pk, single, naive, base); + model->KernelType() = FastMKSModel::POLYNOMIAL_KERNEL; + model->BuildModel(referenceData, pk, single, naive, base); } else if (kernelType == "cosine") { CosineDistance cd; - model.KernelType() = FastMKSModel::COSINE_DISTANCE; - model.BuildModel(referenceData, cd, single, naive, base); + model->KernelType() = FastMKSModel::COSINE_DISTANCE; + model->BuildModel(referenceData, cd, single, naive, base); } else if (kernelType == "gaussian") { GaussianKernel gk(bandwidth); - model.KernelType() = FastMKSModel::GAUSSIAN_KERNEL; - model.BuildModel(referenceData, gk, single, naive, base); + model->KernelType() = FastMKSModel::GAUSSIAN_KERNEL; + model->BuildModel(referenceData, gk, single, naive, base); } else if (kernelType == "epanechnikov") { EpanechnikovKernel ek(bandwidth); - model.KernelType() = FastMKSModel::EPANECHNIKOV_KERNEL; - model.BuildModel(referenceData, ek, single, naive, base); + model->KernelType() = FastMKSModel::EPANECHNIKOV_KERNEL; + model->BuildModel(referenceData, ek, single, naive, base); } else if (kernelType == "triangular") { TriangularKernel tk(bandwidth); - model.KernelType() = FastMKSModel::TRIANGULAR_KERNEL; - model.BuildModel(referenceData, tk, single, naive, base); + model->KernelType() = FastMKSModel::TRIANGULAR_KERNEL; + model->BuildModel(referenceData, tk, single, naive, base); } else if (kernelType == "hyptan") { HyperbolicTangentKernel htk(scale, offset); - model.KernelType() = FastMKSModel::HYPTAN_KERNEL; - model.BuildModel(referenceData, htk, single, naive, base); + model->KernelType() = FastMKSModel::HYPTAN_KERNEL; + model->BuildModel(referenceData, htk, single, naive, base); } } else { // Load model from file, then do whatever is necessary. - model = std::move(CLI::GetParam("input_model")); + model = CLI::GetParam("input_model"); } // Set search preferences. - model.Naive() = CLI::HasParam("naive"); - model.SingleMode() = CLI::HasParam("single"); + model->Naive() = CLI::HasParam("naive"); + model->SingleMode() = CLI::HasParam("single"); // Should we do search? if (CLI::HasParam("k")) @@ -202,23 +203,19 @@ static void mlpackMain() Log::Info << "Loaded query data (" << queryData.n_rows << " x " << queryData.n_cols << ")." << endl; - model.Search(queryData, (size_t) CLI::GetParam("k"), indices, + model->Search(queryData, (size_t) CLI::GetParam("k"), indices, kernels, base); } else { - model.Search((size_t) CLI::GetParam("k"), indices, kernels); + model->Search((size_t) CLI::GetParam("k"), indices, kernels); } - // Save output, if we were asked to. - if (CLI::HasParam("kernels")) - CLI::GetParam("kernels") = std::move(kernels); - - if (CLI::HasParam("indices")) - CLI::GetParam>("indices") = std::move(indices); + // Save output. + CLI::GetParam("kernels") = std::move(kernels); + CLI::GetParam>("indices") = std::move(indices); } - // Save the model, if requested. - if (CLI::HasParam("output_model")) - CLI::GetParam("output_model") = std::move(model); + // Save the model. + CLI::GetParam("output_model") = model; } diff --git a/src/mlpack/methods/gmm/gmm_generate_main.cpp b/src/mlpack/methods/gmm/gmm_generate_main.cpp index a6ed7d6ba4..7842349414 100644 --- a/src/mlpack/methods/gmm/gmm_generate_main.cpp +++ b/src/mlpack/methods/gmm/gmm_generate_main.cpp @@ -55,15 +55,14 @@ static void mlpackMain() RequireParamValue("samples", [](int x) { return x > 0; }, true, "number of samples must be greater than 0"); - GMM gmm = std::move(CLI::GetParam("input_model")); + GMM* gmm = CLI::GetParam("input_model"); size_t length = (size_t) CLI::GetParam("samples"); Log::Info << "Generating " << length << " samples..." << endl; - arma::mat samples(gmm.Dimensionality(), length); + arma::mat samples(gmm->Dimensionality(), length); for (size_t i = 0; i < length; ++i) - samples.col(i) = gmm.Random(); + samples.col(i) = gmm->Random(); // Save, if the user asked for it. - if (CLI::HasParam("output")) - CLI::GetParam("output") = std::move(samples); + CLI::GetParam("output") = std::move(samples); } diff --git a/src/mlpack/methods/gmm/gmm_probability_main.cpp b/src/mlpack/methods/gmm/gmm_probability_main.cpp index ac1669aaac..f40c866284 100644 --- a/src/mlpack/methods/gmm/gmm_probability_main.cpp +++ b/src/mlpack/methods/gmm/gmm_probability_main.cpp @@ -46,16 +46,15 @@ static void mlpackMain() RequireAtLeastOnePassed({ "output" }, false, "no results will be saved"); // Get the GMM and the points. - GMM gmm = std::move(CLI::GetParam("input_model")); + GMM* gmm = CLI::GetParam("input_model"); arma::mat dataset = std::move(CLI::GetParam("input")); // Now calculate the probabilities. arma::rowvec probabilities(dataset.n_cols); for (size_t i = 0; i < dataset.n_cols; ++i) - probabilities[i] = gmm.Probability(dataset.unsafe_col(i)); + probabilities[i] = gmm->Probability(dataset.unsafe_col(i)); // And save the result. - if (CLI::HasParam("output")) - CLI::GetParam("output") = std::move(probabilities); + CLI::GetParam("output") = std::move(probabilities); } diff --git a/src/mlpack/methods/gmm/gmm_train_main.cpp b/src/mlpack/methods/gmm/gmm_train_main.cpp index 82694fb17c..c331d04dc0 100644 --- a/src/mlpack/methods/gmm/gmm_train_main.cpp +++ b/src/mlpack/methods/gmm/gmm_train_main.cpp @@ -153,17 +153,21 @@ static void mlpackMain() } // Initialize GMM. - GMM gmm(size_t(gaussians), dataPoints.n_rows); + GMM* gmm; if (CLI::HasParam("input_model")) { - gmm = std::move(CLI::GetParam("input_model")); + gmm = CLI::GetParam("input_model"); - if (gmm.Dimensionality() != dataPoints.n_rows) + if (gmm->Dimensionality() != dataPoints.n_rows) Log::Fatal << "Given input data (with " << PRINT_PARAM_STRING("input") << ") has dimensionality " << dataPoints.n_rows << ", but the initial" << " model (given with " << PRINT_PARAM_STRING("input_model") - << " has dimensionality " << gmm.Dimensionality() << "!" << endl; + << " has dimensionality " << gmm->Dimensionality() << "!" << endl; + } + else + { + gmm = new GMM(size_t(gaussians), dataPoints.n_rows); } // Gather parameters for EMFit object. @@ -199,7 +203,7 @@ static void mlpackMain() // Compute the parameters of the model using the EM algorithm. Timer::Start("em"); EMFit em(maxIterations, tolerance, k); - likelihood = gmm.Train(dataPoints, CLI::GetParam("trials"), false, + likelihood = gmm->Train(dataPoints, CLI::GetParam("trials"), false, em); Timer::Stop("em"); } @@ -208,7 +212,7 @@ static void mlpackMain() // Compute the parameters of the model using the EM algorithm. Timer::Start("em"); EMFit em(maxIterations, tolerance, k); - likelihood = gmm.Train(dataPoints, CLI::GetParam("trials"), false, + likelihood = gmm->Train(dataPoints, CLI::GetParam("trials"), false, em); Timer::Stop("em"); } @@ -217,7 +221,7 @@ static void mlpackMain() // Compute the parameters of the model using the EM algorithm. Timer::Start("em"); EMFit em(maxIterations, tolerance, k); - likelihood = gmm.Train(dataPoints, CLI::GetParam("trials"), false, + likelihood = gmm->Train(dataPoints, CLI::GetParam("trials"), false, em); Timer::Stop("em"); } @@ -231,7 +235,7 @@ static void mlpackMain() // Compute the parameters of the model using the EM algorithm. Timer::Start("em"); EMFit, DiagonalConstraint> em(maxIterations, tolerance); - likelihood = gmm.Train(dataPoints, CLI::GetParam("trials"), false, + likelihood = gmm->Train(dataPoints, CLI::GetParam("trials"), false, em); Timer::Stop("em"); } @@ -240,7 +244,7 @@ static void mlpackMain() // Compute the parameters of the model using the EM algorithm. Timer::Start("em"); EMFit<> em(maxIterations, tolerance); - likelihood = gmm.Train(dataPoints, CLI::GetParam("trials"), false, + likelihood = gmm->Train(dataPoints, CLI::GetParam("trials"), false, em); Timer::Stop("em"); } @@ -249,7 +253,7 @@ static void mlpackMain() // Compute the parameters of the model using the EM algorithm. Timer::Start("em"); EMFit, NoConstraint> em(maxIterations, tolerance); - likelihood = gmm.Train(dataPoints, CLI::GetParam("trials"), false, + likelihood = gmm->Train(dataPoints, CLI::GetParam("trials"), false, em); Timer::Stop("em"); } @@ -257,6 +261,5 @@ static void mlpackMain() Log::Info << "Log-likelihood of estimate: " << likelihood << "." << endl; - if (CLI::HasParam("output_model")) - CLI::GetParam("output_model") = std::move(gmm); + CLI::GetParam("output_model") = gmm; } diff --git a/src/mlpack/methods/hmm/hmm_loglik_main.cpp b/src/mlpack/methods/hmm/hmm_loglik_main.cpp index 1a3d51a8cf..6ebb3cbef1 100644 --- a/src/mlpack/methods/hmm/hmm_loglik_main.cpp +++ b/src/mlpack/methods/hmm/hmm_loglik_main.cpp @@ -79,5 +79,5 @@ struct Loglik static void mlpackMain() { // Load model, and calculate the log-likelihood of the sequence. - CLI::GetParam("input_model").PerformAction((void*) NULL); + CLI::GetParam("input_model")->PerformAction((void*) NULL); } diff --git a/src/mlpack/methods/hmm/hmm_train_main.cpp b/src/mlpack/methods/hmm/hmm_train_main.cpp index 2983c2528b..06cf01db0e 100644 --- a/src/mlpack/methods/hmm/hmm_train_main.cpp +++ b/src/mlpack/methods/hmm/hmm_train_main.cpp @@ -436,21 +436,21 @@ static void mlpackMain() typeId = HMMType::GaussianMixtureModelHMM; // If we have a model file, we can autodetect the type. - HMMModel hmm(typeId); + HMMModel* hmm; if (CLI::HasParam("input_model")) { - hmm = std::move(CLI::GetParam("input_model")); + hmm = CLI::GetParam("input_model"); } else { // We need to initialize the model. - hmm.PerformAction>(&trainSeq); + hmm = new HMMModel(typeId); + hmm->PerformAction>(&trainSeq); } // Train the model. - hmm.PerformAction>(&trainSeq); + hmm->PerformAction>(&trainSeq); // If necessary, save the output. - if (CLI::HasParam("output_model")) - CLI::GetParam("output_model") = std::move(hmm); + CLI::GetParam("output_model") = hmm; } diff --git a/src/mlpack/methods/hmm/hmm_viterbi_main.cpp b/src/mlpack/methods/hmm/hmm_viterbi_main.cpp index 323bc066cc..d0e1168b3d 100644 --- a/src/mlpack/methods/hmm/hmm_viterbi_main.cpp +++ b/src/mlpack/methods/hmm/hmm_viterbi_main.cpp @@ -77,8 +77,7 @@ struct Viterbi hmm.Predict(dataSeq, sequence); // Save output. - if (CLI::HasParam("output")) - CLI::GetParam>("output") = std::move(sequence); + CLI::GetParam>("output") = std::move(sequence); } }; @@ -86,5 +85,5 @@ static void mlpackMain() { RequireAtLeastOnePassed({ "output" }, false, "no results will be saved"); - CLI::GetParam("input_model").PerformAction((void*) NULL); + CLI::GetParam("input_model")->PerformAction((void*) NULL); } diff --git a/src/mlpack/methods/hoeffding_trees/hoeffding_tree_main.cpp b/src/mlpack/methods/hoeffding_trees/hoeffding_tree_main.cpp index 73012a6265..2c4576337b 100644 --- a/src/mlpack/methods/hoeffding_trees/hoeffding_tree_main.cpp +++ b/src/mlpack/methods/hoeffding_trees/hoeffding_tree_main.cpp @@ -140,25 +140,25 @@ static void mlpackMain() true, "unrecognized numeric split strategy"); // Do we need to load a model or do we already have one? - HoeffdingTreeModel model; + HoeffdingTreeModel* model; DatasetInfo datasetInfo; arma::mat trainingSet; arma::Row labels; if (CLI::HasParam("input_model")) { - model = std::move(CLI::GetParam("input_model")); + model = CLI::GetParam("input_model"); } else { // Initialize a model. if (!CLI::HasParam("info_gain") && (numericSplitStrategy == "domingos")) - model = HoeffdingTreeModel(HoeffdingTreeModel::GINI_HOEFFDING); + model = new HoeffdingTreeModel(HoeffdingTreeModel::GINI_HOEFFDING); else if (!CLI::HasParam("info_gain") && (numericSplitStrategy == "binary")) - model = HoeffdingTreeModel(HoeffdingTreeModel::GINI_BINARY); + model = new HoeffdingTreeModel(HoeffdingTreeModel::GINI_BINARY); else if (CLI::HasParam("info_gain") && (numericSplitStrategy == "domingos")) - model = HoeffdingTreeModel(HoeffdingTreeModel::INFO_HOEFFDING); + model = new HoeffdingTreeModel(HoeffdingTreeModel::INFO_HOEFFDING); else if (CLI::HasParam("info_gain") && (numericSplitStrategy == "binary")) - model = HoeffdingTreeModel(HoeffdingTreeModel::INFO_BINARY); + model = new HoeffdingTreeModel(HoeffdingTreeModel::INFO_BINARY); } // Now, do we need to train? @@ -207,7 +207,7 @@ static void mlpackMain() if (!CLI::HasParam("input_model")) { // Build the model. - model.BuildModel(trainingSet, datasetInfo, labels, + model->BuildModel(trainingSet, datasetInfo, labels, arma::max(labels) + 1, batchTraining, confidence, maxSamples, 100, minSamples, bins, observationsBeforeBinning); --passes; // This model-building takes one pass. @@ -219,12 +219,12 @@ static void mlpackMain() // We only need to do batch training if we've not already called // BuildModel. if (CLI::HasParam("input_model")) - model.Train(trainingSet, labels, true); + model->Train(trainingSet, labels, true); } else { for (size_t p = 0; p < passes; ++p) - model.Train(trainingSet, labels, false); + model->Train(trainingSet, labels, false); } Timer::Stop("tree_training"); @@ -235,7 +235,7 @@ static void mlpackMain() { // Get training error. arma::Row predictions; - model.Classify(trainingSet, predictions); + model->Classify(trainingSet, predictions); size_t correct = 0; for (size_t i = 0; i < labels.n_elem; ++i) @@ -248,7 +248,7 @@ static void mlpackMain() } // Get the number of nodes in the tree. - Log::Info << model.NumNodes() << " nodes in the tree." << endl; + Log::Info << model->NumNodes() << " nodes in the tree." << endl; // The tree is trained or loaded. Now do any testing if we need. if (CLI::HasParam("test")) @@ -262,7 +262,7 @@ static void mlpackMain() arma::rowvec probabilities; Timer::Start("tree_testing"); - model.Classify(testSet, predictions, probabilities); + model->Classify(testSet, predictions, probabilities); Timer::Stop("tree_testing"); if (CLI::HasParam("test_labels")) @@ -281,14 +281,10 @@ static void mlpackMain() 100.0 << ")." << endl; } - if (CLI::HasParam("predictions")) - CLI::GetParam>("predictions") = std::move(predictions); - - if (CLI::HasParam("probabilities")) - CLI::GetParam("probabilities") = std::move(probabilities); + CLI::GetParam>("predictions") = std::move(predictions); + CLI::GetParam("probabilities") = std::move(probabilities); } // Check the accuracy on the training set. - if (CLI::HasParam("output_model")) - CLI::GetParam("output_model") = std::move(model); + CLI::GetParam("output_model") = model; } diff --git a/src/mlpack/methods/lars/lars_main.cpp b/src/mlpack/methods/lars/lars_main.cpp index 0c6d1a28e5..040c5f0c3e 100644 --- a/src/mlpack/methods/lars/lars_main.cpp +++ b/src/mlpack/methods/lars/lars_main.cpp @@ -120,11 +120,12 @@ static void mlpackMain() "no results will be saved"); ReportIgnoredParam({{ "test", true }}, "output_predictions"); - // Initialize the object. - LARS lars(useCholesky, lambda1, lambda2); - + LARS* lars; if (CLI::HasParam("input")) { + // Initialize the object. + lars = new LARS(useCholesky, lambda1, lambda2); + // Load covariates. We can avoid LARS transposing our data by choosing to // not transpose this data (that's why we used PARAM_TMATRIX_IN). mat matX = std::move(CLI::GetParam("input")); @@ -146,11 +147,11 @@ static void mlpackMain() vec beta; arma::rowvec y = std::move(matY); - lars.Train(matX, y, beta, false /* do not transpose */); + lars->Train(matX, y, beta, false /* do not transpose */); } else // We must have --input_model_file. { - lars = std::move(CLI::GetParam("input_model")); + lars = CLI::GetParam("input_model"); } if (CLI::HasParam("test")) @@ -162,19 +163,17 @@ static void mlpackMain() // Make sure the dimensionality is right. We haven't transposed, so, we // check n_cols not n_rows. - if (testPoints.n_cols != lars.BetaPath().back().n_elem) + if (testPoints.n_cols != lars->BetaPath().back().n_elem) Log::Fatal << "Dimensionality of test set (" << testPoints.n_cols << ") " << "is not equal to the dimensionality of the model (" - << lars.BetaPath().back().n_elem << ")!" << endl; + << lars->BetaPath().back().n_elem << ")!" << endl; arma::rowvec predictions; - lars.Predict(testPoints.t(), predictions, false); + lars->Predict(testPoints.t(), predictions, false); // Save test predictions (one per line). - if (CLI::HasParam("output_predictions")) - CLI::GetParam("output_predictions") = predictions.t(); + CLI::GetParam("output_predictions") = predictions.t(); } - if (CLI::HasParam("output_model")) - CLI::GetParam("output_model") = std::move(lars); + CLI::GetParam("output_model") = lars; } diff --git a/src/mlpack/methods/linear_regression/linear_regression_main.cpp b/src/mlpack/methods/linear_regression/linear_regression_main.cpp index 19c418151c..c1e33cd069 100644 --- a/src/mlpack/methods/linear_regression/linear_regression_main.cpp +++ b/src/mlpack/methods/linear_regression/linear_regression_main.cpp @@ -96,7 +96,7 @@ static void mlpackMain() mat regressors; rowvec responses; - LinearRegression lr; + LinearRegression* lr; const bool computeModel = !CLI::HasParam("input_model"); const bool computePrediction = CLI::HasParam("test"); @@ -148,14 +148,14 @@ static void mlpackMain() } Timer::Start("regression"); - lr = LinearRegression(regressors, responses, lambda); + lr = new LinearRegression(regressors, responses, lambda); Timer::Stop("regression"); } else { // A model file was passed in, so load it. Timer::Start("load_model"); - lr = std::move(CLI::GetParam("input_model")); + lr = CLI::GetParam("input_model"); Timer::Stop("load_model"); } @@ -168,10 +168,15 @@ static void mlpackMain() Timer::Stop("load_test_points"); // Ensure that test file data has the right number of features. - if ((lr.Parameters().n_elem - 1) != points.n_rows) + if ((lr->Parameters().n_elem - 1) != points.n_rows) { - Log::Fatal << "The model was trained on " << lr.Parameters().n_elem - 1 - << "-dimensional data, but the test points in '" + // If we built the model, nothing will free it so we have to... + const size_t dimensions = lr->Parameters().n_elem - 1; + if (computeModel) + delete lr; + + Log::Fatal << "The model was trained on " << dimensions << "-dimensional " + << "data, but the test points in '" << CLI::GetPrintableParam("test") << "' are " << points.n_rows << "-dimensional!" << endl; } @@ -179,15 +184,13 @@ static void mlpackMain() // Perform the predictions using our model. rowvec predictions; Timer::Start("prediction"); - lr.Predict(points, predictions); + lr->Predict(points, predictions); Timer::Stop("prediction"); // Save predictions. - if (CLI::HasParam("output_predictions")) - CLI::GetParam("output_predictions") = std::move(predictions); + CLI::GetParam("output_predictions") = std::move(predictions); } // Save the model if needed. - if (CLI::HasParam("output_model")) - CLI::GetParam("output_model") = std::move(lr); + CLI::GetParam("output_model") = lr; } diff --git a/src/mlpack/methods/local_coordinate_coding/local_coordinate_coding_main.cpp b/src/mlpack/methods/local_coordinate_coding/local_coordinate_coding_main.cpp index cdfc34c258..f0ee7e6dc5 100644 --- a/src/mlpack/methods/local_coordinate_coding/local_coordinate_coding_main.cpp +++ b/src/mlpack/methods/local_coordinate_coding/local_coordinate_coding_main.cpp @@ -120,9 +120,11 @@ static void mlpackMain() ReportIgnoredParam({{ "training", false }}, "tolerance"); // Do we have an existing model? - LocalCoordinateCoding lcc(0, 0.0); + LocalCoordinateCoding* lcc; if (CLI::HasParam("input_model")) - lcc = std::move(CLI::GetParam("input_model")); + lcc = CLI::GetParam("input_model"); + else + lcc = new LocalCoordinateCoding(0, 0.0); if (CLI::HasParam("training")) { @@ -136,10 +138,10 @@ static void mlpackMain() matX.col(i) /= norm(matX.col(i), 2); } - lcc.Lambda() = CLI::GetParam("lambda"); - lcc.Atoms() = (size_t) CLI::GetParam("atoms"); - lcc.MaxIterations() = (size_t) CLI::GetParam("max_iterations"); - lcc.Tolerance() = CLI::GetParam("tolerance"); + lcc->Lambda() = CLI::GetParam("lambda"); + lcc->Atoms() = (size_t) CLI::GetParam("atoms"); + lcc->MaxIterations() = (size_t) CLI::GetParam("max_iterations"); + lcc->Tolerance() = CLI::GetParam("tolerance"); // Inform the user if we are overwriting their model. if (CLI::HasParam("input_model")) @@ -147,35 +149,35 @@ static void mlpackMain() Log::Info << "Using dictionary from existing model in '" << CLI::GetPrintableParam("input_model") << "' as initial " << "dictionary for training." << endl; - lcc.Train(matX); + lcc->Train(matX); } else if (CLI::HasParam("initial_dictionary")) { // Load initial dictionary directly into LCC object. - lcc.Dictionary() = std::move(CLI::GetParam("initial_dictionary")); + lcc->Dictionary() = std::move(CLI::GetParam("initial_dictionary")); // Validate the size of the initial dictionary. - if (lcc.Dictionary().n_cols != lcc.Atoms()) + if (lcc->Dictionary().n_cols != lcc->Atoms()) { - Log::Fatal << "The initial dictionary has " << lcc.Dictionary().n_cols + Log::Fatal << "The initial dictionary has " << lcc->Dictionary().n_cols << " atoms, but the number of atoms was specified to be " - << lcc.Atoms() << "!" << endl; + << lcc->Atoms() << "!" << endl; } - if (lcc.Dictionary().n_rows != matX.n_rows) + if (lcc->Dictionary().n_rows != matX.n_rows) { - Log::Fatal << "The initial dictionary has " << lcc.Dictionary().n_rows + Log::Fatal << "The initial dictionary has " << lcc->Dictionary().n_rows << " dimensions, but the data has " << matX.n_rows << " dimensions!" << endl; } // Train the model. - lcc.Train(matX); + lcc->Train(matX); } else { // Run with the default initialization. - lcc.Train(matX); + lcc->Train(matX); } } @@ -184,9 +186,9 @@ static void mlpackMain() { mat matY = std::move(CLI::GetParam("test")); - if (matY.n_rows != lcc.Dictionary().n_rows) + if (matY.n_rows != lcc->Dictionary().n_rows) Log::Fatal << "Model was trained with a dimensionality of " - << lcc.Dictionary().n_rows << ", but data in test file " + << lcc->Dictionary().n_rows << ", but data in test file " << CLI::GetPrintableParam("test") << " has a dimensionality of " << matY.n_rows << "!" << endl; @@ -199,17 +201,12 @@ static void mlpackMain() } mat codes; - lcc.Encode(matY, codes); + lcc->Encode(matY, codes); - if (CLI::HasParam("codes")) - CLI::GetParam("codes") = std::move(codes); + CLI::GetParam("codes") = std::move(codes); } - // Did the user want to save the dictionary? - if (CLI::HasParam("dictionary")) - CLI::GetParam("dictionary") = std::move(lcc.Dictionary()); - - // Did the user want to save the model? - if (CLI::HasParam("output_model")) - CLI::GetParam("output_model") = std::move(lcc); + // Save the dictionary and the model. + CLI::GetParam("dictionary") = lcc->Dictionary(); + CLI::GetParam("output_model") = lcc; } diff --git a/src/mlpack/methods/logistic_regression/logistic_regression_main.cpp b/src/mlpack/methods/logistic_regression/logistic_regression_main.cpp index 57b35a2fe5..fa08aa96d8 100644 --- a/src/mlpack/methods/logistic_regression/logistic_regression_main.cpp +++ b/src/mlpack/methods/logistic_regression/logistic_regression_main.cpp @@ -214,16 +214,18 @@ static void mlpackMain() regressors = std::move(CLI::GetParam("training")); // Load the model, if necessary. - LogisticRegression<> model(0, 0); // Empty model. + LogisticRegression<>* model; if (CLI::HasParam("input_model")) - model = std::move(CLI::GetParam>("input_model")); + model = CLI::GetParam*>("input_model"); else { + model = new LogisticRegression<>(0, 0); + // Set the size of the parameters vector, if necessary. if (!CLI::HasParam("labels")) - model.Parameters() = arma::zeros(regressors.n_rows); + model->Parameters() = arma::zeros(regressors.n_rows); else - model.Parameters() = arma::zeros(regressors.n_rows + 1); + model->Parameters() = arma::zeros(regressors.n_rows + 1); } // Check if the responses are in a separate file. @@ -257,7 +259,7 @@ static void mlpackMain() // Now, do the training. if (CLI::HasParam("training")) { - model.Lambda() = lambda; + model->Lambda() = lambda; if (optimizerType == "sgd") { @@ -269,7 +271,7 @@ static void mlpackMain() Log::Info << "Training model with SGD optimizer." << endl; // This will train the model. - model.Train(regressors, responses, sgdOpt); + model->Train(regressors, responses, sgdOpt); } else if (optimizerType == "lbfgs") { @@ -279,7 +281,7 @@ static void mlpackMain() Log::Info << "Training model with L-BFGS optimizer." << endl; // This will train the model. - model.Train(regressors, responses, lbfgsOpt); + model->Train(regressors, responses, lbfgsOpt); } } @@ -300,7 +302,7 @@ static void mlpackMain() { Log::Info << "Predicting classes of points in '" << CLI::GetPrintableParam("test") << "'." << endl; - model.Classify(testSet, predictions, decisionBoundary); + model->Classify(testSet, predictions, decisionBoundary); CLI::GetParam>("output") = std::move(predictions); } @@ -310,13 +312,12 @@ static void mlpackMain() Log::Info << "Calculating class probabilities of points in '" << CLI::GetPrintableParam("test") << "'." << endl; arma::mat probabilities; - model.Classify(testSet, probabilities); + model->Classify(testSet, probabilities); CLI::GetParam("output_probabilities") = std::move(probabilities); } } - if (CLI::HasParam("output_model")) - CLI::GetParam>("output_model") = std::move(model); + CLI::GetParam*>("output_model") = model; } diff --git a/src/mlpack/methods/lsh/lsh_main.cpp b/src/mlpack/methods/lsh/lsh_main.cpp index f9ce2de8b5..eec2049daa 100644 --- a/src/mlpack/methods/lsh/lsh_main.cpp +++ b/src/mlpack/methods/lsh/lsh_main.cpp @@ -154,9 +154,10 @@ static void mlpackMain() Log::Info << "Using LSH with " << numProj << " projections (K) and " << numTables << " tables (L) with hash width(r): " << hashWidth << endl; - LSHSearch<> allkann; + LSHSearch<>* allkann; if (CLI::HasParam("reference")) { + allkann = new LSHSearch<>(); referenceData = std::move(CLI::GetParam("reference")); Log::Info << "Using reference data from '" << CLI::GetPrintableParam("reference") << "' (" @@ -164,13 +165,13 @@ static void mlpackMain() << endl; Timer::Start("hash_building"); - allkann.Train(std::move(referenceData), numProj, numTables, hashWidth, + allkann->Train(std::move(referenceData), numProj, numTables, hashWidth, secondHashSize, bucketSize); Timer::Stop("hash_building"); } else if (CLI::HasParam("input_model")) { - allkann = std::move(CLI::GetParam>("input_model")); + allkann = CLI::GetParam*>("input_model"); } if (CLI::HasParam("k")) @@ -184,11 +185,11 @@ static void mlpackMain() << CLI::GetPrintableParam("query") << "' (" << queryData.n_rows << " x " << queryData.n_cols << ")." << endl; - allkann.Search(queryData, k, neighbors, distances, 0, numProbes); + allkann->Search(queryData, k, neighbors, distances, 0, numProbes); } else { - allkann.Search(k, neighbors, distances, 0, numProbes); + allkann->Search(k, neighbors, distances, 0, numProbes); } Log::Info << "Neighbors computed." << endl; @@ -205,20 +206,17 @@ static void mlpackMain() << endl; // Compute recall and print it. - double recallPercentage = 100 * allkann.ComputeRecall(neighbors, + double recallPercentage = 100 * allkann->ComputeRecall(neighbors, trueNeighbors); Log::Info << "Recall: " << recallPercentage << endl; } - // Save output, if desired. + // Save output, if we did a search.. if (CLI::HasParam("k")) { - if (CLI::HasParam("distances")) - CLI::GetParam("distances") = std::move(distances); - if (CLI::HasParam("neighbors")) - CLI::GetParam>("neighbors") = std::move(neighbors); + CLI::GetParam("distances") = std::move(distances); + CLI::GetParam>("neighbors") = std::move(neighbors); } - if (CLI::HasParam("output_model")) - CLI::GetParam>("output_model") = std::move(allkann); + CLI::GetParam*>("output_model") = allkann; } diff --git a/src/mlpack/methods/naive_bayes/naive_bayes_classifier_impl.hpp b/src/mlpack/methods/naive_bayes/naive_bayes_classifier_impl.hpp index 69b5397dce..20fa070ddb 100644 --- a/src/mlpack/methods/naive_bayes/naive_bayes_classifier_impl.hpp +++ b/src/mlpack/methods/naive_bayes/naive_bayes_classifier_impl.hpp @@ -331,7 +331,7 @@ void NaiveBayesClassifier::Classify( // Now calculate maximum probabilities for each point. for (size_t i = 0; i < data.n_cols; ++i) { - arma::uword maxIndex; + arma::uword maxIndex = 0; logLikelihoods.unsafe_col(i).max(maxIndex); predictions[i] = maxIndex; } diff --git a/src/mlpack/methods/naive_bayes/nbc_main.cpp b/src/mlpack/methods/naive_bayes/nbc_main.cpp index 99b76cb7e7..68a4a70e04 100644 --- a/src/mlpack/methods/naive_bayes/nbc_main.cpp +++ b/src/mlpack/methods/naive_bayes/nbc_main.cpp @@ -118,9 +118,10 @@ static void mlpackMain() Log::Warn << "No test set given; no task will be performed!" << std::endl; // Either we have to train a model, or load a model. - NBCModel model; + NBCModel* model; if (CLI::HasParam("training")) { + model = new NBCModel(); mat trainingData = std::move(CLI::GetParam("training")); Row labels; @@ -130,7 +131,7 @@ static void mlpackMain() { // Load labels. Row rawLabels = std::move(CLI::GetParam>("labels")); - data::NormalizeLabels(rawLabels, labels, model.mappings); + data::NormalizeLabels(rawLabels, labels, model->mappings); } else { @@ -138,7 +139,7 @@ static void mlpackMain() Log::Info << "Using last dimension of training data as training labels." << endl; data::NormalizeLabels(trainingData.row(trainingData.n_rows - 1), labels, - model.mappings); + model->mappings); // Remove the label row. trainingData.shed_row(trainingData.n_rows - 1); } @@ -146,14 +147,14 @@ static void mlpackMain() const bool incrementalVariance = CLI::HasParam("incremental_variance"); Timer::Start("nbc_training"); - model.nbc = NaiveBayesClassifier<>(trainingData, labels, - model.mappings.n_elem, incrementalVariance); + model->nbc = NaiveBayesClassifier<>(trainingData, labels, + model->mappings.n_elem, incrementalVariance); Timer::Stop("nbc_training"); } else { // Load the model from file. - model = std::move(CLI::GetParam("input_model")); + model = CLI::GetParam("input_model"); } // Do we need to do testing? @@ -161,10 +162,10 @@ static void mlpackMain() { mat testingData = std::move(CLI::GetParam("test")); - if (testingData.n_rows != model.nbc.Means().n_rows) + if (testingData.n_rows != model->nbc.Means().n_rows) { Log::Fatal << "Test data dimensionality (" << testingData.n_rows << ") " - << "must be the same as training data (" << model.nbc.Means().n_rows + << "must be the same as training data (" << model->nbc.Means().n_rows << ")!" << std::endl; } @@ -172,23 +173,21 @@ static void mlpackMain() Row predictions; mat probabilities; Timer::Start("nbc_testing"); - model.nbc.Classify(testingData, predictions, probabilities); + model->nbc.Classify(testingData, predictions, probabilities); Timer::Stop("nbc_testing"); if (CLI::HasParam("output")) { // Un-normalize labels to prepare output. Row rawResults; - data::RevertLabels(predictions, model.mappings, rawResults); + data::RevertLabels(predictions, model->mappings, rawResults); // Output results. CLI::GetParam>("output") = std::move(rawResults); } - if (CLI::HasParam("output_probs")) - CLI::GetParam("output_probs") = probabilities; + CLI::GetParam("output_probs") = probabilities; } - if (CLI::HasParam("output_model")) - CLI::GetParam("output_model") = std::move(model); + CLI::GetParam("output_model") = model; } diff --git a/src/mlpack/methods/neighbor_search/kfn_main.cpp b/src/mlpack/methods/neighbor_search/kfn_main.cpp index d6a5d3d0d7..9d1ebb35bb 100644 --- a/src/mlpack/methods/neighbor_search/kfn_main.cpp +++ b/src/mlpack/methods/neighbor_search/kfn_main.cpp @@ -157,7 +157,7 @@ static void mlpackMain() epsilon = 1 - percentage; // We either have to load the reference data, or we have to load the model. - NSModel kfn; + NSModel* kfn; const string algorithm = CLI::GetParam("algorithm"); RequireParamInSet("algorithm", { "naive", "single_tree", "dual_tree", @@ -175,6 +175,8 @@ static void mlpackMain() if (CLI::HasParam("reference")) { + kfn = new KFNModel(); + // Get all the parameters. RequireParamInSet("tree_type", { "kd", "cover", "r", "r-star", "ball", "x", "hilbert-r", "r-plus", "r-plus-plus", "vp", "rp", "max-rp", @@ -212,8 +214,8 @@ static void mlpackMain() else if (treeType == "oct") tree = KFNModel::OCTREE; - kfn.TreeType() = tree; - kfn.RandomBasis() = randomBasis; + kfn->TreeType() = tree; + kfn->RandomBasis() = randomBasis; arma::mat referenceSet = std::move(CLI::GetParam("reference")); @@ -221,27 +223,28 @@ static void mlpackMain() << CLI::GetPrintableParam("reference") << "' (" << referenceSet.n_rows << "x" << referenceSet.n_cols << ")." << endl; - kfn.BuildModel(std::move(referenceSet), size_t(lsInt), searchMode, epsilon); + kfn->BuildModel(std::move(referenceSet), size_t(lsInt), searchMode, + epsilon); } else { // Load the model from file. - kfn = std::move(CLI::GetParam("input_model")); + kfn = CLI::GetParam("input_model"); // Adjust search mode. - kfn.SearchMode() = searchMode; - kfn.Epsilon() = epsilon; + kfn->SearchMode() = searchMode; + kfn->Epsilon() = epsilon; // If leaf_size wasn't provided, let's consider the current value in the // loaded model. Else, update it (only considered when building the query // tree). if (CLI::HasParam("leaf_size")) - kfn.LeafSize() = size_t(lsInt); + kfn->LeafSize() = size_t(lsInt); Log::Info << "Using kFN model from '" - << CLI::GetPrintableParam("input_model") << "' (trained on " - << kfn.Dataset().n_rows << "x" << kfn.Dataset().n_cols << " dataset)." - << endl; + << CLI::GetPrintableParam("input_model") << "' (trained on " + << kfn->Dataset().n_rows << "x" << kfn->Dataset().n_cols + << " dataset)." << endl; } // Perform search, if desired. @@ -261,11 +264,11 @@ static void mlpackMain() // Sanity check on k value: must be greater than 0, must be less than the // number of reference points. Since it is unsigned, we only test the upper // bound. - if (k > kfn.Dataset().n_cols) + if (k > kfn->Dataset().n_cols) { Log::Fatal << "Invalid k: " << k << "; must be greater than 0 and less " << "than or equal to the number of reference points (" - << kfn.Dataset().n_cols << ")." << endl; + << kfn->Dataset().n_cols << ")." << endl; } // Now run the search. @@ -273,21 +276,19 @@ static void mlpackMain() arma::mat distances; if (CLI::HasParam("query")) - kfn.Search(std::move(queryData), k, neighbors, distances); + kfn->Search(std::move(queryData), k, neighbors, distances); else - kfn.Search(k, neighbors, distances); + kfn->Search(k, neighbors, distances); Log::Info << "Search complete." << endl; - // Save output, if desired. - if (CLI::HasParam("neighbors")) - CLI::GetParam>("neighbors") = std::move(neighbors); - if (CLI::HasParam("distances")) - CLI::GetParam("distances") = std::move(distances); + // Save output. + CLI::GetParam>("neighbors") = std::move(neighbors); + CLI::GetParam("distances") = std::move(distances); // Calculate the effective error, if desired. if (CLI::HasParam("true_distances")) { - if (kfn.Epsilon() == 0) + if (kfn->Epsilon() == 0) Log::Warn << PRINT_PARAM_STRING("true_distances") << " specified, but " << "the search is exact, so there is no need to calculate the " << "error!" << endl; @@ -307,7 +308,7 @@ static void mlpackMain() // Calculate the recall, if desired. if (CLI::HasParam("true_neighbors")) { - if (kfn.Epsilon() == 0) + if (kfn->Epsilon() == 0) Log::Warn << PRINT_PARAM_STRING("true_neighbors") << " specified, but " << "the search is exact, so there is no need to calculate the " << "recall!" << endl; @@ -324,6 +325,5 @@ static void mlpackMain() } } - if (CLI::HasParam("output_model")) - CLI::GetParam("output_model") = std::move(kfn); + CLI::GetParam("output_model") = kfn; } diff --git a/src/mlpack/methods/neighbor_search/knn_main.cpp b/src/mlpack/methods/neighbor_search/knn_main.cpp index 9c43abceaf..2fe257700b 100644 --- a/src/mlpack/methods/neighbor_search/knn_main.cpp +++ b/src/mlpack/methods/neighbor_search/knn_main.cpp @@ -166,7 +166,7 @@ static void mlpackMain() "epsilon must be positive"); // We either have to load the reference data, or we have to load the model. - KNNModel knn; + KNNModel* knn; const string algorithm = CLI::GetParam("algorithm"); RequireParamInSet("algorithm", { "naive", "single_tree", "dual_tree", @@ -184,6 +184,8 @@ static void mlpackMain() if (CLI::HasParam("reference")) { + knn = new KNNModel(); + // Get all the parameters. const string treeType = CLI::GetParam("tree_type"); const bool randomBasis = CLI::HasParam("random_basis"); @@ -223,11 +225,11 @@ static void mlpackMain() else if (treeType == "oct") tree = KNNModel::OCTREE; - knn.TreeType() = tree; - knn.RandomBasis() = randomBasis; - knn.LeafSize() = size_t(lsInt); - knn.Tau() = tau; - knn.Rho() = rho; + knn->TreeType() = tree; + knn->RandomBasis() = randomBasis; + knn->LeafSize() = size_t(lsInt); + knn->Tau() = tau; + knn->Rho() = rho; arma::mat referenceSet = std::move(CLI::GetParam("reference")); @@ -236,27 +238,28 @@ static void mlpackMain() << referenceSet.n_rows << " x " << referenceSet.n_cols << ")." << endl; - knn.BuildModel(std::move(referenceSet), size_t(lsInt), searchMode, epsilon); + knn->BuildModel(std::move(referenceSet), size_t(lsInt), searchMode, + epsilon); } else { // Load the model from file. - knn = std::move(CLI::GetParam("input_model")); + knn = CLI::GetParam("input_model"); // Adjust search mode. - knn.SearchMode() = searchMode; - knn.Epsilon() = epsilon; + knn->SearchMode() = searchMode; + knn->Epsilon() = epsilon; // If leaf_size wasn't provided, let's consider the current value in the // loaded model. Else, update it (only considered when building the query // tree). if (CLI::HasParam("leaf_size")) - knn.LeafSize() = size_t(lsInt); + knn->LeafSize() = size_t(lsInt); Log::Info << "Loaded kNN model from '" - << CLI::GetPrintableParam("input_model") << "' (trained on " - << knn.Dataset().n_rows << "x" << knn.Dataset().n_cols << " dataset)." - << endl; + << CLI::GetPrintableParam("input_model") << "' (trained on " + << knn->Dataset().n_rows << "x" << knn->Dataset().n_cols + << " dataset)." << endl; } // Perform search, if desired. @@ -276,11 +279,11 @@ static void mlpackMain() // Sanity check on k value: must be greater than 0, must be less than the // number of reference points. Since it is unsigned, we only test the upper // bound. - if (k > knn.Dataset().n_cols) + if (k > knn->Dataset().n_cols) { Log::Fatal << "Invalid k: " << k << "; must be greater than 0 and less "; Log::Fatal << "than or equal to the number of reference points ("; - Log::Fatal << knn.Dataset().n_cols << ")." << endl; + Log::Fatal << knn->Dataset().n_cols << ")." << endl; } // Now run the search. @@ -288,21 +291,19 @@ static void mlpackMain() arma::mat distances; if (CLI::HasParam("query")) - knn.Search(std::move(queryData), k, neighbors, distances); + knn->Search(std::move(queryData), k, neighbors, distances); else - knn.Search(k, neighbors, distances); + knn->Search(k, neighbors, distances); Log::Info << "Search complete." << endl; - // Save output, if desired. - if (CLI::HasParam("neighbors")) - CLI::GetParam>("neighbors") = std::move(neighbors); - if (CLI::HasParam("distances")) - CLI::GetParam("distances") = std::move(distances); + // Save output. + CLI::GetParam>("neighbors") = std::move(neighbors); + CLI::GetParam("distances") = std::move(distances); // Calculate the effective error, if desired. if (CLI::HasParam("true_distances")) { - if (knn.TreeType() != KNNModel::SPILL_TREE && knn.Epsilon() == 0) + if (knn->TreeType() != KNNModel::SPILL_TREE && knn->Epsilon() == 0) Log::Warn << PRINT_PARAM_STRING("true_distances") << "specified, but " << "the search is exact, so there is no need to calculate the " << "error!" << endl; @@ -322,7 +323,7 @@ static void mlpackMain() // Calculate the recall, if desired. if (CLI::HasParam("true_neighbors")) { - if (knn.TreeType() != KNNModel::SPILL_TREE && knn.Epsilon() == 0) + if (knn->TreeType() != KNNModel::SPILL_TREE && knn->Epsilon() == 0) Log::Warn << PRINT_PARAM_STRING("true_neighbors") << " specified, but " << " the search is exact, so there is no need to calculate the " << "recall!" << endl; @@ -339,6 +340,5 @@ static void mlpackMain() } } - if (CLI::HasParam("output_model")) - CLI::GetParam("output_model") = std::move(knn); + CLI::GetParam("output_model") = knn; } diff --git a/src/mlpack/methods/perceptron/perceptron_impl.hpp b/src/mlpack/methods/perceptron/perceptron_impl.hpp index 38226bdb9a..6e15b53e83 100644 --- a/src/mlpack/methods/perceptron/perceptron_impl.hpp +++ b/src/mlpack/methods/perceptron/perceptron_impl.hpp @@ -148,7 +148,7 @@ void Perceptron::Train( size_t j, i = 0; bool converged = false; size_t tempLabel; - arma::uword maxIndexRow, maxIndexCol; + arma::uword maxIndexRow = 0, maxIndexCol = 0; arma::mat tempLabelMat; LearnPolicy LP; diff --git a/src/mlpack/methods/perceptron/perceptron_main.cpp b/src/mlpack/methods/perceptron/perceptron_main.cpp index 78f29babd6..4cc4e27929 100644 --- a/src/mlpack/methods/perceptron/perceptron_main.cpp +++ b/src/mlpack/methods/perceptron/perceptron_main.cpp @@ -137,14 +137,18 @@ static void mlpackMain() true, "maximum number of iterations must be nonnegative"); // Now, load our model, if there is one. - PerceptronModel p; + PerceptronModel* p; if (CLI::HasParam("input_model")) { Log::Info << "Using saved perceptron from " - << CLI::GetPrintableParam("input_model") << "." + << CLI::GetPrintableParam("input_model") << "." << endl; - p = std::move(CLI::GetParam("input_model")); + p = CLI::GetParam("input_model"); + } + else + { + p = new PerceptronModel(); } // Next, load the training data and labels (if they have been given). @@ -199,8 +203,8 @@ static void mlpackMain() // Normalize the labels. Row labels; - data::NormalizeLabels(labelsIn, labels, p.Map()); - const size_t numClasses = p.Map().n_elem; + data::NormalizeLabels(labelsIn, labels, p->Map()); + const size_t numClasses = p->Map().n_elem; // Now, if we haven't already created a perceptron, do it. Otherwise, make // sure the dimensions are right, then continue training. @@ -208,35 +212,35 @@ static void mlpackMain() { // Create and train the classifier. Timer::Start("training"); - p.P() = Perceptron<>(trainingData, labels, numClasses, maxIterations); + p->P() = Perceptron<>(trainingData, labels, numClasses, maxIterations); Timer::Stop("training"); } else { // Check dimensionality. - if (p.P().Weights().n_rows != trainingData.n_rows) + if (p->P().Weights().n_rows != trainingData.n_rows) { Log::Fatal << "Perceptron from '" - << CLI::GetPrintableParam("input_model") - << "' is built on data with " << p.P().Weights().n_rows + << CLI::GetPrintableParam("input_model") + << "' is built on data with " << p->P().Weights().n_rows << " dimensions, but data in '" << CLI::GetPrintableParam("training") << "' has " << trainingData.n_rows << "dimensions!" << endl; } // Check the number of labels. - if (numClasses > p.P().Weights().n_cols) + if (numClasses > p->P().Weights().n_cols) { Log::Fatal << "Perceptron from '" - << CLI::GetPrintableParam("input_model") << "' " - << "has " << p.P().Weights().n_cols << " classes, but the training " - << "data has " << numClasses + 1 << " classes!" << endl; + << CLI::GetPrintableParam("input_model") << "' " + << "has " << p->P().Weights().n_cols << " classes, but the training" + << " data has " << numClasses + 1 << " classes!" << endl; } // Now train. Timer::Start("training"); - p.P().MaxIterations() = maxIterations; - p.P().Train(trainingData, labels.t(), numClasses); + p->P().MaxIterations() = maxIterations; + p->P().Train(trainingData, labels.t(), numClasses); Timer::Stop("training"); } } @@ -248,29 +252,28 @@ static void mlpackMain() << CLI::GetPrintableParam("test") << "'." << endl; mat testData = std::move(CLI::GetParam("test")); - if (testData.n_rows != p.P().Weights().n_rows) + if (testData.n_rows != p->P().Weights().n_rows) { Log::Fatal << "Test data dimensionality (" << testData.n_rows << ") must " << "be the same as the dimensionality of the perceptron (" - << p.P().Weights().n_rows << ")!" << endl; + << p->P().Weights().n_rows << ")!" << endl; } // Time the running of the perceptron classifier. Row predictedLabels(testData.n_cols); Timer::Start("testing"); - p.P().Classify(testData, predictedLabels); + p->P().Classify(testData, predictedLabels); Timer::Stop("testing"); // Un-normalize labels to prepare output. Row results; - data::RevertLabels(predictedLabels, p.Map(), results); + data::RevertLabels(predictedLabels, p->Map(), results); // Save the predicted labels. if (CLI::HasParam("output")) CLI::GetParam>("output") = std::move(results); } - // Lastly, do we need to save the output model? - if (CLI::HasParam("output_model")) - CLI::GetParam("output_model") = std::move(p); + // Lastly, save the output model. + CLI::GetParam("output_model") = p; } diff --git a/src/mlpack/methods/random_forest/random_forest_main.cpp b/src/mlpack/methods/random_forest/random_forest_main.cpp index e6cdfd5aa3..df38516fb9 100644 --- a/src/mlpack/methods/random_forest/random_forest_main.cpp +++ b/src/mlpack/methods/random_forest/random_forest_main.cpp @@ -104,9 +104,11 @@ static void mlpackMain() ReportIgnoredParam({{ "training", false }}, "num_trees"); ReportIgnoredParam({{ "training", false }}, "minimum_leaf_size"); - RandomForestModel rfModel; + RandomForestModel* rfModel; if (CLI::HasParam("training")) { + rfModel = new RandomForestModel(); + // Train the model on the given input data. arma::mat data = std::move(CLI::GetParam("training")); arma::Row labels = @@ -121,13 +123,13 @@ static void mlpackMain() const size_t numClasses = arma::max(labels) + 1; // Train the model. - rfModel.rf.Train(data, labels, numClasses, numTrees, minimumLeafSize); + rfModel->rf.Train(data, labels, numClasses, numTrees, minimumLeafSize); // Did we want training accuracy? if (CLI::HasParam("print_training_accuracy")) { arma::Row predictions; - rfModel.rf.Classify(data, predictions); + rfModel->rf.Classify(data, predictions); const size_t correct = arma::accu(predictions == labels); @@ -139,7 +141,7 @@ static void mlpackMain() else { // Then we must be loading a model. - rfModel = std::move(CLI::GetParam("input_model")); + rfModel = CLI::GetParam("input_model"); } if (CLI::HasParam("test")) @@ -149,7 +151,7 @@ static void mlpackMain() // Get predictions and probabilities. arma::Row predictions; arma::mat probabilities; - rfModel.rf.Classify(testData, predictions, probabilities); + rfModel->rf.Classify(testData, predictions, probabilities); // Did we want to calculate test accuracy? if (CLI::HasParam("test_labels")) @@ -164,14 +166,11 @@ static void mlpackMain() << ")." << endl; } - // Should we save the outputs? - if (CLI::HasParam("probabilities")) - CLI::GetParam("probabilities") = std::move(probabilities); - if (CLI::HasParam("predictions")) - CLI::GetParam>("predictions") = std::move(predictions); + // Save the outputs. + CLI::GetParam("probabilities") = std::move(probabilities); + CLI::GetParam>("predictions") = std::move(predictions); } - // Did the user want to save the output model? - if (CLI::HasParam("output_model")) - CLI::GetParam("output_model") = std::move(rfModel); + // Save the output model. + CLI::GetParam("output_model") = rfModel; } diff --git a/src/mlpack/methods/range_search/range_search_main.cpp b/src/mlpack/methods/range_search/range_search_main.cpp index 66a070e6d6..27e49caf19 100644 --- a/src/mlpack/methods/range_search/range_search_main.cpp +++ b/src/mlpack/methods/range_search/range_search_main.cpp @@ -136,11 +136,13 @@ static void mlpackMain() "leaf size must be greater than 0"); // We either have to load the reference data, or we have to load the model. - RSModel rs; + RSModel* rs; const bool naive = CLI::HasParam("naive"); const bool singleMode = CLI::HasParam("single_mode"); if (CLI::HasParam("reference")) { + rs = new RSModel(); + // Get all the parameters. const string treeType = CLI::GetParam("tree_type"); RequireParamInSet("tree_type", { "kd", "cover", "r", "r-star", @@ -178,8 +180,8 @@ static void mlpackMain() else if (treeType == "oct") tree = RSModel::OCTREE; - rs.TreeType() = tree; - rs.RandomBasis() = randomBasis; + rs->TreeType() = tree; + rs->RandomBasis() = randomBasis; arma::mat referenceSet = std::move(CLI::GetParam("reference")); @@ -189,22 +191,22 @@ static void mlpackMain() const size_t leafSize = size_t(lsInt); - rs.BuildModel(std::move(referenceSet), leafSize, naive, singleMode); + rs->BuildModel(std::move(referenceSet), leafSize, naive, singleMode); } else { // Load the model from file. - rs = std::move(CLI::GetParam("input_model")); + rs = CLI::GetParam("input_model"); Log::Info << "Using range search model from '" << CLI::GetPrintableParam("input_model") << "' (" - << "trained on " << rs.Dataset().n_rows << "x" << rs.Dataset().n_cols + << "trained on " << rs->Dataset().n_rows << "x" << rs->Dataset().n_cols << " dataset)." << endl; // Adjust singleMode and naive if necessary. - rs.SingleMode() = CLI::HasParam("single_mode"); - rs.Naive() = CLI::HasParam("naive"); - rs.LeafSize() = size_t(lsInt); + rs->SingleMode() = CLI::HasParam("single_mode"); + rs->Naive() = CLI::HasParam("naive"); + rs->LeafSize() = size_t(lsInt); } // Perform search, if desired. @@ -235,9 +237,9 @@ static void mlpackMain() vector> distances; if (CLI::HasParam("query")) - rs.Search(std::move(queryData), r, neighbors, distances); + rs->Search(std::move(queryData), r, neighbors, distances); else - rs.Search(r, neighbors, distances); + rs->Search(r, neighbors, distances); Log::Info << "Search complete." << endl; @@ -301,7 +303,6 @@ static void mlpackMain() } } - // Save the output model, if desired. - if (CLI::HasParam("output_model")) - CLI::GetParam("output_model") = std::move(rs); + // Save the output model. + CLI::GetParam("output_model") = rs; } diff --git a/src/mlpack/methods/rann/krann_main.cpp b/src/mlpack/methods/rann/krann_main.cpp index dd7fdef538..703fdd0850 100644 --- a/src/mlpack/methods/rann/krann_main.cpp +++ b/src/mlpack/methods/rann/krann_main.cpp @@ -135,11 +135,13 @@ static void mlpackMain() "leaf size must be greater than 0"); // We either have to load the reference data, or we have to load the model. - RANNModel rann; + RANNModel* rann; const bool naive = CLI::HasParam("naive"); const bool singleMode = CLI::HasParam("single_mode"); if (CLI::HasParam("reference")) { + rann = new RANNModel(); + // Get all the parameters. const string treeType = CLI::GetParam("tree_type"); RequireParamInSet("tree_type", { "kd", "cover", "r", "r-star", "x", @@ -169,8 +171,8 @@ static void mlpackMain() else if (treeType == "oct") tree = RANNModel::OCTREE; - rann.TreeType() = tree; - rann.RandomBasis() = randomBasis; + rann->TreeType() = tree; + rann->RandomBasis() = randomBasis; arma::mat referenceSet = std::move(CLI::GetParam("reference")); @@ -179,33 +181,33 @@ static void mlpackMain() << referenceSet.n_rows << " x " << referenceSet.n_cols << ")." << endl; - rann.BuildModel(std::move(referenceSet), size_t(lsInt), naive, singleMode); + rann->BuildModel(std::move(referenceSet), size_t(lsInt), naive, singleMode); } else { // Load the model from file. - rann = std::move(CLI::GetParam("input_model")); + rann = CLI::GetParam("input_model"); Log::Info << "Using rank-approximate kNN model from '" << CLI::GetPrintableParam("input_model") << "' (trained on " - << rann.Dataset().n_rows << "x" << rann.Dataset().n_cols << " dataset)." - << endl; + << rann->Dataset().n_rows << "x" << rann->Dataset().n_cols + << " dataset)." << endl; // Adjust singleMode and naive if necessary. - rann.SingleMode() = CLI::HasParam("single_mode"); - rann.Naive() = CLI::HasParam("naive"); - rann.LeafSize() = size_t(lsInt); + rann->SingleMode() = CLI::HasParam("single_mode"); + rann->Naive() = CLI::HasParam("naive"); + rann->LeafSize() = size_t(lsInt); } // Apply the parameters for search. if (CLI::HasParam("tau")) - rann.Tau() = CLI::GetParam("tau"); + rann->Tau() = CLI::GetParam("tau"); if (CLI::HasParam("alpha")) - rann.Alpha() = CLI::GetParam("alpha"); + rann->Alpha() = CLI::GetParam("alpha"); if (CLI::HasParam("single_sample_limit")) - rann.SingleSampleLimit() = CLI::GetParam("single_sample_limit"); - rann.SampleAtLeaves() = CLI::HasParam("sample_at_leaves"); - rann.FirstLeafExact() = CLI::HasParam("sample_at_leaves"); + rann->SingleSampleLimit() = CLI::GetParam("single_sample_limit"); + rann->SampleAtLeaves() = CLI::HasParam("sample_at_leaves"); + rann->FirstLeafExact() = CLI::HasParam("sample_at_leaves"); // Perform search, if desired. if (CLI::HasParam("k")) @@ -224,28 +226,26 @@ static void mlpackMain() // Sanity check on k value: must be greater than 0, must be less than the // number of reference points. Since it is unsigned, we only test the upper // bound. - if (k > rann.Dataset().n_cols) + if (k > rann->Dataset().n_cols) { Log::Fatal << "Invalid k: " << k << "; must be greater than 0 and less "; Log::Fatal << "than or equal to the number of reference points ("; - Log::Fatal << rann.Dataset().n_cols << ")." << endl; + Log::Fatal << rann->Dataset().n_cols << ")." << endl; } arma::Mat neighbors; arma::mat distances; if (CLI::HasParam("query")) - rann.Search(std::move(queryData), k, neighbors, distances); + rann->Search(std::move(queryData), k, neighbors, distances); else - rann.Search(k, neighbors, distances); + rann->Search(k, neighbors, distances); Log::Info << "Search complete." << endl; - // Save output, if desired. - if (CLI::HasParam("neighbors")) - CLI::GetParam>("neighbors") = std::move(neighbors); - if (CLI::HasParam("distances")) - CLI::GetParam("distances") = std::move(distances); + // Save output. + CLI::GetParam>("neighbors") = std::move(neighbors); + CLI::GetParam("distances") = std::move(distances); } - if (CLI::HasParam("output_model")) - CLI::GetParam("output_model") = std::move(rann); + // Save the output model. + CLI::GetParam("output_model") = rann; } diff --git a/src/mlpack/methods/softmax_regression/softmax_regression_main.cpp b/src/mlpack/methods/softmax_regression/softmax_regression_main.cpp index 4194ed463c..97b54a2a54 100644 --- a/src/mlpack/methods/softmax_regression/softmax_regression_main.cpp +++ b/src/mlpack/methods/softmax_regression/softmax_regression_main.cpp @@ -113,7 +113,7 @@ void TestClassifyAcc(const size_t numClasses, const Model& model); // Build the softmax model given the parameters. template -unique_ptr TrainSoftmax(const size_t maxIterations); +Model* TrainSoftmax(const size_t maxIterations); static void mlpackMain() { @@ -144,13 +144,11 @@ static void mlpackMain() RequireAtLeastOnePassed({ "output_model", "predictions" }, false, "no results" " will be saved"); - using SM = SoftmaxRegression; - unique_ptr sm = TrainSoftmax(maxIterations); + SoftmaxRegression* sm = TrainSoftmax(maxIterations); TestClassifyAcc(sm->NumClasses(), *sm); - if (CLI::HasParam("output_model")) - CLI::GetParam("output_model") = std::move(*sm); + CLI::GetParam("output_model") = sm; } size_t CalculateNumberOfClasses(const size_t numClasses, @@ -233,17 +231,14 @@ void TestClassifyAcc(size_t numClasses, const Model& model) } template -unique_ptr TrainSoftmax(const size_t maxIterations) +Model* TrainSoftmax(const size_t maxIterations) { using namespace mlpack; - using SRF = regression::SoftmaxRegressionFunction; - - unique_ptr sm; + Model* sm; if (CLI::HasParam("input_model")) { - sm.reset(new Model(0, 0, false)); - *sm = std::move(CLI::GetParam("input_model")); + sm = CLI::GetParam("input_model"); } else { @@ -262,8 +257,8 @@ unique_ptr TrainSoftmax(const size_t maxIterations) const size_t numBasis = 5; optimization::L_BFGS optimizer(numBasis, maxIterations); - sm.reset(new Model(trainData, trainLabels, numClasses, - CLI::GetParam("lambda"), intercept, std::move(optimizer))); + sm = new Model(trainData, trainLabels, numClasses, + CLI::GetParam("lambda"), intercept, std::move(optimizer)); } return sm; diff --git a/src/mlpack/methods/sparse_coding/sparse_coding_main.cpp b/src/mlpack/methods/sparse_coding/sparse_coding_main.cpp index 0ce5bf8336..e4b561f530 100644 --- a/src/mlpack/methods/sparse_coding/sparse_coding_main.cpp +++ b/src/mlpack/methods/sparse_coding/sparse_coding_main.cpp @@ -146,9 +146,11 @@ static void mlpackMain() "Newton method tolerance must be nonnegative"); // Do we have an existing model? - SparseCoding sc(0, 0.0); + SparseCoding* sc; if (CLI::HasParam("input_model")) - sc = std::move(CLI::GetParam("input_model")); + sc = CLI::GetParam("input_model"); + else + sc = new SparseCoding(0, 0.0); if (CLI::HasParam("training")) { @@ -162,12 +164,12 @@ static void mlpackMain() matX.col(i) /= norm(matX.col(i), 2); } - sc.Lambda1() = CLI::GetParam("lambda1"); - sc.Lambda2() = CLI::GetParam("lambda2"); - sc.MaxIterations() = (size_t) CLI::GetParam("max_iterations"); - sc.Atoms() = (size_t) CLI::GetParam("atoms"); - sc.ObjTolerance() = CLI::GetParam("objective_tolerance"); - sc.NewtonTolerance() = CLI::GetParam("newton_tolerance"); + sc->Lambda1() = CLI::GetParam("lambda1"); + sc->Lambda2() = CLI::GetParam("lambda2"); + sc->MaxIterations() = (size_t) CLI::GetParam("max_iterations"); + sc->Atoms() = (size_t) CLI::GetParam("atoms"); + sc->ObjTolerance() = CLI::GetParam("objective_tolerance"); + sc->NewtonTolerance() = CLI::GetParam("newton_tolerance"); // Inform the user if we are overwriting their model. if (CLI::HasParam("input_model")) @@ -175,36 +177,43 @@ static void mlpackMain() Log::Info << "Using dictionary from existing model in '" << CLI::GetPrintableParam("input_model") << "' as initial dictionary for training." << endl; - sc.Train(matX); + sc->Train(matX); } else if (CLI::HasParam("initial_dictionary")) { // Load initial dictionary directly into sparse coding object. - sc.Dictionary() = + sc->Dictionary() = std::move(CLI::GetParam("initial_dictionary")); // Validate size of initial dictionary. - if (sc.Dictionary().n_cols != sc.Atoms()) + if (sc->Dictionary().n_cols != sc->Atoms()) { - Log::Fatal << "The initial dictionary has " << sc.Dictionary().n_cols + const size_t dictAtoms = sc->Dictionary().n_cols; + const size_t atoms = sc->Atoms(); + if (!CLI::HasParam("input_model")) + delete sc; + Log::Fatal << "The initial dictionary has " << dictAtoms << " atoms, but the number of atoms was specified to be " - << sc.Atoms() << "!" << endl; + << atoms << "!" << endl; } - if (sc.Dictionary().n_rows != matX.n_rows) + if (sc->Dictionary().n_rows != matX.n_rows) { - Log::Fatal << "The initial dictionary has " << sc.Dictionary().n_rows + const size_t dim = sc->Dictionary().n_rows; + if (!CLI::HasParam("input_model")) + delete sc; + Log::Fatal << "The initial dictionary has " << dim << " dimensions, but the data has " << matX.n_rows << " dimensions!" << endl; } // Run sparse coding. - sc.Train(matX); + sc->Train(matX); } else { // Run sparse coding with the default initialization. - sc.Train(matX); + sc->Train(matX); } } @@ -213,11 +222,16 @@ static void mlpackMain() { mat matY = std::move(CLI::GetParam("test")); - if (matY.n_rows != sc.Dictionary().n_rows) + if (matY.n_rows != sc->Dictionary().n_rows) + { + const size_t dim = sc->Dictionary().n_rows; + if (!CLI::HasParam("input_model")) + delete sc; Log::Fatal << "Model was trained with a dimensionality of " - << sc.Dictionary().n_rows << ", but test data '" + << dim << ", but test data '" << CLI::GetPrintableParam("test") << "' have a " << "dimensionality of " << matY.n_rows << "!" << endl; + } // Normalize each point if the user asked for it. if (CLI::HasParam("normalize")) @@ -228,20 +242,15 @@ static void mlpackMain() } mat codes; - sc.Encode(matY, codes); + sc->Encode(matY, codes); - if (CLI::HasParam("codes")) - CLI::GetParam("codes") = std::move(codes); + CLI::GetParam("codes") = std::move(codes); } - // Did the user want to save the dictionary? If so we can move that, but only - // if we are not also saving an output model. - if (CLI::HasParam("dictionary") && !CLI::HasParam("output_model")) - CLI::GetParam("dictionary") = std::move(sc.Dictionary()); - else if (CLI::HasParam("dictionary")) - CLI::GetParam("dictionary") = sc.Dictionary(); + // Did the user want to save the dictionary? Use an alias for the dictionary. + CLI::GetParam("dictionary") = arma::mat(sc->Dictionary().memptr(), + sc->Dictionary().n_rows, sc->Dictionary().n_cols, false, false); - // Did the user want to save the model? - if (CLI::HasParam("output_model")) - CLI::GetParam("output_model") = std::move(sc); + // Save the model. + CLI::GetParam("output_model") = sc; } diff --git a/src/mlpack/tests/CMakeLists.txt b/src/mlpack/tests/CMakeLists.txt index ae3135533f..12962845e8 100644 --- a/src/mlpack/tests/CMakeLists.txt +++ b/src/mlpack/tests/CMakeLists.txt @@ -125,6 +125,7 @@ add_executable(mlpack_test vantage_point_tree_test.cpp main_tests/test_helper.hpp main_tests/emst_test.cpp + main_tests/adaboost_test.cpp main_tests/decision_tree_test.cpp main_tests/decision_stump_test.cpp main_tests/linear_regression_test.cpp @@ -137,6 +138,7 @@ add_executable(mlpack_test main_tests/preprocess_split_test.cpp main_tests/random_forest_test.cpp main_tests/softmax_regression_test.cpp + main_tests/sparse_coding_test.cpp ) # Link dependencies of test executable. diff --git a/src/mlpack/tests/ada_delta_test.cpp b/src/mlpack/tests/ada_delta_test.cpp index bd14d7faeb..873f162278 100644 --- a/src/mlpack/tests/ada_delta_test.cpp +++ b/src/mlpack/tests/ada_delta_test.cpp @@ -14,7 +14,7 @@ #include #include -#include +#include #include #include diff --git a/src/mlpack/tests/ada_grad_test.cpp b/src/mlpack/tests/ada_grad_test.cpp index 7362af4f36..484b6a7306 100644 --- a/src/mlpack/tests/ada_grad_test.cpp +++ b/src/mlpack/tests/ada_grad_test.cpp @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include #include "test_tools.hpp" diff --git a/src/mlpack/tests/adam_test.cpp b/src/mlpack/tests/adam_test.cpp index 97256d5fa5..0d581cffdd 100644 --- a/src/mlpack/tests/adam_test.cpp +++ b/src/mlpack/tests/adam_test.cpp @@ -15,7 +15,15 @@ #include #include -#include + +#include +#include +#include +#include +#include +#include +#include +#include #include #include @@ -32,6 +40,99 @@ using namespace mlpack; BOOST_AUTO_TEST_SUITE(AdamTest); + +/** + * Test the Adam optimizer on the Sphere function. + */ +BOOST_AUTO_TEST_CASE(AdamSphereFunctionTest) +{ + SphereFunction f(2); + Adam optimizer(0.5, 2, 0.7, 0.999, 1e-8, 500000, 1e-3, false); + + arma::mat coordinates = f.GetInitialPoint(); + optimizer.Optimize(f, coordinates); + + BOOST_REQUIRE_SMALL(coordinates[0], 0.1); + BOOST_REQUIRE_SMALL(coordinates[1], 0.1); +} + +/** + * Test the Adam optimizer on the Wood function. + */ +BOOST_AUTO_TEST_CASE(AdamStyblinskiTangFunctionTest) +{ + StyblinskiTangFunction f(2); + Adam optimizer(0.5, 2, 0.7, 0.999, 1e-8, 500000, 1e-3, false); + + arma::mat coordinates = f.GetInitialPoint(); + optimizer.Optimize(f, coordinates); + + BOOST_REQUIRE_CLOSE(coordinates[0], -2.9, 1.0); // 1% error tolerance. + BOOST_REQUIRE_CLOSE(coordinates[1], -2.9, 1.0); // 1% error tolerance. +} + +/** + * Test the Adam optimizer on the McCormick function. + */ +BOOST_AUTO_TEST_CASE(AdamMcCormickFunctionTest) +{ + McCormickFunction f; + Adam optimizer(0.5, 1, 0.7, 0.999, 1e-8, 500000, 1e-5, false); + + arma::mat coordinates = f.GetInitialPoint(); + optimizer.Optimize(f, coordinates); + + BOOST_REQUIRE_CLOSE(coordinates[0], -0.547, 3.0); // 3% error tolerance. + BOOST_REQUIRE_CLOSE(coordinates[1], -1.547, 3.0); // 3% error tolerance. +} + +/** + * Test the Adam optimizer on the Matyas function. + */ +BOOST_AUTO_TEST_CASE(AdamMatyasFunctionTest) +{ + MatyasFunction f; + Adam optimizer(0.5, 1, 0.7, 0.999, 1e-8, 500000, 1e-5, false); + + arma::mat coordinates = f.GetInitialPoint(); + optimizer.Optimize(f, coordinates); + + // 3% error tolerance. + BOOST_REQUIRE_CLOSE(std::trunc(100.0 * coordinates[0]) / 100.0, 0.0, 3.0); + BOOST_REQUIRE_CLOSE(std::trunc(100.0 * coordinates[1]) / 100.0, 0.0, 3.0); +} + +/** + * Test the Adam optimizer on the Easom function. + */ +BOOST_AUTO_TEST_CASE(AdamEasomFunctionTest) +{ + EasomFunction f; + Adam optimizer(0.2, 1, 0.7, 0.999, 1e-8, 500000, 1e-5, false); + + arma::mat coordinates = arma::mat("2.9; 2.9"); + optimizer.Optimize(f, coordinates); + + // 5% error tolerance. + BOOST_REQUIRE_CLOSE(std::trunc(100.0 * coordinates[0]) / 100.0, 3.14, 3.0); + BOOST_REQUIRE_CLOSE(std::trunc(100.0 * coordinates[1]) / 100.0, 3.14, 3.0); +} + +/** + * Test the Adam optimizer on the Booth function. + */ +BOOST_AUTO_TEST_CASE(AdamBoothFunctionTest) +{ + BoothFunction f; + Adam optimizer(1e-1, 1, 0.7, 0.999, 1e-8, 500000, 1e-9, true); + + arma::mat coordinates = f.GetInitialPoint(); + optimizer.Optimize(f, coordinates); + + BOOST_REQUIRE_CLOSE(coordinates[0], 1.0, 0.2); + BOOST_REQUIRE_CLOSE(coordinates[1], 3.0, 0.2); +} + /** * Tests the Adam optimizer using a simple test function. */ diff --git a/src/mlpack/tests/ann_layer_test.cpp b/src/mlpack/tests/ann_layer_test.cpp index 25edc36c94..c768e744d5 100644 --- a/src/mlpack/tests/ann_layer_test.cpp +++ b/src/mlpack/tests/ann_layer_test.cpp @@ -1144,6 +1144,66 @@ BOOST_AUTO_TEST_CASE(SimpleLogSoftmaxLayerTest) arma::mat("1.6487; 0.6487") - delta)), 1e-3); } +/** + * Simple test for the Sigmoid Cross Entropy Layer. + */ +BOOST_AUTO_TEST_CASE(SimpleSigmoidCrossEntropyLayerTest) +{ + arma::mat input1, input2, input3, output, target1, + target2, target3, expectedOutput; + SigmoidCrossEntropyError<> module; + + // Test the Forward function on a user generator input and compare it against + // the manually calculated result. + input1 = arma::mat("0.5 0.5 0.5 0.5 0.5 0.5 0.5 0.5"); + target1 = arma::zeros(1, 8); + double error1 = module.Forward(std::move(input1), std::move(target1)); + double expected = 0.97407699; + // Value computed using tensorflow. + BOOST_REQUIRE_SMALL(error1 / input1.n_elem - expected, 1e-7); + + input2 = arma::mat("1 2 3 4 5"); + target2 = arma::mat("0 0 1 0 1"); + double error2 = module.Forward(std::move(input2), std::move(target2)); + expected = 1.5027283; + BOOST_REQUIRE_SMALL(error2 / input2.n_elem - expected, 1e-6); + + input3 = arma::mat("0 -1 -1 0 -1 0 0 -1"); + target3 = arma::mat("0 -1 -1 0 -1 0 0 -1"); + double error3 = module.Forward(std::move(input3), std::move(target3)); + expected = 0.00320443; + BOOST_REQUIRE_SMALL(error3 / input3.n_elem - expected, 1e-6); + + // Test the Backward function. + module.Backward(std::move(input1), std::move(target1), std::move(output)); + expected = 0.62245929; + for (size_t i = 0; i < output.n_elem; i++) + BOOST_REQUIRE_SMALL(output(i) - expected, 1e-5); + BOOST_REQUIRE_EQUAL(output.n_rows, input1.n_rows); + BOOST_REQUIRE_EQUAL(output.n_cols, input1.n_cols); + + expectedOutput = arma::mat( + "0.7310586 0.88079709 -0.04742587 0.98201376 -0.00669285"); + module.Backward(std::move(input2), std::move(target2), std::move(output)); + for (size_t i = 0; i < output.n_elem; i++) + BOOST_REQUIRE_SMALL(output(i) - expectedOutput(i), 1e-5); + BOOST_REQUIRE_EQUAL(output.n_rows, input2.n_rows); + BOOST_REQUIRE_EQUAL(output.n_cols, input2.n_cols); + + module.Backward(std::move(input3), std::move(target3), std::move(output)); + expectedOutput = arma::mat("0.5 1.2689414"); + for (size_t i = 0; i < 8; ++i) + { + double el = output.at(0, i); + if (std::abs(input3.at(i) - 0.0) < 1e-5) + BOOST_REQUIRE_SMALL(el - expectedOutput[0], 2e-6); + else + BOOST_REQUIRE_SMALL(el - expectedOutput[1], 2e-6); + } + BOOST_REQUIRE_EQUAL(output.n_rows, input3.n_rows); + BOOST_REQUIRE_EQUAL(output.n_cols, input3.n_cols); +} + /* * Simple test for the cross-entropy error performance function. */ diff --git a/src/mlpack/tests/binarize_test.cpp b/src/mlpack/tests/binarize_test.cpp index 2083383d59..712e79b769 100644 --- a/src/mlpack/tests/binarize_test.cpp +++ b/src/mlpack/tests/binarize_test.cpp @@ -2,7 +2,7 @@ * @file binarize_test.cpp * @author Keon Kim * - * Test the Binarzie method. + * Test the Binarize method. * * mlpack 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 diff --git a/src/mlpack/tests/cli_binding_test.cpp b/src/mlpack/tests/cli_binding_test.cpp index 526bb00545..adab141c9f 100644 --- a/src/mlpack/tests/cli_binding_test.cpp +++ b/src/mlpack/tests/cli_binding_test.cpp @@ -238,20 +238,21 @@ BOOST_AUTO_TEST_CASE(GetParamModelTest) data::Save("kernel.bin", "model", gk); // Create tuple. - gk.Bandwidth(2.0); - tuple t = make_tuple(gk, filename); + tuple t = make_tuple((GaussianKernel*) NULL, + filename); d.value = boost::any(t); // Make sure it is not loaded yet. d.input = true; d.loaded = false; - GaussianKernel* output = NULL; - GetParam((const util::ParamData&) d, (void*) NULL, + GaussianKernel** output = NULL; + GetParam((const util::ParamData&) d, (void*) NULL, (void*) &output); - BOOST_REQUIRE_EQUAL(output->Bandwidth(), 5.0); + BOOST_REQUIRE_EQUAL((*output)->Bandwidth(), 5.0); remove("kernel.bin"); + delete *output; } BOOST_AUTO_TEST_CASE(RawParamDoubleTest) @@ -300,17 +301,17 @@ BOOST_AUTO_TEST_CASE(GetRawParamModelTest) kernel::GaussianKernel gk(5.0); // Create tuple. - tuple t = make_tuple(gk, filename); + tuple t = make_tuple(&gk, filename); d.value = boost::any(t); // Make sure it is not loaded yet. d.input = true; d.loaded = false; - tuple* output = NULL; - GetRawParam>((const util::ParamData&) d, + tuple* output = NULL; + GetRawParam>((const util::ParamData&) d, (void*) NULL, (void*) &output); - BOOST_REQUIRE_EQUAL(get<0>(*output).Bandwidth(), 5.0); + BOOST_REQUIRE_EQUAL(get<0>(*output)->Bandwidth(), 5.0); } BOOST_AUTO_TEST_CASE(GetRawParamDatasetInfoTest) @@ -403,7 +404,7 @@ BOOST_AUTO_TEST_CASE(OutputParamModelTest) // Create value. string filename = "kernel.bin"; GaussianKernel gk(5.0); - tuple t = make_tuple(gk, filename); + tuple t = make_tuple(&gk, filename); d.value = boost::any(t); d.input = false; @@ -491,7 +492,7 @@ BOOST_AUTO_TEST_CASE(SetParamModelTest) // Create initial value. string filename = "kernel.bin"; GaussianKernel gk(2.0); - d.value = boost::any(make_tuple(gk, filename)); + d.value = boost::any(make_tuple(&gk, filename)); // Get a new string. string newFilename = "new_kernel.bin"; @@ -501,8 +502,8 @@ BOOST_AUTO_TEST_CASE(SetParamModelTest) (void*) NULL); // Make sure the change went through. - tuple& t = - *boost::any_cast>(&d.value); + tuple& t = + *boost::any_cast>(&d.value); BOOST_REQUIRE_EQUAL(get<1>(t), "new_kernel.bin"); } @@ -537,4 +538,97 @@ BOOST_AUTO_TEST_CASE(SetParamDatasetInfoMatTest) BOOST_REQUIRE_EQUAL(get<1>(t3), "new_filename.csv"); } +// Test that GetAllocatedMemory() will properly return NULL for a non-model +// type. +BOOST_AUTO_TEST_CASE(GetAllocatedMemoryNonModelTest) +{ + util::ParamData d; + + bool b = true; + d.value = boost::any(b); + d.input = true; + + void* result = (void*) 1; // Invalid pointer, should be overwritten. + + GetAllocatedMemory((const util::ParamData&) d, + (const void*) NULL, (void*) &result); + + BOOST_REQUIRE_EQUAL(result, (void*) NULL); + + // Also test with a matrix type. + arma::mat test(10, 10, arma::fill::ones); + string filename = "test.csv"; + tuple t = make_tuple(test, filename); + d.value = boost::any(t); + + result = (void*) 1; + + GetAllocatedMemory((const util::ParamData&) d, + (const void*) NULL, (void*) &result); + + BOOST_REQUIRE_EQUAL(result, (void*) NULL); +} + +// Test that GetAllocatedMemory() will properly return pointers for a +// serializable model type. +BOOST_AUTO_TEST_CASE(GetAllocatedMemoryModelTest) +{ + util::ParamData d; + + GaussianKernel g(2.0); + string filename = "hello.bin"; + tuple t = make_tuple(&g, filename); + d.value = boost::any(t); + d.input = true; + + void* result = NULL; + + GetAllocatedMemory((const util::ParamData&) d, + (const void*) NULL, (void*) &result); + + BOOST_REQUIRE_EQUAL(&g, (GaussianKernel*) result); +} + +// Test that calling DeleteAllocatedMemory() on non-model types does not delete +// pointers. +BOOST_AUTO_TEST_CASE(DeleteAllocatedMemoryNonModelTest) +{ + util::ParamData d; + + bool b = true; + d.value = boost::any(b); + d.input = true; + + DeleteAllocatedMemory((const util::ParamData&) d, + (const void*) NULL, (void*) NULL); + + arma::mat test(10, 10, arma::fill::ones); + string filename = "test.csv"; + tuple t = make_tuple(test, filename); + d.value = boost::any(t); + + DeleteAllocatedMemory((const util::ParamData&) d, + (const void*) NULL, (void*) NULL); +} + +// Test that DeleteAllocatedMemory() will properly delete pointers for a +// serializable model type. +BOOST_AUTO_TEST_CASE(DeleteAllocatedMemoryModelTest) +{ + // This test will just delete it, and we'll hope that it worked and that + // valgrind won't throw any issues (so really we can't *quite* test this in + // the context of the boost unit test framework). + util::ParamData d; + + GaussianKernel* g = new GaussianKernel(2.0); + string filename = "hello.bin"; + tuple t = make_tuple(g, filename); + + d.value = boost::any(t); + d.input = false; + + DeleteAllocatedMemory((const util::ParamData&) d, + (const void*) NULL, (void*) NULL); +} + BOOST_AUTO_TEST_SUITE_END(); diff --git a/src/mlpack/tests/cli_test.cpp b/src/mlpack/tests/cli_test.cpp index 144e8c9967..5b44385a6f 100644 --- a/src/mlpack/tests/cli_test.cpp +++ b/src/mlpack/tests/cli_test.cpp @@ -866,9 +866,9 @@ BOOST_AUTO_TEST_CASE(UnmappedParamTest) BOOST_REQUIRE_EQUAL(CLI::GetPrintableParam("matrix"), "file1.csv"); BOOST_REQUIRE_EQUAL(CLI::GetPrintableParam("matrix2"), "file2.csv"); - BOOST_REQUIRE_EQUAL(CLI::GetPrintableParam("kernel"), + BOOST_REQUIRE_EQUAL(CLI::GetPrintableParam("kernel"), "kernel.txt"); - BOOST_REQUIRE_EQUAL(CLI::GetPrintableParam("kernel2"), + BOOST_REQUIRE_EQUAL(CLI::GetPrintableParam("kernel2"), "kernel2.txt"); remove("kernel.txt"); @@ -894,9 +894,9 @@ BOOST_AUTO_TEST_CASE(SerializationTest) ParseCommandLine(argc, const_cast(argv)); // Create the kernel we'll save. - GaussianKernel gk(0.5); + GaussianKernel* gk = new GaussianKernel(0.5); - CLI::GetParam("kernel") = move(gk); + CLI::GetParam("kernel") = gk; // Save it. EndProgram(); @@ -910,9 +910,12 @@ BOOST_AUTO_TEST_CASE(SerializationTest) ParseCommandLine(argc, const_cast(argv)); // Load the kernel from file. - GaussianKernel gk2 = move(CLI::GetParam("kernel")); + GaussianKernel* gk2 = CLI::GetParam("kernel"); - BOOST_REQUIRE_CLOSE(gk2.Bandwidth(), 0.5, 1e-5); + BOOST_REQUIRE_CLOSE(gk2->Bandwidth(), 0.5, 1e-5); + + // Clean up the memory... + delete gk2; // Now remove the file we made. remove("kernel.txt"); diff --git a/src/mlpack/tests/cmaes_test.cpp b/src/mlpack/tests/cmaes_test.cpp index 8a42919320..63606d2267 100644 --- a/src/mlpack/tests/cmaes_test.cpp +++ b/src/mlpack/tests/cmaes_test.cpp @@ -12,7 +12,7 @@ */ #include #include -#include +#include #include #include diff --git a/src/mlpack/tests/gmm_test.cpp b/src/mlpack/tests/gmm_test.cpp index 9a694cd12a..f1945c9836 100644 --- a/src/mlpack/tests/gmm_test.cpp +++ b/src/mlpack/tests/gmm_test.cpp @@ -761,7 +761,6 @@ BOOST_AUTO_TEST_CASE(UseExistingModelTest) */ BOOST_AUTO_TEST_CASE(DiagonalGMMTrainTest) { - Log::Warn.ignoreInput = false; // We'll have three diagonal-covariance Gaussian distributions from this // mixture. distribution::GaussianDistribution d1("0.0 1.0 0.0", "1.0 0.0 0.0;" diff --git a/src/mlpack/tests/gradient_clipping_test.cpp b/src/mlpack/tests/gradient_clipping_test.cpp index dc8882a7cc..87afd669c5 100644 --- a/src/mlpack/tests/gradient_clipping_test.cpp +++ b/src/mlpack/tests/gradient_clipping_test.cpp @@ -13,8 +13,7 @@ #include #include #include -#include -#include +#include #include #include "test_tools.hpp" diff --git a/src/mlpack/tests/gradient_descent_test.cpp b/src/mlpack/tests/gradient_descent_test.cpp index 6c07fb35aa..12048d9d8a 100644 --- a/src/mlpack/tests/gradient_descent_test.cpp +++ b/src/mlpack/tests/gradient_descent_test.cpp @@ -11,7 +11,8 @@ */ #include #include -#include + +#include #include #include diff --git a/src/mlpack/tests/lbfgs_test.cpp b/src/mlpack/tests/lbfgs_test.cpp index 2e194f8079..dc95ae0f24 100644 --- a/src/mlpack/tests/lbfgs_test.cpp +++ b/src/mlpack/tests/lbfgs_test.cpp @@ -12,7 +12,10 @@ */ #include #include -#include + +#include +#include +#include #include #include "test_tools.hpp" @@ -42,6 +45,23 @@ BOOST_AUTO_TEST_CASE(RosenbrockFunctionTest) BOOST_REQUIRE_CLOSE(coords[1], 1.0, 1e-5); } +/** + * Tests the L-BFGS optimizer using the Colville Function. + */ +BOOST_AUTO_TEST_CASE(ColvilleFunctionTest) +{ + ColvilleFunction f; + L_BFGS lbfgs; + lbfgs.MaxIterations() = 10000; + + arma::vec coords = f.GetInitialPoint(); + if (!lbfgs.Optimize(f, coords)) + BOOST_FAIL("L-BFGS optimization reported failure."); + + BOOST_REQUIRE_CLOSE(coords[0], 1.0, 1e-5); + BOOST_REQUIRE_CLOSE(coords[1], 1.0, 1e-5); +} + /** * Tests the L-BFGS optimizer using the Wood Function. */ diff --git a/src/mlpack/tests/main_tests/adaboost_test.cpp b/src/mlpack/tests/main_tests/adaboost_test.cpp new file mode 100644 index 0000000000..4d4a3e197a --- /dev/null +++ b/src/mlpack/tests/main_tests/adaboost_test.cpp @@ -0,0 +1,415 @@ +/** + * @file adaboost_test.cpp + * @author Nikhil Goel + * + * Test mlpackMain() of adaboost_main.cpp. + */ +#include + +#define BINDING_TYPE BINDING_TYPE_TEST +static const std::string testName = "AdaBoost"; + +#include +#include +#include "test_helper.hpp" +#include + +#include +#include "../test_tools.hpp" + +using namespace mlpack; + +struct AdaBoostTestFixture +{ + public: + AdaBoostTestFixture() + { + // Cache in the options for this program. + CLI::RestoreSettings(testName); + } + + ~AdaBoostTestFixture() + { + // Clear the settings. + bindings::tests::CleanMemory(); + CLI::ClearSettings(); + } +}; + +BOOST_FIXTURE_TEST_SUITE(AdaBoostMainTest, AdaBoostTestFixture); + +/** + * Check that number of output labels and number of input + * points are equal. + */ +BOOST_AUTO_TEST_CASE(AdaBoostOutputDimensionTest) +{ + arma::mat trainData; + if (!data::Load("vc2.csv", trainData)) + BOOST_FAIL("Unable to load train dataset vc2.csv!"); + + arma::Row labels; + if (!data::Load("vc2_labels.txt", labels)) + BOOST_FAIL("Unable to load label dataset vc2_labels.txt!"); + + arma::mat testData; + if (!data::Load("vc2_test.csv", testData)) + BOOST_FAIL("Unable to load test dataset vc2.csv!"); + + size_t testSize = testData.n_cols; + + SetInputParam("training", std::move(trainData)); + SetInputParam("labels", std::move(labels)); + + SetInputParam("test", std::move(testData)); + + mlpackMain(); + + // Check that number of predicted labels is equal to the input test points. + BOOST_REQUIRE_EQUAL(CLI::GetParam>("output").n_cols, + testSize); + BOOST_REQUIRE_EQUAL(CLI::GetParam>("output").n_rows, 1); +} + +/** + * Ensure that saved model can be used again. + */ +BOOST_AUTO_TEST_CASE(AdaBoostModelReuseTest) +{ + arma::mat trainData; + if (!data::Load("vc2.csv", trainData)) + BOOST_FAIL("Unable to load train dataset vc2.csv!"); + + arma::Row labels; + if (!data::Load("vc2_labels.txt", labels)) + BOOST_FAIL("Unable to load label dataset vc2_labels.txt!"); + + arma::mat testData; + if (!data::Load("vc2_test.csv", testData)) + BOOST_FAIL("Unable to load test dataset vc2.csv!"); + + SetInputParam("training", std::move(trainData)); + SetInputParam("labels", std::move(labels)); + + SetInputParam("test", testData); + + mlpackMain(); + + arma::Row output; + output = std::move(CLI::GetParam>("output")); + + CLI::GetSingleton().Parameters()["training"].wasPassed = false; + CLI::GetSingleton().Parameters()["labels"].wasPassed = false; + CLI::GetSingleton().Parameters()["test"].wasPassed = false; + + SetInputParam("test", std::move(testData)); + SetInputParam("input_model", + CLI::GetParam("output_model")); + + mlpackMain(); + + // Check that initial output and output using saved model are same. + CheckMatrices(output, CLI::GetParam>("output")); +} + +/** + * Test that iterations in adaboost is always non-negative. + */ +BOOST_AUTO_TEST_CASE(AdaBoostItrTest) +{ + arma::mat trainData; + if (!data::Load("trainSet.csv", trainData)) + BOOST_FAIL("Unable load train dataset trainSet.csv!"); + + SetInputParam("training", std::move(trainData)); + SetInputParam("iterations", (int) -1); + + Log::Fatal.ignoreInput = true; + BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error); + Log::Fatal.ignoreInput = false; +} + +/** + * Check that the last dimension of the training set is + * used as labels when labels are not passed specifically + * and results are same from both label and without label models. + */ +BOOST_AUTO_TEST_CASE(AdaBoostWithoutLabelTest) +{ + // Train adaboost without providing labels. + arma::mat trainData; + if (!data::Load("trainSet.csv", trainData)) + BOOST_FAIL("Unable to load train dataset trainSet.csv!"); + + // Give labels. + arma::Row labels(trainData.n_cols); + for (size_t i = 0; i < trainData.n_cols; ++i) + labels[i] = trainData(trainData.n_rows - 1, i); + + arma::mat testData; + if (!data::Load("testSet.csv", testData)) + BOOST_FAIL("Unable to load test dataset testSet.csv!"); + + // Delete the last row containing labels from test dataset. + testData.shed_row(testData.n_rows - 1); + + SetInputParam("training", trainData); + + SetInputParam("test", testData); + + mlpackMain(); + + CLI::GetSingleton().Parameters()["training"].wasPassed = false; + CLI::GetSingleton().Parameters()["test"].wasPassed = false; + + arma::Row output; + output = std::move(CLI::GetParam>("output")); + + bindings::tests::CleanMemory(); + + trainData.shed_row(trainData.n_rows - 1); + + // Now train Adaboost with labels provided. + SetInputParam("training", std::move(trainData)); + SetInputParam("test", std::move(testData)); + SetInputParam("labels", std::move(labels)); + + mlpackMain(); + + // Check that initial output and final output matrix are same. + CheckMatrices(output, CLI::GetParam>("output")); +} + +/** + * Testing that only one of training data or pre-trained model is passed. + */ +BOOST_AUTO_TEST_CASE(AdaBoostTrainingDataOrModelTest) +{ + arma::mat trainData; + if (!data::Load("trainSet.csv", trainData)) + BOOST_FAIL("Unable to load train dataset trainSet.csv!"); + + SetInputParam("training", std::move(trainData)); + + mlpackMain(); + + SetInputParam("input_model", + CLI::GetParam("output_model")); + + Log::Fatal.ignoreInput = true; + BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error); + Log::Fatal.ignoreInput = false; +} + +/** + * Weak learner should be either Decision Stump or Perceptron. + */ +BOOST_AUTO_TEST_CASE(AdaBoostWeakLearnerTest) +{ + arma::mat trainData; + if (!data::Load("trainSet.csv", trainData)) + BOOST_FAIL("Unable to load train dataset trainSet.csv!"); + + SetInputParam("training", std::move(trainData)); + SetInputParam("weak_learner", std::string("decision tree")); + + Log::Fatal.ignoreInput = true; + BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error); + Log::Fatal.ignoreInput = false; +} + +/** + * Different Weak learner should give different outputs. + */ +BOOST_AUTO_TEST_CASE(AdaBoostDiffWeakLearnerOutputTest) +{ + arma::mat trainData; + if (!data::Load("vc2.csv", trainData)) + BOOST_FAIL("Unable to load train dataset vc2.csv!"); + + arma::Row labels; + if (!data::Load("vc2_labels.txt", labels)) + BOOST_FAIL("Unable to load label dataset vc2_labels.txt!"); + + arma::mat testData; + if (!data::Load("vc2_test.csv", testData)) + BOOST_FAIL("Unable to load test dataset vc2.csv!"); + + SetInputParam("training", trainData); + SetInputParam("labels", labels); + SetInputParam("test", testData); + + mlpackMain(); + + arma::Row output; + output = std::move(CLI::GetParam>("output")); + + bindings::tests::CleanMemory(); + + CLI::GetSingleton().Parameters()["training"].wasPassed = false; + CLI::GetSingleton().Parameters()["labels"].wasPassed = false; + CLI::GetSingleton().Parameters()["test"].wasPassed = false; + + SetInputParam("training", trainData); + SetInputParam("labels", labels); + SetInputParam("test", testData); + SetInputParam("weak_learner", std::string("perceptron")); + + mlpackMain(); + + arma::Row outputPerceptron; + outputPerceptron = std::move(CLI::GetParam>("output")); + + BOOST_REQUIRE_GT(arma::accu(output != outputPerceptron), 1); +} + +/** + * Accuracy increases as Number of Iterations increases. + * (Or converges and remains same) + */ +BOOST_AUTO_TEST_CASE(AdaBoostDiffItrTest) +{ + arma::mat trainData; + if (!data::Load("vc2.csv", trainData)) + BOOST_FAIL("Unable to load train dataset vc2.csv!"); + + arma::Row labels; + if (!data::Load("vc2_labels.txt", labels)) + BOOST_FAIL("Unable to load label dataset vc2_labels.txt!"); + + arma::mat testData; + if (!data::Load("vc2_test.csv", testData)) + BOOST_FAIL("Unable to load test dataset vc2.csv!"); + + arma::Row testLabels; + if (!data::Load("vc2_test_labels.txt", testLabels)) + BOOST_FAIL("Unable to load labels for vc2__test_labels.txt"); + + // Iterations = 1 + SetInputParam("training", trainData); + SetInputParam("labels", labels); + SetInputParam("weak_learner", std::string("perceptron")); + SetInputParam("iterations", (int) 1); + + mlpackMain(); + + // Calculate accuracy. + arma::Row output; + CLI::GetParam("output_model")->Classify(testData, + output); + + size_t correct = arma::accu(output == testLabels); + double accuracy1 = (double(correct) / double(testLabels.n_elem) * 100); + + bindings::tests::CleanMemory(); + + // Iterations = 10 + SetInputParam("training", trainData); + SetInputParam("labels", labels); + SetInputParam("weak_learner", std::string("perceptron")); + SetInputParam("iterations", (int) 10); + + mlpackMain(); + + // Calculate accuracy. + CLI::GetParam("output_model")->Classify(testData, + output); + + correct = arma::accu(output == testLabels); + double accuracy10 = (double(correct) / double(testLabels.n_elem) * 100); + + bindings::tests::CleanMemory(); + + // Iterations = 100 + SetInputParam("training", trainData); + SetInputParam("labels", labels); + SetInputParam("weak_learner", std::string("perceptron")); + SetInputParam("iterations", (int) 100); + + mlpackMain(); + + // Calculate accuracy. + CLI::GetParam("output_model")->Classify(testData, + output); + + correct = arma::accu(output == testLabels); + double accuracy100 = (double(correct) / double(testLabels.n_elem) * 100); + + BOOST_REQUIRE_LE(accuracy1, accuracy10); + BOOST_REQUIRE_LE(accuracy10, accuracy100); +} + +/** + * Accuracy increases as tolerance decreases. + * (Execution Time also increases) + */ +BOOST_AUTO_TEST_CASE(AdaBoostDiffTolTest) +{ + arma::mat trainData; + if (!data::Load("vc2.csv", trainData)) + BOOST_FAIL("Unable to load train dataset vc2.csv!"); + + arma::Row labels; + if (!data::Load("vc2_labels.txt", labels)) + BOOST_FAIL("Unable to load label dataset vc2_labels.txt!"); + + arma::mat testData; + if (!data::Load("vc2_test.csv", testData)) + BOOST_FAIL("Unable to load test dataset vc2.csv!"); + + arma::Row testLabels; + if (!data::Load("vc2_test_labels.txt", testLabels)) + BOOST_FAIL("Unable to load labels for vc2__test_labels.txt"); + + // tolerance = 0.001 + SetInputParam("training", trainData); + SetInputParam("labels", labels); + SetInputParam("tolerance", (double) 0.001); + + mlpackMain(); + + // Calculate accuracy. + arma::Row output; + CLI::GetParam("output_model")->Classify(testData, + output); + + size_t correct = arma::accu(output == testLabels); + double accuracy1 = (double(correct) / double(testLabels.n_elem) * 100); + + bindings::tests::CleanMemory(); + + // tolerance = 0.01 + SetInputParam("training", trainData); + SetInputParam("labels", labels); + SetInputParam("tolerance", (double) 0.01); + + mlpackMain(); + + // Calculate accuracy. + CLI::GetParam("output_model")->Classify(testData, + output); + + correct = arma::accu(output == testLabels); + double accuracy2 = (double(correct) / double(testLabels.n_elem) * 100); + + bindings::tests::CleanMemory(); + + // tolerance = 0.1 + SetInputParam("training", trainData); + SetInputParam("labels", labels); + SetInputParam("tolerance", (double) 0.1); + + mlpackMain(); + + // Calculate accuracy. + CLI::GetParam("output_model")->Classify(testData, + output); + + correct = arma::accu(output == testLabels); + double accuracy3 = (double(correct) / double(testLabels.n_elem) * 100); + + BOOST_REQUIRE_LE(accuracy1, accuracy2); + BOOST_REQUIRE_LE(accuracy2, accuracy3); +} + +BOOST_AUTO_TEST_SUITE_END(); diff --git a/src/mlpack/tests/main_tests/decision_stump_test.cpp b/src/mlpack/tests/main_tests/decision_stump_test.cpp index f2e07a9198..f5de6b89d8 100644 --- a/src/mlpack/tests/main_tests/decision_stump_test.cpp +++ b/src/mlpack/tests/main_tests/decision_stump_test.cpp @@ -35,6 +35,7 @@ struct DecisionStumpTestFixture ~DecisionStumpTestFixture() { // Clear the settings. + bindings::tests::CleanMemory(); CLI::ClearSettings(); } }; @@ -136,6 +137,9 @@ BOOST_AUTO_TEST_CASE(DecisionStumpLabelsLessDimensionTest) arma::Row predictions; predictions = std::move(CLI::GetParam>("predictions")); + // Delete the previous model. + bindings::tests::CleanMemory(); + // Now train DS with labels provided. // Delete last row of inputData. @@ -198,7 +202,7 @@ BOOST_AUTO_TEST_CASE(DecisionStumpModelReuseTest) // Input trained model. SetInputParam("test", std::move(testData)); SetInputParam("input_model", - std::move(CLI::GetParam("output_model"))); + std::move(CLI::GetParam("output_model"))); mlpackMain(); @@ -249,7 +253,7 @@ BOOST_AUTO_TEST_CASE(DecisionStumpTrainingVerTest) // Input pre-trained model. SetInputParam("input_model", - std::move(CLI::GetParam("output_model"))); + std::move(CLI::GetParam("output_model"))); Log::Fatal.ignoreInput = true; BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error); diff --git a/src/mlpack/tests/main_tests/decision_tree_test.cpp b/src/mlpack/tests/main_tests/decision_tree_test.cpp index 6e0788a337..7e48d3ecbe 100644 --- a/src/mlpack/tests/main_tests/decision_tree_test.cpp +++ b/src/mlpack/tests/main_tests/decision_tree_test.cpp @@ -36,6 +36,7 @@ struct DecisionTreeTestFixture ~DecisionTreeTestFixture() { // Clear the settings. + bindings::tests::CleanMemory(); CLI::ClearSettings(); } }; @@ -207,7 +208,7 @@ BOOST_AUTO_TEST_CASE(DecisionModelReuseTest) // Input trained model. SetInputParam("test", std::move(std::make_tuple(info, testData))); SetInputParam("input_model", - std::move(CLI::GetParam("output_model"))); + std::move(CLI::GetParam("output_model"))); mlpackMain(); @@ -254,7 +255,7 @@ BOOST_AUTO_TEST_CASE(DecisionTreeTrainingVerTest) // Input pre-trained model. SetInputParam("input_model", - std::move(CLI::GetParam("output_model"))); + std::move(CLI::GetParam("output_model"))); Log::Fatal.ignoreInput = true; BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error); @@ -308,7 +309,7 @@ BOOST_AUTO_TEST_CASE(DecisionModelCategoricalReuseTest) // Input trained model. SetInputParam("test", std::move(std::make_tuple(info, testData))); SetInputParam("input_model", - std::move(CLI::GetParam("output_model"))); + std::move(CLI::GetParam("output_model"))); mlpackMain(); diff --git a/src/mlpack/tests/main_tests/emst_test.cpp b/src/mlpack/tests/main_tests/emst_test.cpp index f49d934a88..87ca2cd3a9 100644 --- a/src/mlpack/tests/main_tests/emst_test.cpp +++ b/src/mlpack/tests/main_tests/emst_test.cpp @@ -38,6 +38,7 @@ struct EMSTTestFixture ~EMSTTestFixture() { // Clear the settings. + bindings::tests::CleanMemory(); CLI::ClearSettings(); } }; diff --git a/src/mlpack/tests/main_tests/linear_regression_test.cpp b/src/mlpack/tests/main_tests/linear_regression_test.cpp index e3d0d7993d..d24e928844 100644 --- a/src/mlpack/tests/main_tests/linear_regression_test.cpp +++ b/src/mlpack/tests/main_tests/linear_regression_test.cpp @@ -32,6 +32,7 @@ struct LRTestFixture ~LRTestFixture() { // Clear the settings. + bindings::tests::CleanMemory(); CLI::ClearSettings(); } }; @@ -66,6 +67,7 @@ BOOST_AUTO_TEST_CASE(LRDifferentLambdas) mlpackMain(); const double testY1 = CLI::GetParam("output_predictions")(0); + bindings::tests::CleanMemory(); ResetSettings(); SetInputParam("training", std::move(trainX)); @@ -100,6 +102,7 @@ BOOST_AUTO_TEST_CASE(LRResponsesRepresentation) mlpackMain(); const double testY1 = CLI::GetParam("output_predictions")(0); + bindings::tests::CleanMemory(); ResetSettings(); arma::mat trainX2({1.0, 2.0, 3.0}); @@ -135,12 +138,12 @@ BOOST_AUTO_TEST_CASE(LRModelReload) mlpackMain(); - LinearRegression model = CLI::GetParam("output_model"); + LinearRegression* model = CLI::GetParam("output_model"); const arma::rowvec testY1 = CLI::GetParam("output_predictions"); ResetSettings(); - SetInputParam("input_model", std::move(model)); + SetInputParam("input_model", model); SetInputParam("test", std::move(testX)); mlpackMain(); @@ -209,7 +212,7 @@ BOOST_AUTO_TEST_CASE(LRWrongDimOfDataTest2) mlpackMain(); - LinearRegression model = CLI::GetParam("output_model"); + LinearRegression* model = CLI::GetParam("output_model"); ResetSettings(); diff --git a/src/mlpack/tests/main_tests/nbc_test.cpp b/src/mlpack/tests/main_tests/nbc_test.cpp index ebb58cab27..212e0d1d98 100644 --- a/src/mlpack/tests/main_tests/nbc_test.cpp +++ b/src/mlpack/tests/main_tests/nbc_test.cpp @@ -35,6 +35,7 @@ struct NBCTestFixture ~NBCTestFixture() { // Clear the settings. + bindings::tests::CleanMemory(); CLI::ClearSettings(); } }; @@ -142,6 +143,8 @@ BOOST_AUTO_TEST_CASE(NBCLabelsLessDimensionTest) output = std::move(CLI::GetParam>("output")); output_probs = std::move(CLI::GetParam("output_probs")); + bindings::tests::CleanMemory(); + // Now train NBC with labels provided. inputData.shed_row(inputData.n_rows - 1); @@ -208,7 +211,7 @@ BOOST_AUTO_TEST_CASE(NBCModelReuseTest) // Input trained model. SetInputParam("test", std::move(testData)); SetInputParam("input_model", - std::move(CLI::GetParam("output_model"))); + std::move(CLI::GetParam("output_model"))); mlpackMain(); @@ -244,7 +247,7 @@ BOOST_AUTO_TEST_CASE(NBCTrainingVerTest) // Input pre-trained model. SetInputParam("input_model", - std::move(CLI::GetParam("output_model"))); + std::move(CLI::GetParam("output_model"))); Log::Fatal.ignoreInput = true; BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error); @@ -290,6 +293,8 @@ BOOST_AUTO_TEST_CASE(NBCIncrementalVarianceTest) BOOST_REQUIRE_EQUAL(CLI::GetParam>("output").n_rows, 1); BOOST_REQUIRE_EQUAL(CLI::GetParam("output_probs").n_rows, 2); + bindings::tests::CleanMemory(); + // Reset data passed. CLI::GetSingleton().Parameters()["training"].wasPassed = false; CLI::GetSingleton().Parameters()["incremental_variance"].wasPassed = false; diff --git a/src/mlpack/tests/main_tests/pca_test.cpp b/src/mlpack/tests/main_tests/pca_test.cpp index a191aef906..b2b51984ed 100644 --- a/src/mlpack/tests/main_tests/pca_test.cpp +++ b/src/mlpack/tests/main_tests/pca_test.cpp @@ -31,6 +31,7 @@ struct PCATestFixture ~PCATestFixture() { // Clear the settings. + bindings::tests::CleanMemory(); CLI::ClearSettings(); } }; diff --git a/src/mlpack/tests/main_tests/perceptron_test.cpp b/src/mlpack/tests/main_tests/perceptron_test.cpp index 1dae568787..e0a6290e92 100644 --- a/src/mlpack/tests/main_tests/perceptron_test.cpp +++ b/src/mlpack/tests/main_tests/perceptron_test.cpp @@ -35,6 +35,7 @@ struct PerceptronTestFixture ~PerceptronTestFixture() { // Clear the settings. + bindings::tests::CleanMemory(); CLI::ClearSettings(); } }; @@ -143,7 +144,9 @@ BOOST_AUTO_TEST_CASE(PerceptronLabelsLessDimensionTest) arma::Row output; output = std::move(CLI::GetParam>("output")); - // Now train pereptron with labels provided. + bindings::tests::CleanMemory(); + + // Now train perceptron with labels provided. // Input training data. SetInputParam("training", std::move(inputData)); @@ -201,7 +204,7 @@ BOOST_AUTO_TEST_CASE(PerceptronModelReuseTest) // Input trained model. SetInputParam("test", std::move(testData)); SetInputParam("input_model", - std::move(CLI::GetParam("output_model"))); + CLI::GetParam("output_model")); mlpackMain(); diff --git a/src/mlpack/tests/main_tests/preprocess_binarize_test.cpp b/src/mlpack/tests/main_tests/preprocess_binarize_test.cpp index 5f47d0dad9..bf93f75464 100644 --- a/src/mlpack/tests/main_tests/preprocess_binarize_test.cpp +++ b/src/mlpack/tests/main_tests/preprocess_binarize_test.cpp @@ -35,6 +35,7 @@ struct PreprocessBinarizeTestFixture ~PreprocessBinarizeTestFixture() { // Clear the settings. + bindings::tests::CleanMemory(); CLI::ClearSettings(); } }; diff --git a/src/mlpack/tests/main_tests/preprocess_imputer_test.cpp b/src/mlpack/tests/main_tests/preprocess_imputer_test.cpp index bf30f102eb..ce1ed4f104 100644 --- a/src/mlpack/tests/main_tests/preprocess_imputer_test.cpp +++ b/src/mlpack/tests/main_tests/preprocess_imputer_test.cpp @@ -37,6 +37,7 @@ struct PreprocessImputerTestFixture ~PreprocessImputerTestFixture() { // Clear the settings. + bindings::tests::CleanMemory(); CLI::ClearSettings(); } }; diff --git a/src/mlpack/tests/main_tests/preprocess_split_test.cpp b/src/mlpack/tests/main_tests/preprocess_split_test.cpp index 5911e92b8c..509a74a3d3 100644 --- a/src/mlpack/tests/main_tests/preprocess_split_test.cpp +++ b/src/mlpack/tests/main_tests/preprocess_split_test.cpp @@ -37,6 +37,7 @@ struct PreprocessSplitTestFixture ~PreprocessSplitTestFixture() { // Clear the settings. + bindings::tests::CleanMemory(); CLI::ClearSettings(); } }; diff --git a/src/mlpack/tests/main_tests/random_forest_test.cpp b/src/mlpack/tests/main_tests/random_forest_test.cpp index f4460ed752..ab4a8460da 100644 --- a/src/mlpack/tests/main_tests/random_forest_test.cpp +++ b/src/mlpack/tests/main_tests/random_forest_test.cpp @@ -35,6 +35,7 @@ struct RandomForestTestFixture ~RandomForestTestFixture() { // Clear the settings. + bindings::tests::CleanMemory(); CLI::ClearSettings(); } }; @@ -124,7 +125,7 @@ BOOST_AUTO_TEST_CASE(RandomForestModelReuseTest) // Input trained model. SetInputParam("test", std::move(testData)); SetInputParam("input_model", - std::move(CLI::GetParam("output_model"))); + CLI::GetParam("output_model")); mlpackMain(); @@ -206,7 +207,7 @@ BOOST_AUTO_TEST_CASE(RandomForestTrainingVerTest) // Input pre-trained model. SetInputParam("input_model", - std::move(CLI::GetParam("output_model"))); + CLI::GetParam("output_model")); Log::Fatal.ignoreInput = true; BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error); @@ -218,7 +219,7 @@ BOOST_AUTO_TEST_CASE(RandomForestTrainingVerTest) */ BOOST_AUTO_TEST_CASE(RandomForestDiffMinLeafSizeTest) { - // Train for minimium leaf size 20. + // Train for minimum leaf size 20. arma::mat inputData; if (!data::Load("vc2.csv", inputData)) BOOST_FAIL("Cannot load train dataset vc2.csv!"); @@ -236,13 +237,15 @@ BOOST_AUTO_TEST_CASE(RandomForestDiffMinLeafSizeTest) // Calculate training accuracy. arma::Row predictions; - CLI::GetParam("output_model").rf.Classify(inputData, + CLI::GetParam("output_model")->rf.Classify(inputData, predictions); size_t correct = arma::accu(predictions == labels); double accuracy20 = (double(correct) / double(labels.n_elem) * 100); - // Train for minimium leaf size 10. + bindings::tests::CleanMemory(); + + // Train for minimum leaf size 10. // Input training data. SetInputParam("training", inputData); @@ -252,13 +255,15 @@ BOOST_AUTO_TEST_CASE(RandomForestDiffMinLeafSizeTest) mlpackMain(); // Calculate training accuracy. - CLI::GetParam("output_model").rf.Classify(inputData, + CLI::GetParam("output_model")->rf.Classify(inputData, predictions); correct = arma::accu(predictions == labels); double accuracy10 = (double(correct) / double(labels.n_elem) * 100); - // Train for minimium leaf size 1. + bindings::tests::CleanMemory(); + + // Train for minimum leaf size 1. // Input training data. SetInputParam("training", inputData); @@ -268,7 +273,7 @@ BOOST_AUTO_TEST_CASE(RandomForestDiffMinLeafSizeTest) mlpackMain(); // Calculate training accuracy. - CLI::GetParam("output_model").rf.Classify(inputData, + CLI::GetParam("output_model")->rf.Classify(inputData, predictions); correct = arma::accu(predictions == labels); @@ -308,8 +313,9 @@ BOOST_AUTO_TEST_CASE(RandomForestDiffNumTreeTest) // Calculate training accuracy. arma::Row predictions; - CLI::GetParam("output_model").rf.Classify(testData, + CLI::GetParam("output_model")->rf.Classify(testData, predictions); + bindings::tests::CleanMemory(); size_t correct = arma::accu(predictions == testLabels); double accuracy1 = (double(correct) / double(testLabels.n_elem) * 100); @@ -324,8 +330,9 @@ BOOST_AUTO_TEST_CASE(RandomForestDiffNumTreeTest) mlpackMain(); // Calculate training accuracy. - CLI::GetParam("output_model").rf.Classify(testData, + CLI::GetParam("output_model")->rf.Classify(testData, predictions); + bindings::tests::CleanMemory(); correct = arma::accu(predictions == testLabels); double accuracy5 = (double(correct) / double(testLabels.n_elem) * 100); @@ -340,7 +347,7 @@ BOOST_AUTO_TEST_CASE(RandomForestDiffNumTreeTest) mlpackMain(); // Calculate training accuracy. - CLI::GetParam("output_model").rf.Classify(testData, + CLI::GetParam("output_model")->rf.Classify(testData, predictions); correct = arma::accu(predictions == testLabels); diff --git a/src/mlpack/tests/main_tests/softmax_regression_test.cpp b/src/mlpack/tests/main_tests/softmax_regression_test.cpp index 04dc10169a..f646b69b9c 100644 --- a/src/mlpack/tests/main_tests/softmax_regression_test.cpp +++ b/src/mlpack/tests/main_tests/softmax_regression_test.cpp @@ -35,6 +35,7 @@ struct SoftmaxRegressionTestFixture ~SoftmaxRegressionTestFixture() { // Clear the settings. + bindings::tests::CleanMemory(); CLI::ClearSettings(); } }; @@ -150,7 +151,7 @@ BOOST_AUTO_TEST_CASE(SoftmaxRegressionModelReuseTest) // Input trained model. SetInputParam("test", std::move(testData)); SetInputParam("input_model", - std::move(CLI::GetParam("output_model"))); + CLI::GetParam("output_model")); mlpackMain(); @@ -273,7 +274,7 @@ BOOST_AUTO_TEST_CASE(SoftmaxRegressionTrainingVerTest) // Input pre-trained model. SetInputParam("input_model", - std::move(CLI::GetParam("output_model"))); + CLI::GetParam("output_model")); Log::Fatal.ignoreInput = true; BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error); @@ -306,8 +307,6 @@ BOOST_AUTO_TEST_CASE(SoftmaxRegressionDiffLambdaTest) // Delete the last row containing labels from test dataset. testData.shed_row(testData.n_rows - 1); - size_t testSize = testData.n_cols; - // Input training data. SetInputParam("training", inputData); SetInputParam("labels", labels); @@ -320,7 +319,9 @@ BOOST_AUTO_TEST_CASE(SoftmaxRegressionDiffLambdaTest) // Store output parameters. arma::mat modelParam; - modelParam = CLI::GetParam("output_model").Parameters(); + modelParam = CLI::GetParam("output_model")->Parameters(); + + bindings::tests::CleanMemory(); // Reset passed parameters. CLI::GetSingleton().Parameters()["training"].wasPassed = false; @@ -342,13 +343,13 @@ BOOST_AUTO_TEST_CASE(SoftmaxRegressionDiffLambdaTest) for (size_t i = 0; i < modelParam.n_elem; ++i) { BOOST_REQUIRE_NE(modelParam[i], - CLI::GetParam("output_model").Parameters()[i]); + CLI::GetParam("output_model")->Parameters()[i]); } } /** - * Check that output object parameters are different - * for different numbers of max_iterations. + * Check that output object parameters are different for different numbers of + * max_iterations. */ BOOST_AUTO_TEST_CASE(SoftmaxRegressionDiffMaxItrTest) { @@ -372,8 +373,6 @@ BOOST_AUTO_TEST_CASE(SoftmaxRegressionDiffMaxItrTest) // Delete the last row containing labels from test dataset. testData.shed_row(testData.n_rows - 1); - size_t testSize = testData.n_cols; - // Input training data. SetInputParam("training", inputData); SetInputParam("labels", labels); @@ -386,7 +385,9 @@ BOOST_AUTO_TEST_CASE(SoftmaxRegressionDiffMaxItrTest) // Store output parameters. arma::mat modelParam; - modelParam = CLI::GetParam("output_model").Parameters(); + modelParam = CLI::GetParam("output_model")->Parameters(); + + bindings::tests::CleanMemory(); // Reset passed parameters. CLI::GetSingleton().Parameters()["training"].wasPassed = false; @@ -408,7 +409,7 @@ BOOST_AUTO_TEST_CASE(SoftmaxRegressionDiffMaxItrTest) for (size_t i = 0; i < modelParam.n_elem; ++i) { BOOST_REQUIRE_NE(modelParam[i], - CLI::GetParam("output_model").Parameters()[i]); + CLI::GetParam("output_model")->Parameters()[i]); } } @@ -438,8 +439,6 @@ BOOST_AUTO_TEST_CASE(SoftmaxRegressionDiffInterceptTest) // Delete the last row containing labels from test dataset. testData.shed_row(testData.n_rows - 1); - size_t testSize = testData.n_cols; - // Input training data. SetInputParam("training", inputData); SetInputParam("labels", labels); @@ -452,7 +451,9 @@ BOOST_AUTO_TEST_CASE(SoftmaxRegressionDiffInterceptTest) // Store output parameters. arma::mat modelParam; - modelParam = CLI::GetParam("output_model").Parameters(); + modelParam = CLI::GetParam("output_model")->Parameters(); + + bindings::tests::CleanMemory(); // Reset passed parameters. CLI::GetSingleton().Parameters()["training"].wasPassed = false; @@ -472,7 +473,7 @@ BOOST_AUTO_TEST_CASE(SoftmaxRegressionDiffInterceptTest) // Check that initial parameters has 1 more parameter than // final parameters matrix. BOOST_REQUIRE_EQUAL( - CLI::GetParam("output_model").Parameters().n_cols, + CLI::GetParam("output_model")->Parameters().n_cols, modelParam.n_cols + 1); } diff --git a/src/mlpack/tests/main_tests/sparse_coding_test.cpp b/src/mlpack/tests/main_tests/sparse_coding_test.cpp new file mode 100644 index 0000000000..103c8fb366 --- /dev/null +++ b/src/mlpack/tests/main_tests/sparse_coding_test.cpp @@ -0,0 +1,699 @@ +/** + * @file sparse_coding_test.cpp + * @author Manish Kumar + * + * Test mlpackMain() of sparse_coding_main.cpp. + * + * mlpack 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 mlpack. If not, see + * http://www.opensource.org/licenses/BSD-3-Clause for more information. + */ +#include + +#define BINDING_TYPE BINDING_TYPE_TEST +static const std::string testName = "SparseCoding"; + +#include +#include +#include +#include "test_helper.hpp" + +#include +#include "../test_tools.hpp" + +using namespace mlpack; + +struct SparseCodingTestFixture +{ + public: + SparseCodingTestFixture() + { + // Cache in the options for this program. + CLI::RestoreSettings(testName); + } + + ~SparseCodingTestFixture() + { + // Clear the settings. + bindings::tests::CleanMemory(); + CLI::ClearSettings(); + } +}; + +BOOST_FIXTURE_TEST_SUITE(SparseCodingMainTest, SparseCodingTestFixture); + +/** + * Helper function to load datasets. + */ +void LoadData(arma::mat& inputData, arma::mat& testData) +{ + // Load train dataset. + if (!data::Load("iris_train.csv", inputData)) + BOOST_FAIL("Cannot load train dataset iris_train.csv!"); + + // Load test dataset. + if (!data::Load("iris_test.csv", testData)) + BOOST_FAIL("Cannot load test dataset iris_test.csv!"); +} + +/** + * Make sure that output points in dictionary equals number of + * atoms passed and codes have desired dimension. + */ +BOOST_AUTO_TEST_CASE(SparseCodingOutputDimensionTest) +{ + arma::mat inputData; + arma::mat testData; + LoadData(inputData, testData); + + mat initialDictionary = inputData.cols(0, 1); + + // Input data. + SetInputParam("training", std::move(inputData)); + SetInputParam("atoms", (int) 2); + SetInputParam("max_iterations", (int) 100); + SetInputParam("test", std::move(testData)); + + mlpackMain(); + + // Check that number of output dictionary points are equals number of atoms. + BOOST_REQUIRE_EQUAL(CLI::GetParam("dictionary").n_cols, 2); + + // Check that number of output dictionary rows equal number of input rows + // which equal 4 for each data point. + BOOST_REQUIRE_EQUAL(CLI::GetParam("dictionary").n_rows, 4); + + // Check that number of output points are equal to number of test points. + // Test file contains 63 data points. + BOOST_REQUIRE_EQUAL(CLI::GetParam("codes").n_cols, 63); + + // Check that number of output codes rows equal number of atoms. + BOOST_REQUIRE_EQUAL(CLI::GetParam("codes").n_rows, 2); +} + +/** + * Ensure that training data is normalized if normalize + * parameter is set to true. + */ +BOOST_AUTO_TEST_CASE(SparseCodingNormalizationTest) +{ + arma::mat inputData; + arma::mat testData; + LoadData(inputData, testData); + + mat initialDictionary = inputData.cols(0, 1); + + // Train for normalization set to true. + + // Input data. + SetInputParam("training", inputData); + SetInputParam("atoms", (int) 2); + SetInputParam("initial_dictionary", initialDictionary); + SetInputParam("max_iterations", (int) 100); + SetInputParam("normalize", (bool) true); + SetInputParam("test", testData); + + mlpackMain(); + + // Store outputs. + arma::mat dictionary = CLI::GetParam("dictionary"); + arma::mat codes = + std::move(CLI::GetParam("codes")); + + // Train for normalization set to false. + + // Reset passed parameters. + bindings::tests::CleanMemory(); + CLI::GetSingleton().Parameters()["normalize"].wasPassed = false; + + // Normalize train dataset. + for (size_t i = 0; i < inputData.n_cols; ++i) + inputData.col(i) /= norm(inputData.col(i), 2); + + // Normalize test dataset. + for (size_t i = 0; i < testData.n_cols; ++i) + testData.col(i) /= norm(testData.col(i), 2); + + // Input data. + SetInputParam("training", std::move(inputData)); + SetInputParam("atoms", (int) 2); + SetInputParam("initial_dictionary", std::move(initialDictionary)); + SetInputParam("max_iterations", (int) 100); + SetInputParam("test", std::move(testData)); + + mlpackMain(); + + // Check that initial outputs and final outputs + // using two models model are same. + CheckMatrices(dictionary, CLI::GetParam("dictionary")); + CheckMatrices(codes, CLI::GetParam("codes")); +} + +/** + * Ensure that l1, l2, max_iterations, objective_tolerance, + * newton_tolerance value is always non-negative and number + * of atoms is always positive. + */ +BOOST_AUTO_TEST_CASE(SparseCodingBoundsTest) +{ + arma::mat inputData; + if (!data::Load("iris_train.csv", inputData)) + BOOST_FAIL("Cannot load train dataset iris_train.csv!"); + + // Test for L1 value. + + // Input training data. + SetInputParam("training", inputData); + SetInputParam("atoms", (int) 10); + SetInputParam("lambda1", (double) -1.0); + + Log::Fatal.ignoreInput = true; + BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error); + Log::Fatal.ignoreInput = false; + + // Test for L2 value. + + // Input training data. + bindings::tests::CleanMemory(); + SetInputParam("training", inputData); + SetInputParam("atoms", (int) 10); + SetInputParam("lambda2", (double) -1.0); + + Log::Fatal.ignoreInput = true; + BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error); + Log::Fatal.ignoreInput = false; + + // Test for max_iterations. + + // Input training data. + bindings::tests::CleanMemory(); + SetInputParam("training", inputData); + SetInputParam("atoms", (int) 10); + SetInputParam("max_iterations", (int) -1.0); + + Log::Fatal.ignoreInput = true; + BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error); + Log::Fatal.ignoreInput = false; + + // Test for objective_tolerance. + + // Input training data. + bindings::tests::CleanMemory(); + SetInputParam("training", inputData); + SetInputParam("atoms", (int) 10); + SetInputParam("objective_tolerance", (double) -1.0); + + Log::Fatal.ignoreInput = true; + BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error); + Log::Fatal.ignoreInput = false; + + // Test for newton_tolerance. + + // Input training data. + bindings::tests::CleanMemory(); + SetInputParam("training", inputData); + SetInputParam("atoms", (int) 10); + SetInputParam("newton_tolerance", (double) -1.0); + + Log::Fatal.ignoreInput = true; + BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error); + Log::Fatal.ignoreInput = false; + + // Test for atoms. + + // Input training data. + SetInputParam("training", std::move(inputData)); + SetInputParam("atoms", (int) 0); + + Log::Fatal.ignoreInput = true; + BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error); + Log::Fatal.ignoreInput = false; +} + +/** + * Make sure atoms are specified if training data is passed. + */ +BOOST_AUTO_TEST_CASE(SparseCodingReqAtomsTest) +{ + arma::mat inputData; + if (!data::Load("iris_train.csv", inputData)) + BOOST_FAIL("Cannot load train dataset iris_train.csv!"); + + // Input training data. + SetInputParam("training", std::move(inputData)); + + Log::Fatal.ignoreInput = true; + BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error); + Log::Fatal.ignoreInput = false; +} + +/** + * Ensure only one of input_model or initial_dictionary + * is specified. + */ +BOOST_AUTO_TEST_CASE(SparseCodingModelVerTest) +{ + arma::mat inputData; + arma::mat testData; + LoadData(inputData, testData); + + mat initialDictionary = inputData.cols(0, 1); + SparseCoding* c = new SparseCoding(); + + // Input trained model and initial_dictionary. + SetInputParam("input_model", c); + SetInputParam("initial_dictionary", std::move(initialDictionary)); + + Log::Fatal.ignoreInput = true; + BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error); + Log::Fatal.ignoreInput = false; +} + +/** + * Ensure that specified number of atoms and initial_dictionary + * atoms are equal. + */ +BOOST_AUTO_TEST_CASE(SparseCodingAtomsVerTest) +{ + arma::mat inputData; + arma::mat testData; + LoadData(inputData, testData); + + mat initialDictionary = inputData.cols(0, 1); // 2 points. + + // Input data and initial_dictionary. + SetInputParam("training", std::move(inputData)); + SetInputParam("atoms", (int) 40); // Invalid. + SetInputParam("initial_dictionary", std::move(initialDictionary)); + SetInputParam("max_iterations", (int) 100); + + Log::Fatal.ignoreInput = true; + BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error); + Log::Fatal.ignoreInput = false; +} + +/** + * Ensure that input data and initial_dictionary + * have same number of rows. + */ +BOOST_AUTO_TEST_CASE(SparseCodingRowsVerTest) +{ + arma::mat inputData; + arma::mat testData; + LoadData(inputData, testData); + + mat initialDictionary = inputData.cols(0, 1); + + // Trim inputData. + inputData.shed_rows(1, 2); + + // Input data and initial_dictionary. + SetInputParam("training", std::move(inputData)); // Invalid Data. + SetInputParam("atoms", (int) 2); + SetInputParam("initial_dictionary", std::move(initialDictionary)); + SetInputParam("max_iterations", (int) 100); + SetInputParam("normalize", (bool) true); + + Log::Fatal.ignoreInput = true; + BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error); + Log::Fatal.ignoreInput = false; +} + +/** + * Ensure that training data and test data + * have same dimensionality w.r.t rows. + */ +BOOST_AUTO_TEST_CASE(SparseCodingDataDimensionalityTest) +{ + arma::mat inputData; + arma::mat testData; + LoadData(inputData, testData); + + mat initialDictionary = inputData.cols(0, 1); + + // Trim testData. + testData.shed_rows(1, 2); + + // Input data. + SetInputParam("training", inputData); + SetInputParam("atoms", (int) 2); + SetInputParam("max_iterations", (int) 100); + SetInputParam("test", std::move(testData)); + + Log::Fatal.ignoreInput = true; + BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error); + Log::Fatal.ignoreInput = false; +} + +/** + * Check that saved model can be reused again. + */ +BOOST_AUTO_TEST_CASE(SparseCodingModelReuseTest) +{ + arma::mat inputData; + arma::mat testData; + LoadData(inputData, testData); + + // Input data. + SetInputParam("training", inputData); + SetInputParam("atoms", (int) 2); + SetInputParam("max_iterations", (int) 100); + SetInputParam("normalize", (bool) true); + SetInputParam("test", testData); + + mlpackMain(); + + // Store outputs. + arma::mat dictionary = + std::move(CLI::GetParam("dictionary")); + arma::mat codes = + std::move(CLI::GetParam("codes")); + + // Reset passed parameters. + CLI::GetSingleton().Parameters()["training"].wasPassed = false; + + // Test the correctness of trained model. + + // Input data. + SetInputParam("max_iterations", (int) 100); + SetInputParam("input_model", CLI::GetParam("output_model")); + SetInputParam("normalize", (bool) true); + SetInputParam("test", std::move(testData)); + + mlpackMain(); + + // Check that number of output dictionary points are equals number of atoms. + BOOST_REQUIRE_EQUAL(CLI::GetParam("dictionary").n_cols, 2); + + // Check that number of output dictionary rows equal number of input rows + // which equal 4 for each data point. + BOOST_REQUIRE_EQUAL(CLI::GetParam("dictionary").n_rows, 4); + + // Check that number of output points are equal to number of test points. + // Test file contains 63 data points. + BOOST_REQUIRE_EQUAL(CLI::GetParam("codes").n_cols, 63); + + // Check that number of output codes rows equal number of atoms. + BOOST_REQUIRE_EQUAL(CLI::GetParam("codes").n_rows, 2); + + // Check that initial outputs and final outputs + // using two models model are same. + CheckMatrices(dictionary, CLI::GetParam("dictionary")); + CheckMatrices(codes, CLI::GetParam("codes")); +} + +/** + * Ensure that for different value of max iterations + * outputs are different. + */ +BOOST_AUTO_TEST_CASE(SparseCodingDiffMaxItrTest) +{ + arma::mat inputData; + arma::mat testData; + LoadData(inputData, testData); + + mat initialDictionary = inputData.cols(0, 1); + + // Train for max_iterations equals to 2. + + // Input data. + SetInputParam("training", inputData); + SetInputParam("atoms", (int) 2); + SetInputParam("initial_dictionary", initialDictionary); + SetInputParam("max_iterations", (int) 2); + SetInputParam("normalize", (bool) true); + SetInputParam("test", testData); + + mlpackMain(); + + // Store outputs. + arma::mat dictionary = CLI::GetParam("dictionary"); + arma::mat codes = + std::move(CLI::GetParam("codes")); + + // Train for max_iterations equals to 100. + + // Input data. + bindings::tests::CleanMemory(); + SetInputParam("training", std::move(inputData)); + SetInputParam("atoms", (int) 2); + SetInputParam("initial_dictionary", std::move(initialDictionary)); + SetInputParam("max_iterations", (int) 100); + SetInputParam("normalize", (bool) true); + SetInputParam("test", std::move(testData)); + + mlpackMain(); + + // Check that initial outputs and final outputs + // using two models model are different. + BOOST_REQUIRE_LT(arma::accu(dictionary == + CLI::GetParam("dictionary")), dictionary.n_elem); + + BOOST_REQUIRE_LT(arma::accu(codes == + CLI::GetParam("codes")), codes.n_elem); +} + +/** + * Ensure that for different value of objective_tolerance + * outputs are different. + */ +BOOST_AUTO_TEST_CASE(SparseCodingDiffObjToleranceTest) +{ + arma::mat inputData; + arma::mat testData; + LoadData(inputData, testData); + + mat initialDictionary = inputData.cols(0, 1); + + // Train for default objective_tolerance. + + // Input data. + SetInputParam("training", inputData); + SetInputParam("atoms", (int) 2); + SetInputParam("initial_dictionary", initialDictionary); + SetInputParam("test", testData); + + mlpackMain(); + + // Store outputs. + arma::mat dictionary = CLI::GetParam("dictionary"); + arma::mat codes = + std::move(CLI::GetParam("codes")); + + // Train for objective_tolerance equals to 10000.0. + + // Input data. + bindings::tests::CleanMemory(); + SetInputParam("training", std::move(inputData)); + SetInputParam("atoms", (int) 2); + SetInputParam("initial_dictionary", std::move(initialDictionary)); + SetInputParam("objective_tolerance", (double) 10000.0); + SetInputParam("test", std::move(testData)); + + mlpackMain(); + + // Check that initial outputs and final outputs + // using two models model are different. + BOOST_REQUIRE_LT(arma::accu(dictionary == + CLI::GetParam("dictionary")), dictionary.n_elem); + + BOOST_REQUIRE_LT(arma::accu(codes == + CLI::GetParam("codes")), codes.n_elem); +} + +/** + * Ensure that for different value of newton_tolerance + * outputs are different. + */ +BOOST_AUTO_TEST_CASE(SparseCodingDiffNewtonToleranceTest) +{ + arma::mat inputData; + arma::mat testData; + LoadData(inputData, testData); + + mat initialDictionary = inputData.cols(0, 1); + + // Train for default newton_tolerance. + + // Input data. + SetInputParam("training", inputData); + SetInputParam("atoms", (int) 2); + SetInputParam("initial_dictionary", initialDictionary); + SetInputParam("test", testData); + + mlpackMain(); + + // Store outputs. + arma::mat dictionary = CLI::GetParam("dictionary"); + arma::mat codes = + std::move(CLI::GetParam("codes")); + + // Train for newton_tolerance equals to 10000.0. + + // Input data. + bindings::tests::CleanMemory(); + SetInputParam("training", std::move(inputData)); + SetInputParam("atoms", (int) 2); + SetInputParam("initial_dictionary", std::move(initialDictionary)); + SetInputParam("newton_tolerance", (double) 10000.0); + SetInputParam("test", std::move(testData)); + + mlpackMain(); + + // Check that initial outputs and final outputs + // using two models model are different. + BOOST_REQUIRE_LT(arma::accu(dictionary == + CLI::GetParam("dictionary")), dictionary.n_elem); + + BOOST_REQUIRE_LT(arma::accu(codes == + CLI::GetParam("codes")), codes.n_elem); +} + +/** + * Ensure that for different value of lambda1 + * outputs are different. + */ +BOOST_AUTO_TEST_CASE(SparseCodingDiffL1Test) +{ + arma::mat inputData; + arma::mat testData; + LoadData(inputData, testData); + + mat initialDictionary = inputData.cols(0, 1); + + // Train for default lambda1. + + // Input data. + SetInputParam("training", inputData); + SetInputParam("atoms", (int) 2); + SetInputParam("initial_dictionary", initialDictionary); + SetInputParam("test", testData); + + mlpackMain(); + + // Store outputs. + arma::mat dictionary = CLI::GetParam("dictionary"); + arma::mat codes = + std::move(CLI::GetParam("codes")); + + // Train for lambda1 equals to 10000.0. + + // Input data. + bindings::tests::CleanMemory(); + SetInputParam("training", std::move(inputData)); + SetInputParam("atoms", (int) 2); + SetInputParam("initial_dictionary", std::move(initialDictionary)); + SetInputParam("lambda1", (double) 10000.0); + SetInputParam("test", std::move(testData)); + + mlpackMain(); + + // Check that initial outputs and final outputs + // using two models model are different. + BOOST_REQUIRE_LT(arma::accu(dictionary == + CLI::GetParam("dictionary")), dictionary.n_elem); + + BOOST_REQUIRE_LT(arma::accu(codes == + CLI::GetParam("codes")), codes.n_elem); +} + +/** + * Ensure that for different value of lambda2 + * outputs are different. + */ +BOOST_AUTO_TEST_CASE(SparseCodingDiffL2Test) +{ + arma::mat inputData; + arma::mat testData; + LoadData(inputData, testData); + + mat initialDictionary = inputData.cols(0, 1); + + // Train for default lambda2. + + // Input data. + SetInputParam("training", inputData); + SetInputParam("atoms", (int) 2); + SetInputParam("initial_dictionary", initialDictionary); + SetInputParam("test", testData); + + mlpackMain(); + + // Store outputs. + arma::mat dictionary = CLI::GetParam("dictionary"); + arma::mat codes = + std::move(CLI::GetParam("codes")); + + // Train for lambda2 equals to 10000.0. + + // Input data. + bindings::tests::CleanMemory(); + SetInputParam("training", std::move(inputData)); + SetInputParam("atoms", (int) 2); + SetInputParam("initial_dictionary", std::move(initialDictionary)); + SetInputParam("lambda2", (double) 10000.0); + SetInputParam("test", std::move(testData)); + + mlpackMain(); + + // Check that initial outputs and final outputs + // using two models model are different. + BOOST_REQUIRE_LT(arma::accu(dictionary == + CLI::GetParam("dictionary")), dictionary.n_elem); + + BOOST_REQUIRE_LT(arma::accu(codes == + CLI::GetParam("codes")), codes.n_elem); +} + +/** + * Ensure that for different value of lambda1 & lambda2 + * outputs are different. + */ +BOOST_AUTO_TEST_CASE(SparseCodingDiffL1L2Test) +{ + arma::mat inputData; + arma::mat testData; + LoadData(inputData, testData); + + mat initialDictionary = inputData.cols(0, 1); + + // Train for default lambda2 & lambda1 equal to 10000.0. + + // Input data. + SetInputParam("training", inputData); + SetInputParam("atoms", (int) 2); + SetInputParam("lambda1", (double) 10000.0); + SetInputParam("initial_dictionary", initialDictionary); + SetInputParam("test", testData); + + mlpackMain(); + + // Store outputs. + arma::mat dictionary = CLI::GetParam("dictionary"); + arma::mat codes = + std::move(CLI::GetParam("codes")); + + // Train for lambda1 EQUALS 0.0 & lambda2 equals to 10000.0. + + // Input data. + bindings::tests::CleanMemory(); + SetInputParam("training", std::move(inputData)); + SetInputParam("atoms", (int) 2); + SetInputParam("initial_dictionary", std::move(initialDictionary)); + SetInputParam("lambda1", (double) 0.0); + SetInputParam("lambda2", (double) 10000.0); + SetInputParam("test", std::move(testData)); + + mlpackMain(); + + // Check that initial outputs and final outputs + // using two models model are different. + BOOST_REQUIRE_LT(arma::accu(dictionary == + CLI::GetParam("dictionary")), dictionary.n_elem); + + BOOST_REQUIRE_LT(arma::accu(codes == + CLI::GetParam("codes")), codes.n_elem); +} + +BOOST_AUTO_TEST_SUITE_END(); diff --git a/src/mlpack/tests/main_tests/test_helper.hpp b/src/mlpack/tests/main_tests/test_helper.hpp index 0043b6d242..12cb72c715 100644 --- a/src/mlpack/tests/main_tests/test_helper.hpp +++ b/src/mlpack/tests/main_tests/test_helper.hpp @@ -9,6 +9,10 @@ * 3-clause BSD license along with mlpack. If not, see * http://www.opensource.org/licenses/BSD-3-Clause for more information. */ +#ifndef MLPACK_TESTS_MAIN_TESTS_TEST_HELPER_HPP +#define MLPACK_TESTS_MAIN_TESTS_TEST_HELPER_HPP + +#include namespace mlpack { namespace util { @@ -31,3 +35,5 @@ void SetInputParam(const std::string& name, T&& value) } // namespace util } // namespace mlpack + +#endif diff --git a/src/mlpack/tests/momentum_sgd_test.cpp b/src/mlpack/tests/momentum_sgd_test.cpp index 649bc89d59..ba1932c3bb 100644 --- a/src/mlpack/tests/momentum_sgd_test.cpp +++ b/src/mlpack/tests/momentum_sgd_test.cpp @@ -13,8 +13,8 @@ #include #include #include -#include -#include +#include +#include #include #include "test_tools.hpp" diff --git a/src/mlpack/tests/parallel_sgd_test.cpp b/src/mlpack/tests/parallel_sgd_test.cpp index cc84ceea18..8e78551d17 100644 --- a/src/mlpack/tests/parallel_sgd_test.cpp +++ b/src/mlpack/tests/parallel_sgd_test.cpp @@ -13,7 +13,8 @@ #include #include #include -#include +#include + // We need some thorough testing #define private public #include diff --git a/src/mlpack/tests/rmsprop_test.cpp b/src/mlpack/tests/rmsprop_test.cpp index 0fb51c24a0..7135d06c28 100644 --- a/src/mlpack/tests/rmsprop_test.cpp +++ b/src/mlpack/tests/rmsprop_test.cpp @@ -12,7 +12,7 @@ #include #include -#include +#include #include diff --git a/src/mlpack/tests/sa_test.cpp b/src/mlpack/tests/sa_test.cpp index 5ada51d436..9f964166c0 100644 --- a/src/mlpack/tests/sa_test.cpp +++ b/src/mlpack/tests/sa_test.cpp @@ -12,7 +12,9 @@ #include #include #include -#include +#include +#include +#include #include #include @@ -30,6 +32,7 @@ using namespace mlpack::metric; BOOST_AUTO_TEST_SUITE(SATest); +// The Generalized-Rosenbrock function is a simple function to optimize. BOOST_AUTO_TEST_CASE(GeneralizedRosenbrockTest) { size_t dim = 10; @@ -74,36 +77,9 @@ BOOST_AUTO_TEST_CASE(RosenbrockTest) } /** - * The Rastrigrin function, a (not very) simple nonconvex function. It is - * defined by - * - * f(x) = 10n + \sum_{i = 1}^{n} (x_i^2 - 10 cos(2 \pi x_i)). - * - * It has very many local minima, so finding the true global minimum is - * difficult. The function is two-dimensional, and has minimum 0 where - * x = [0 0]. We are only using it for simulated annealing, so there is no need - * to implement the gradient. + * The Rastrigrin function, a (not very) simple nonconvex function. It has very + * many local minima, so finding the true global minimum is difficult. */ -class RastrigrinFunction -{ - public: - double Evaluate(const arma::mat& coordinates) const - { - double objective = 20; // 10 * n, n = 2. - objective += std::pow(coordinates[0], 2.0) - - 10 * std::cos(2 * M_PI * coordinates[0]); - objective += std::pow(coordinates[1], 2.0) - - 10 * std::cos(2 * M_PI * coordinates[1]); - - return objective; - } - - arma::mat GetInitialPoint() const - { - return arma::mat("-3 -3"); - } -}; - BOOST_AUTO_TEST_CASE(RastrigrinFunctionTest) { // Simulated annealing isn't guaranteed to converge (except in very specific @@ -113,9 +89,10 @@ BOOST_AUTO_TEST_CASE(RastrigrinFunctionTest) for (size_t trial = 0; trial < 4; ++trial) { - RastrigrinFunction f; + RastriginFunction f(2); ExponentialSchedule schedule; // The convergence is very sensitive to the choices of maxMove and initMove. + // SA<> sa(schedule, 2000000, 100, 50, 1000, 1e-12, 2, 2.0, 0.5, 0.1); SA<> sa(schedule, 2000000, 100, 50, 1000, 1e-12, 2, 2.0, 0.5, 0.1); arma::mat coordinates = f.GetInitialPoint(); diff --git a/src/mlpack/tests/sgd_test.cpp b/src/mlpack/tests/sgd_test.cpp index 77224adc78..b4015daef0 100644 --- a/src/mlpack/tests/sgd_test.cpp +++ b/src/mlpack/tests/sgd_test.cpp @@ -11,8 +11,8 @@ */ #include #include -#include -#include +#include +#include #include #include "test_tools.hpp" diff --git a/src/mlpack/tests/smorms3_test.cpp b/src/mlpack/tests/smorms3_test.cpp index 30d307f952..de5c1f569c 100644 --- a/src/mlpack/tests/smorms3_test.cpp +++ b/src/mlpack/tests/smorms3_test.cpp @@ -12,7 +12,7 @@ #include #include -#include +#include #include #include