Merge branch 'master' into markdown-bindings
This commit is contained in:
+39
-24
@@ -1,33 +1,48 @@
|
||||
sudo: required
|
||||
dist: trusty
|
||||
language: cpp
|
||||
|
||||
env:
|
||||
- CMAKE_OPTIONS="-DDEBUG=OFF -DPROFILE=OFF -DPYTHON=/usr/bin/python"
|
||||
- CMAKE_OPTIONS="-DDEBUG=OFF -DPROFILE=OFF -DPYTHON=/usr/bin/python3"
|
||||
- CMAKE_OPTIONS="-DDEBUG=ON -DPROFILE=OFF -DBUILD_PYTHON_BINDINGS=OFF"
|
||||
matrix:
|
||||
include:
|
||||
- os: linux
|
||||
dist: xenial
|
||||
env: CMAKE_OPTIONS="-DDEBUG=OFF -DPROFILE=OFF -DPYTHON=/usr/bin/python"
|
||||
before_install:
|
||||
- sudo apt-get update
|
||||
- sudo apt-get install -y --allow-unauthenticated libopenblas-dev liblapack-dev g++ libboost-all-dev python-pip cython python-numpy python-pandas
|
||||
- sudo pip install --upgrade --ignore-installed setuptools
|
||||
- curl https://ftp.fau.de/macports/distfiles/armadillo/armadillo-6.500.5.tar.gz | tar xvz && cd armadillo*
|
||||
- cmake . && make && sudo make install && cd ..
|
||||
- sudo cp .travis/config.hpp /usr/include/armadillo_bits/config.hpp
|
||||
|
||||
before_install:
|
||||
# For the python bindings we need cython >= 0.24.
|
||||
- sudo add-apt-repository -y ppa:imcode/s3ql-trusty-backport
|
||||
# For the python bindings we need pandas >= 0.15.0.
|
||||
- wget -O- http://neuro.debian.net/lists/trusty.us-ca.full | sudo tee /etc/apt/sources.list.d/neurodebian.sources.list
|
||||
- sudo apt-key adv --recv-keys --keyserver hkp://ha.pool.sks-keyservers.net 0xA5D32F012649A5A9 ||
|
||||
sudo apt-key adv --recv-keys --keyserver hkp://pgp.mit.edu 0xA5D32F012649A5A9 ||
|
||||
sudo apt-key adv --recv-keys --keyserver hkp://keyserver.pgp.com 0xA5D32F012649A5A9
|
||||
- sudo apt-get update
|
||||
- sudo apt-get install -y --allow-unauthenticated libopenblas-dev liblapack-dev g++ libboost-all-dev python3-pip cython3 python3-numpy python3-pandas
|
||||
# Install both python2 and python3 modules, and the build will decide which to
|
||||
# use.
|
||||
- sudo pip install cython numpy pandas
|
||||
- sudo pip install --upgrade --ignore-installed setuptools
|
||||
- sudo pip3 install --upgrade --ignore-installed setuptools
|
||||
- curl https://ftp.fau.de/macports/distfiles/armadillo/armadillo-6.500.5.tar.gz | tar xvz && cd armadillo*
|
||||
- cmake . && make && sudo make install && cd ..
|
||||
- sudo cp .travis/config.hpp /usr/include/armadillo_bits/config.hpp
|
||||
- os: linux
|
||||
dist: xenial
|
||||
env: CMAKE_OPTIONS="-DDEBUG=OFF -DPROFILE=OFF -DPYTHON=/usr/bin/python3"
|
||||
before_install:
|
||||
- sudo apt-get update
|
||||
- sudo apt-get install -y --allow-unauthenticated libopenblas-dev liblapack-dev g++ libboost-all-dev python3-pip cython3 python3-numpy python3-pandas
|
||||
- sudo pip3 install --upgrade --ignore-installed setuptools
|
||||
- curl https://ftp.fau.de/macports/distfiles/armadillo/armadillo-6.500.5.tar.gz | tar xvz && cd armadillo*
|
||||
- cmake . && make && sudo make install && cd ..
|
||||
- sudo cp .travis/config.hpp /usr/include/armadillo_bits/config.hpp
|
||||
|
||||
- os: linux
|
||||
dist: xenial
|
||||
env: CMAKE_OPTIONS="-DDEBUG=ON -DPROFILE=OFF -DBUILD_PYTHON_BINDINGS=OFF"
|
||||
before_install:
|
||||
- sudo apt-get update
|
||||
- sudo apt-get install -y --allow-unauthenticated libopenblas-dev liblapack-dev g++ libboost-all-dev
|
||||
- curl https://ftp.fau.de/macports/distfiles/armadillo/armadillo-6.500.5.tar.gz | tar xvz && cd armadillo*
|
||||
- cmake . && make && sudo make install && cd ..
|
||||
- sudo cp .travis/config.hpp /usr/include/armadillo_bits/config.hpp
|
||||
|
||||
- os: osx
|
||||
osx_image: xcode9.4 # Maybe we can try some different ones?
|
||||
env: CMAKE_OPTIONS="-DDEBUG=ON -DPROFILE=OFF -DBUILD_PYTHON_BINDINGS=OFF"
|
||||
before_install:
|
||||
- brew install openblas armadillo || brew install openblas armadillo
|
||||
|
||||
install:
|
||||
- mkdir build && cd build && cmake $CMAKE_OPTIONS .. && make -j2
|
||||
- mkdir build && cd build && cmake $CMAKE_OPTIONS .. && travis_wait 60 make -j2
|
||||
|
||||
script:
|
||||
- CTEST_OUTPUT_ON_FAILURE=1 travis_wait 30 ctest -j2
|
||||
|
||||
@@ -3,16 +3,18 @@
|
||||
#
|
||||
# This module sets the following variables:
|
||||
# ENSMALLEN_FOUND - set to true if the library is found
|
||||
# ENSMALLEN_INCLUDE_DIRS - list of required include directories
|
||||
# ENSMALLEN_INCLUDE_DIR - list of required include directories
|
||||
# ENSMALLEN_VERSION_MAJOR - major version number
|
||||
# ENSMALLEN_VERSION_MINOR - minor version number
|
||||
# ENSMALLEN_VERSION_PATCH - patch version number
|
||||
# ENSMALLEN_VERSION_STRING - version number as a string (ex: "1.0.4")
|
||||
# ENSMALLEN_VERSION_NAME - name of the version (ex: "Antipodean Antileech")
|
||||
|
||||
file(GLOB ENSMALLEN_SEARCH_PATHS
|
||||
${CMAKE_BINARY_DIR}/deps/ensmallen-[0-9]*.[0-9]*.[0-9]*)
|
||||
find_path(ENSMALLEN_INCLUDE_DIR
|
||||
NAMES ensmallen.hpp
|
||||
PATHS "$ENV{ProgramFiles}/ensmallen/include")
|
||||
PATHS ${ENSMALLEN_SEARCH_PATHS}/include)
|
||||
|
||||
if(ENSMALLEN_INCLUDE_DIR)
|
||||
# ------------------------------------------------------------------------
|
||||
@@ -26,15 +28,17 @@ if(ENSMALLEN_INCLUDE_DIR)
|
||||
|
||||
if(EXISTS "${ENSMALLEN_INCLUDE_DIR}/ensmallen_bits/ens_version.hpp")
|
||||
|
||||
set(ENSMALLEN_FOUND YES)
|
||||
|
||||
# Read and parse armdillo version header file for version number
|
||||
file(READ "${ENSMALLEN_INCLUDE_DIR}/ensmallen_bits/ens_version.hpp"
|
||||
_ensmallen_HEADER_CONTENTS)
|
||||
string(REGEX REPLACE ".*#define ENS_VERSION_MAJOR ([0-9]+).*" "\\1"
|
||||
ENSMALLEN_VERSION_MAJOR "${_armadillo_HEADER_CONTENTS}")
|
||||
ENSMALLEN_VERSION_MAJOR "${_ensmallen_HEADER_CONTENTS}")
|
||||
string(REGEX REPLACE ".*#define ENS_VERSION_MINOR ([0-9]+).*" "\\1"
|
||||
ENSMALLEN_VERSION_MINOR "${_armadillo_HEADER_CONTENTS}")
|
||||
ENSMALLEN_VERSION_MINOR "${_ensmallen_HEADER_CONTENTS}")
|
||||
string(REGEX REPLACE ".*#define ENS_VERSION_PATCH ([0-9]+).*" "\\1"
|
||||
ENSMALLEN_VERSION_PATCH "${_armadillo_HEADER_CONTENTS}")
|
||||
ENSMALLEN_VERSION_PATCH "${_ensmallen_HEADER_CONTENTS}")
|
||||
|
||||
# WARNING: The number of spaces before the version name is not one.
|
||||
string(REGEX REPLACE
|
||||
|
||||
+1
-1
@@ -350,7 +350,7 @@ ${ENS_DOWNLOAD_ERROR}! Error log: ${ENS_DOWBLOAD_LOG}")
|
||||
endif ()
|
||||
endif ()
|
||||
else ()
|
||||
set(MLPACK_INCLUDE_DIRS ${MLPACK_INCLUDE_DIRS} ${ENSMALLEN_INCLUDE_DIRS})
|
||||
set(MLPACK_INCLUDE_DIRS ${MLPACK_INCLUDE_DIRS} "${ENSMALLEN_INCLUDE_DIR}")
|
||||
endif ()
|
||||
|
||||
# Unfortunately this configuration variable is necessary and will need to be
|
||||
|
||||
+9
-7
@@ -11,7 +11,7 @@ interested in participating in Google Summer of Code, see
|
||||
|
||||
## Pull request process
|
||||
|
||||
Once a pull request is submitted, it must be reviewed by at least one member of
|
||||
Once a pull request is submitted, it must be approved by at least one member of
|
||||
mlpack's Contributors team, to ensure that (if applicable):
|
||||
|
||||
* the design meshes with the rest of mlpack
|
||||
@@ -19,9 +19,11 @@ mlpack's Contributors team, to ensure that (if applicable):
|
||||
[Style Guide](http://github.com/mlpack/mlpack/wiki/DesignGuidelines)
|
||||
* any new functionality is tested and working
|
||||
|
||||
Once the pull request is approved by one member of the Contributors team, it may
|
||||
be merged between 3 and 7 days after approval. This allows other contributors
|
||||
and maintainers to have time to also review the PR. If a pull request has at
|
||||
least two approvals from members of the Contributors team, then the pull
|
||||
request may be immediately merged. This applies even if the submitter of the
|
||||
PR is a member of the Contributors team.
|
||||
The pull request can be merged as soon as it receives two approvals; 24 hours
|
||||
after the first approval, mlpack-bot will provide a second approval. This is to
|
||||
leave time for anyone to comment on the PR before it is merged.
|
||||
|
||||
Members of the Contributors team are encouraged to review pull requests that
|
||||
have already been reviewed, and pull request contributors are encouraged to seek
|
||||
multiple reviews. Reviews from anyone not on the Contributors team are always
|
||||
appreciated and encouraged!
|
||||
|
||||
+2
-1
@@ -93,7 +93,7 @@ Copyright:
|
||||
Copyright 2018, B Kartheek Reddy <bkartheekreddy@gmail.com>
|
||||
Copyright 2018, Atharva Khandait <akhandait45@gmail.com>
|
||||
Copyright 2018, Wenhao Huang <wenhao.huang.work@gmail.com>
|
||||
Copyright 2018, Roberto Hueso <robertohueso96@gmail.com>
|
||||
Copyright 2018-2019, Roberto Hueso <robertohueso96@gmail.com>
|
||||
Copyright 2018, Prabhat Sharma <prabhatsharma7298@gmail.com>
|
||||
Copyright 2018, Tan Jun An <yamidarkxxx@gmail.com>
|
||||
Copyright 2018, Moksh Jain <mokshjn00@gmail.com>
|
||||
@@ -108,6 +108,7 @@ Copyright:
|
||||
Copyright 2018, Ayush Chamoli
|
||||
Copyright 2018, Tommi Laivamaa <tommi.laivamaa@protonmail.com>
|
||||
Copyright 2019, Kim SangYeon <sy0814k@gmail.com>
|
||||
Copyright 2019, Niteya Shah <niteya.56@gmail.com>
|
||||
|
||||
License: BSD-3-clause
|
||||
All rights reserved.
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
### mlpack 3.1.0
|
||||
###### ????-??-??
|
||||
* Add kernel density estimation (KDE) implementation with bindings to other
|
||||
languages (#1301).
|
||||
|
||||
### mlpack 3.0.5
|
||||
###### ????-??-??
|
||||
* Change DBSCAN to use PointSelectionPolicy and add OrderedPointSelection (#1625).
|
||||
|
||||
@@ -254,6 +254,7 @@
|
||||
* - Ayush Chamoli
|
||||
* - Tommi Laivamaa <tommi.laivamaa@protonmail.com>
|
||||
* - Kim SangYeon <sy0814k@gmail.com>
|
||||
* - Niteya Shah <niteya.56@gmail.com>
|
||||
*/
|
||||
|
||||
// First, include all of the prerequisites.
|
||||
|
||||
@@ -38,50 +38,142 @@ template<typename MatType,
|
||||
bool NumClasses>
|
||||
struct TrainForm;
|
||||
|
||||
template<typename PT, typename WT, typename... SignatureParams>
|
||||
struct TrainFormBase
|
||||
{
|
||||
using PredictionsType = PT;
|
||||
using WeightsType = WT;
|
||||
#if _MSC_VER <= 1916 // Visual Studio 2017 version 15.9 or older.
|
||||
// Due to an internal MSVC compiler bug (MSVC ) we can't use two parameter
|
||||
// packs. So we have to write multiple TrainFormBase forms.
|
||||
template<typename PT, typename WT, typename T1, typename T2>
|
||||
struct TrainFormBase4
|
||||
{
|
||||
using PredictionsType = PT;
|
||||
using WeightsType = WT;
|
||||
|
||||
/* A minimum number of parameters that should be inferred */
|
||||
static const size_t MinNumberOfAdditionalArgs = 1;
|
||||
/* A minimum number of parameters that should be inferred */
|
||||
static const size_t MinNumberOfAdditionalArgs = 1;
|
||||
|
||||
template<typename Class, typename RT, typename... Ts>
|
||||
using Type = RT(Class::*)(SignatureParams..., Ts...);
|
||||
};
|
||||
template<typename Class, typename RT, typename... Ts>
|
||||
using Type = RT(Class::*)(T1, T2, Ts...);
|
||||
};
|
||||
|
||||
template<typename MT, typename PT>
|
||||
struct TrainForm<MT, PT, void, false, false> : public TrainFormBase<PT, void,
|
||||
const MT&, const PT&> {};
|
||||
template<typename PT, typename WT, typename T1, typename T2, typename T3>
|
||||
struct TrainFormBase5
|
||||
{
|
||||
using PredictionsType = PT;
|
||||
using WeightsType = WT;
|
||||
|
||||
template<typename MT, typename PT>
|
||||
struct TrainForm<MT, PT, void, true, false> : public TrainFormBase<PT, void,
|
||||
const MT&, const data::DatasetInfo&, const PT&> {};
|
||||
/* A minimum number of parameters that should be inferred */
|
||||
static const size_t MinNumberOfAdditionalArgs = 1;
|
||||
|
||||
template<typename MT, typename PT, typename WT>
|
||||
struct TrainForm<MT, PT, WT, false, false> : public TrainFormBase<PT, WT,
|
||||
const MT&, const PT&, const WT&> {};
|
||||
template<typename Class, typename RT, typename... Ts>
|
||||
using Type = RT(Class::*)(T1, T2, T3, Ts...);
|
||||
};
|
||||
|
||||
template<typename MT, typename PT, typename WT>
|
||||
struct TrainForm<MT, PT, WT, true, false> : public TrainFormBase<PT, WT,
|
||||
const MT&, const data::DatasetInfo&, const PT&, const WT&> {};
|
||||
template<typename PT, typename WT, typename T1, typename T2, typename T3,
|
||||
typename T4>
|
||||
struct TrainFormBase6
|
||||
{
|
||||
using PredictionsType = PT;
|
||||
using WeightsType = WT;
|
||||
|
||||
template<typename MT, typename PT>
|
||||
struct TrainForm<MT, PT, void, false, true> : public TrainFormBase<PT, void,
|
||||
const MT&, const PT&, const size_t> {};
|
||||
/* A minimum number of parameters that should be inferred */
|
||||
static const size_t MinNumberOfAdditionalArgs = 1;
|
||||
|
||||
template<typename MT, typename PT>
|
||||
struct TrainForm<MT, PT, void, true, true> : public TrainFormBase<PT, void,
|
||||
const MT&, const data::DatasetInfo&, const PT&, const size_t> {};
|
||||
template<typename Class, typename RT, typename... Ts>
|
||||
using Type = RT(Class::*)(T1, T2, T3, T4, Ts...);
|
||||
};
|
||||
|
||||
template<typename MT, typename PT, typename WT>
|
||||
struct TrainForm<MT, PT, WT, false, true> : public TrainFormBase<PT, WT,
|
||||
const MT&, const PT&, const size_t, const WT&> {};
|
||||
template<typename PT, typename WT, typename T1, typename T2, typename T3,
|
||||
typename T4, typename T5>
|
||||
struct TrainFormBase7
|
||||
{
|
||||
using PredictionsType = PT;
|
||||
using WeightsType = WT;
|
||||
|
||||
template<typename MT, typename PT, typename WT>
|
||||
struct TrainForm<MT, PT, WT, true, true> : public TrainFormBase<PT, WT,
|
||||
const MT&, const data::DatasetInfo&, const PT&, const size_t, const WT&> {};
|
||||
/* A minimum number of parameters that should be inferred */
|
||||
static const size_t MinNumberOfAdditionalArgs = 1;
|
||||
|
||||
template<typename Class, typename RT, typename... Ts>
|
||||
using Type = RT(Class::*)(T1, T2, T3, T4, T5, Ts...);
|
||||
};
|
||||
|
||||
template<typename MT, typename PT>
|
||||
struct TrainForm<MT, PT, void, false, false> : public TrainFormBase4<PT, void,
|
||||
const MT&, const PT&> {};
|
||||
|
||||
template<typename MT, typename PT>
|
||||
struct TrainForm<MT, PT, void, true, false> : public TrainFormBase5<PT, void,
|
||||
const MT&, const data::DatasetInfo&, const PT&> {};
|
||||
|
||||
template<typename MT, typename PT, typename WT>
|
||||
struct TrainForm<MT, PT, WT, false, false> : public TrainFormBase5<PT, WT,
|
||||
const MT&, const PT&, const WT&> {};
|
||||
|
||||
template<typename MT, typename PT, typename WT>
|
||||
struct TrainForm<MT, PT, WT, true, false> : public TrainFormBase6<PT, WT,
|
||||
const MT&, const data::DatasetInfo&, const PT&, const WT&> {};
|
||||
|
||||
template<typename MT, typename PT>
|
||||
struct TrainForm<MT, PT, void, false, true> : public TrainFormBase5<PT, void,
|
||||
const MT&, const PT&, const size_t> {};
|
||||
|
||||
template<typename MT, typename PT>
|
||||
struct TrainForm<MT, PT, void, true, true> : public TrainFormBase6<PT, void,
|
||||
const MT&, const data::DatasetInfo&, const PT&, const size_t> {};
|
||||
|
||||
template<typename MT, typename PT, typename WT>
|
||||
struct TrainForm<MT, PT, WT, false, true> : public TrainFormBase6<PT, WT,
|
||||
const MT&, const PT&, const size_t, const WT&> {};
|
||||
|
||||
template<typename MT, typename PT, typename WT>
|
||||
struct TrainForm<MT, PT, WT, true, true> : public TrainFormBase7<PT, WT,
|
||||
const MT&, const data::DatasetInfo&, const PT&,
|
||||
const size_t, const WT&> {};
|
||||
#else
|
||||
template<typename PT, typename WT, typename... SignatureParams>
|
||||
struct TrainFormBase
|
||||
{
|
||||
using PredictionsType = PT;
|
||||
using WeightsType = WT;
|
||||
|
||||
/* A minimum number of parameters that should be inferred */
|
||||
static const size_t MinNumberOfAdditionalArgs = 1;
|
||||
|
||||
template<typename Class, typename RT, typename... Ts>
|
||||
using Type = RT(Class::*)(SignatureParams..., Ts...);
|
||||
};
|
||||
|
||||
template<typename MT, typename PT>
|
||||
struct TrainForm<MT, PT, void, false, false> : public TrainFormBase<PT, void,
|
||||
const MT&, const PT&> {};
|
||||
|
||||
template<typename MT, typename PT>
|
||||
struct TrainForm<MT, PT, void, true, false> : public TrainFormBase<PT, void,
|
||||
const MT&, const data::DatasetInfo&, const PT&> {};
|
||||
|
||||
template<typename MT, typename PT, typename WT>
|
||||
struct TrainForm<MT, PT, WT, false, false> : public TrainFormBase<PT, WT,
|
||||
const MT&, const PT&, const WT&> {};
|
||||
|
||||
template<typename MT, typename PT, typename WT>
|
||||
struct TrainForm<MT, PT, WT, true, false> : public TrainFormBase<PT, WT,
|
||||
const MT&, const data::DatasetInfo&, const PT&, const WT&> {};
|
||||
|
||||
template<typename MT, typename PT>
|
||||
struct TrainForm<MT, PT, void, false, true> : public TrainFormBase<PT, void,
|
||||
const MT&, const PT&, const size_t> {};
|
||||
|
||||
template<typename MT, typename PT>
|
||||
struct TrainForm<MT, PT, void, true, true> : public TrainFormBase<PT, void,
|
||||
const MT&, const data::DatasetInfo&, const PT&, const size_t> {};
|
||||
|
||||
template<typename MT, typename PT, typename WT>
|
||||
struct TrainForm<MT, PT, WT, false, true> : public TrainFormBase<PT, WT,
|
||||
const MT&, const PT&, const size_t, const WT&> {};
|
||||
|
||||
template<typename MT, typename PT, typename WT>
|
||||
struct TrainForm<MT, PT, WT, true, true> : public TrainFormBase<PT, WT,
|
||||
const MT&, const data::DatasetInfo&, const PT&,
|
||||
const size_t, const WT&> {};
|
||||
#endif
|
||||
|
||||
/* A struct for indication that a right method form can't be found */
|
||||
struct NotFoundMethodForm
|
||||
|
||||
@@ -55,6 +55,14 @@ class DatasetMapper
|
||||
*/
|
||||
explicit DatasetMapper(PolicyType& policy, const size_t dimensionality = 0);
|
||||
|
||||
/**
|
||||
* Set the dimensionality of an existing DatasetMapper object. This resets
|
||||
* all mappings (but not the PolicyType).
|
||||
*
|
||||
* @param dimensionality New dimensionality.
|
||||
*/
|
||||
void SetDimensionality(const size_t dimensionality);
|
||||
|
||||
/**
|
||||
* Preprocessing: during a first pass of the data, pass the input on to the
|
||||
* MapPolicy if they are needed.
|
||||
|
||||
@@ -37,6 +37,14 @@ inline DatasetMapper<PolicyType, InputType>::DatasetMapper(PolicyType& policy,
|
||||
// Nothing to initialize here.
|
||||
}
|
||||
|
||||
template<typename PolicyType, typename InputType>
|
||||
inline void DatasetMapper<PolicyType, InputType>::SetDimensionality(
|
||||
const size_t dimensionality)
|
||||
{
|
||||
types = std::vector<Datatype>(dimensionality, Datatype::numeric);
|
||||
maps.clear();
|
||||
}
|
||||
|
||||
// Utility helper function to call MapFirstPass.
|
||||
template<typename PolicyType, typename InputType, typename T>
|
||||
void CallMapFirstPass(
|
||||
|
||||
@@ -180,7 +180,7 @@ class LoadCSV
|
||||
stringRule[findRowSize] % delimiterRule);
|
||||
|
||||
// Now that we know the dimensionality, initialize the DatasetMapper.
|
||||
info = DatasetMapper<MapPolicy>(rows);
|
||||
info.SetDimensionality(rows);
|
||||
}
|
||||
|
||||
// If we need to do a first pass for the DatasetMapper, do it.
|
||||
|
||||
@@ -30,6 +30,9 @@
|
||||
|
||||
#if (BINDING_TYPE == BINDING_TYPE_CLI) // This is a command-line executable.
|
||||
|
||||
// Matrices are transposed on load/save.
|
||||
#define BINDING_MATRIX_TRANSPOSED true
|
||||
|
||||
#include <mlpack/bindings/cli/cli_option.hpp>
|
||||
#include <mlpack/bindings/cli/print_doc_functions.hpp>
|
||||
|
||||
@@ -75,6 +78,9 @@ int main(int argc, char** argv)
|
||||
|
||||
#elif(BINDING_TYPE == BINDING_TYPE_TEST) // This is a unit test.
|
||||
|
||||
// Matrices are not transposed on load/save.
|
||||
#define BINDING_MATRIX_TRANSPOSED false
|
||||
|
||||
#include <mlpack/bindings/tests/test_option.hpp>
|
||||
#include <mlpack/bindings/tests/ignore_check.hpp>
|
||||
#include <mlpack/bindings/tests/clean_memory.hpp>
|
||||
@@ -106,6 +112,9 @@ using Option = mlpack::bindings::tests::TestOption<T>;
|
||||
|
||||
#elif(BINDING_TYPE == BINDING_TYPE_PYX) // This is a Python binding.
|
||||
|
||||
// Matrices are transposed on load/save.
|
||||
#define BINDING_MATRIX_TRANSPOSED true
|
||||
|
||||
#include <mlpack/bindings/python/py_option.hpp>
|
||||
#include <mlpack/bindings/python/print_doc_functions.hpp>
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ set(DIRS
|
||||
gmm
|
||||
hmm
|
||||
hoeffding_trees
|
||||
kde
|
||||
kernel_pca
|
||||
kmeans
|
||||
lars
|
||||
|
||||
@@ -42,7 +42,7 @@ namespace ann /** Artificial Neural Network. */ {
|
||||
* f'(x) &=& \left\{
|
||||
* \begin{array}{lr}
|
||||
* 1 & : x > 0 \\
|
||||
* y + \alpha & : x \le 0
|
||||
* f(x) + \alpha & : x \le 0
|
||||
* \end{array}
|
||||
* \right.
|
||||
* @f}
|
||||
@@ -73,7 +73,7 @@ namespace ann /** Artificial Neural Network. */ {
|
||||
* f'(x) &=& \left\{
|
||||
* \begin{array}{lr}
|
||||
* \lambda & : x > 0 \\
|
||||
* \lambda * (y + \alpha) & : x \le 0
|
||||
* f(x) + \lambda * \alpha & : x \le 0
|
||||
* \end{array}
|
||||
* \right.
|
||||
* @f}
|
||||
@@ -90,6 +90,10 @@ namespace ann /** Artificial Neural Network. */ {
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* In the deterministic mode, there is no computation of the derivative.
|
||||
*
|
||||
* @note During training deterministic should be set to false and during
|
||||
* testing/inference deterministic should be set to true.
|
||||
* @note Make sure to use SELU activation function with normalized inputs and
|
||||
* weights initialized with Lecun Normal Initialization.
|
||||
*
|
||||
@@ -137,7 +141,7 @@ class ELU
|
||||
* f(x) by propagating x backwards through f. Using the results from the feed
|
||||
* forward pass.
|
||||
*
|
||||
* @param input The propagated input activation.
|
||||
* @param input The propagated input activation f(x).
|
||||
* @param gy The backpropagated error.
|
||||
* @param g The calculated gradient.
|
||||
*/
|
||||
@@ -206,27 +210,29 @@ class ELU
|
||||
* Computes the first derivative of the activation function.
|
||||
*
|
||||
* @param x Input data.
|
||||
* @param y Propagated data f(x).
|
||||
* @return f'(x)
|
||||
*/
|
||||
double Deriv(const double y)
|
||||
double Deriv(const double x, const double y)
|
||||
{
|
||||
return (y > 0) ? lambda : lambda * (y + alpha);
|
||||
return (x > 0) ? lambda : y + lambda * alpha;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the first derivative of the activation function.
|
||||
*
|
||||
* @param y Input activations.
|
||||
* @param x The resulting derivatives.
|
||||
* @param x Input data.
|
||||
* @param y Output activations f(x).
|
||||
* @param z The resulting derivatives.
|
||||
*/
|
||||
template<typename InputType, typename OutputType>
|
||||
void Deriv(const InputType& x, OutputType& y)
|
||||
{
|
||||
y = x;
|
||||
derivative.set_size(arma::size(x));
|
||||
|
||||
for (size_t i = 0; i < x.n_elem; i++)
|
||||
{
|
||||
y(i) = Deriv(x(i));
|
||||
derivative(i) = Deriv(x(i), y(i));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,6 +242,9 @@ class ELU
|
||||
//! Locally-stored output parameter object.
|
||||
OutputDataType outputParameter;
|
||||
|
||||
//! Locally stored first derivative of the activation function.
|
||||
arma::mat derivative;
|
||||
|
||||
//! ELU Hyperparameter (0 < alpha)
|
||||
//! SELU parameter fixed to 1.6732632423543774 for normalized inputs.
|
||||
double alpha;
|
||||
@@ -245,6 +254,9 @@ class ELU
|
||||
//! For SELU activation function, lambda = 1.0507009873554802 for normalized
|
||||
//! inputs.
|
||||
double lambda;
|
||||
|
||||
//! If true the derivative computation is disabled, see notes above.
|
||||
bool deterministic;
|
||||
}; // class ELU
|
||||
|
||||
// Template alias for SELU using ELU class.
|
||||
|
||||
@@ -29,7 +29,8 @@ namespace ann /** Artificial Neural Network. */ {
|
||||
template<typename InputDataType, typename OutputDataType>
|
||||
ELU<InputDataType, OutputDataType>::ELU() :
|
||||
alpha(1.6732632423543774),
|
||||
lambda(1.0507009873554802)
|
||||
lambda(1.0507009873554802),
|
||||
deterministic(false)
|
||||
{
|
||||
// Nothing to do here.
|
||||
}
|
||||
@@ -38,7 +39,9 @@ ELU<InputDataType, OutputDataType>::ELU() :
|
||||
// is fixed and equal to 1. 'alpha' is a hyperparameter.
|
||||
template<typename InputDataType, typename OutputDataType>
|
||||
ELU<InputDataType, OutputDataType>::ELU(const double alpha) :
|
||||
alpha(alpha), lambda(1)
|
||||
alpha(alpha),
|
||||
lambda(1),
|
||||
deterministic(false)
|
||||
{
|
||||
// Nothing to do here.
|
||||
}
|
||||
@@ -49,15 +52,18 @@ void ELU<InputDataType, OutputDataType>::Forward(
|
||||
const InputType&& input, OutputType&& output)
|
||||
{
|
||||
Fn(input, output);
|
||||
|
||||
if (!deterministic)
|
||||
{
|
||||
Deriv(input, output);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename InputDataType, typename OutputDataType>
|
||||
template<typename DataType>
|
||||
void ELU<InputDataType, OutputDataType>::Backward(
|
||||
const DataType&& input, DataType&& gy, DataType&& g)
|
||||
const DataType&& /* input */, DataType&& gy, DataType&& g)
|
||||
{
|
||||
DataType derivative;
|
||||
Deriv(input, derivative);
|
||||
g = gy % derivative;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
#include "../visitor/delete_visitor.hpp"
|
||||
#include "../visitor/delta_visitor.hpp"
|
||||
#include "../visitor/output_parameter_visitor.hpp"
|
||||
#include "../visitor/weight_size_visitor.hpp"
|
||||
|
||||
#include "layer_types.hpp"
|
||||
#include "add_merge.hpp"
|
||||
@@ -192,9 +191,6 @@ class Recurrent
|
||||
//! Locally-stored merge module.
|
||||
LayerTypes<CustomLayers...> mergeModule;
|
||||
|
||||
//! Locally-stored weight size visitor.
|
||||
WeightSizeVisitor weightSizeVisitor;
|
||||
|
||||
//! Locally-stored delta visitor.
|
||||
DeltaVisitor deltaVisitor;
|
||||
|
||||
|
||||
@@ -86,11 +86,6 @@ Recurrent<InputDataType, OutputDataType, CustomLayers...>::Recurrent(
|
||||
boost::apply_visitor(AddVisitor<CustomLayers...>(transferModule),
|
||||
initialModule);
|
||||
|
||||
boost::apply_visitor(weightSizeVisitor, startModule);
|
||||
boost::apply_visitor(weightSizeVisitor, inputModule);
|
||||
boost::apply_visitor(weightSizeVisitor, feedbackModule);
|
||||
boost::apply_visitor(weightSizeVisitor, transferModule);
|
||||
|
||||
boost::apply_visitor(AddVisitor<CustomLayers...>(inputModule), mergeModule);
|
||||
boost::apply_visitor(AddVisitor<CustomLayers...>(feedbackModule),
|
||||
mergeModule);
|
||||
@@ -271,11 +266,6 @@ void Recurrent<InputDataType, OutputDataType, CustomLayers...>::serialize(
|
||||
boost::apply_visitor(AddVisitor<CustomLayers...>(transferModule),
|
||||
initialModule);
|
||||
|
||||
boost::apply_visitor(weightSizeVisitor, startModule);
|
||||
boost::apply_visitor(weightSizeVisitor, inputModule);
|
||||
boost::apply_visitor(weightSizeVisitor, feedbackModule);
|
||||
boost::apply_visitor(weightSizeVisitor, transferModule);
|
||||
|
||||
boost::apply_visitor(AddVisitor<CustomLayers...>(inputModule),
|
||||
mergeModule);
|
||||
boost::apply_visitor(AddVisitor<CustomLayers...>(feedbackModule),
|
||||
|
||||
@@ -51,6 +51,8 @@ HoeffdingTree<
|
||||
ownsInfo(true),
|
||||
successProbability(successProbability),
|
||||
splitDimension(size_t(-1)),
|
||||
majorityClass(0),
|
||||
majorityProbability(0.0),
|
||||
categoricalSplit(0),
|
||||
numericSplit()
|
||||
{
|
||||
@@ -107,6 +109,8 @@ HoeffdingTree<
|
||||
ownsInfo(true),
|
||||
successProbability(successProbability),
|
||||
splitDimension(size_t(-1)),
|
||||
majorityClass(0),
|
||||
majorityProbability(0.0),
|
||||
categoricalSplit(0),
|
||||
numericSplit()
|
||||
{
|
||||
@@ -169,6 +173,8 @@ HoeffdingTree<
|
||||
ownsInfo(true),
|
||||
successProbability(0.95),
|
||||
splitDimension(size_t(-1)),
|
||||
majorityClass(0),
|
||||
majorityProbability(0.0),
|
||||
categoricalSplit(0),
|
||||
numericSplit()
|
||||
{
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
# Define the files we need to compile.
|
||||
# Anything not in this list will not be compiled into mlpack.
|
||||
set(SOURCES
|
||||
kde.hpp
|
||||
kde_impl.hpp
|
||||
kde_rules.hpp
|
||||
kde_rules_impl.hpp
|
||||
kde_stat.hpp
|
||||
kde_model.hpp
|
||||
kde_model_impl.hpp
|
||||
)
|
||||
|
||||
# Add directory name to sources.
|
||||
set(DIR_SRCS)
|
||||
foreach(file ${SOURCES})
|
||||
set(DIR_SRCS ${DIR_SRCS} ${CMAKE_CURRENT_SOURCE_DIR}/${file})
|
||||
endforeach()
|
||||
# Append sources (with directory name) to list of all mlpack sources (used at
|
||||
# the parent scope).
|
||||
set(MLPACK_SRCS ${MLPACK_SRCS} ${DIR_SRCS} PARENT_SCOPE)
|
||||
|
||||
add_cli_executable(kde)
|
||||
add_python_binding(kde)
|
||||
@@ -0,0 +1,263 @@
|
||||
/**
|
||||
* @file kde.hpp
|
||||
* @author Roberto Hueso
|
||||
*
|
||||
* Kernel Density Estimation.
|
||||
*
|
||||
* 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_KDE_KDE_HPP
|
||||
#define MLPACK_METHODS_KDE_KDE_HPP
|
||||
|
||||
#include <mlpack/prereqs.hpp>
|
||||
#include <mlpack/core/tree/binary_space_tree.hpp>
|
||||
|
||||
#include "kde_stat.hpp"
|
||||
|
||||
namespace mlpack {
|
||||
namespace kde /** Kernel Density Estimation. */ {
|
||||
|
||||
//! KDEMode represents the ways in which KDE algorithm can be executed.
|
||||
enum KDEMode
|
||||
{
|
||||
DUAL_TREE_MODE,
|
||||
SINGLE_TREE_MODE
|
||||
};
|
||||
|
||||
/**
|
||||
* The KDE class is a template class for performing Kernel Density Estimations.
|
||||
* In statistics, kernel density estimation is a way to estimate the
|
||||
* probability density function of a variable in a non parametric way.
|
||||
* This implementation performs this estimation using a tree-independent
|
||||
* dual-tree algorithm. Details about this algorithm are available in KDERules.
|
||||
*
|
||||
* @tparam KernelType Kernel function to use for KDE calculations.
|
||||
* @tparam MetricType Metric to use for KDE calculations.
|
||||
* @tparam MatType Type of data to use.
|
||||
* @tparam TreeType Type of tree to use; must satisfy the TreeType policy API.
|
||||
* @tparam DualTreeTraversalType Type of dual-tree traversal to use.
|
||||
* @tparam SingleTreeTraversalType Type of single-tree traversal to use.
|
||||
*/
|
||||
template<typename KernelType = kernel::GaussianKernel,
|
||||
typename MetricType = mlpack::metric::EuclideanDistance,
|
||||
typename MatType = arma::mat,
|
||||
template<typename TreeMetricType,
|
||||
typename TreeStatType,
|
||||
typename TreeMatType> class TreeType = tree::KDTree,
|
||||
template<typename RuleType> class DualTreeTraversalType =
|
||||
TreeType<MetricType,
|
||||
kde::KDEStat,
|
||||
MatType>::template DualTreeTraverser,
|
||||
template<typename RuleType> class SingleTreeTraversalType =
|
||||
TreeType<MetricType,
|
||||
kde::KDEStat,
|
||||
MatType>::template SingleTreeTraverser>
|
||||
class KDE
|
||||
{
|
||||
public:
|
||||
//! Convenience typedef.
|
||||
typedef TreeType<MetricType, kde::KDEStat, MatType> Tree;
|
||||
|
||||
/**
|
||||
* Initialize KDE object using custom instantiated Metric and Kernel objects.
|
||||
*
|
||||
* @param relError Relative error tolerance of the model.
|
||||
* @param absError Absolute error tolerance of the model.
|
||||
* @param kernel Instantiated kernel object.
|
||||
* @param mode Mode for the algorithm.
|
||||
* @param metric Instantiated metric object.
|
||||
*/
|
||||
KDE(const double relError = 0.05,
|
||||
const double absError = 0,
|
||||
KernelType kernel = KernelType(),
|
||||
const KDEMode mode = DUAL_TREE_MODE,
|
||||
MetricType metric = MetricType());
|
||||
|
||||
/**
|
||||
* Construct KDE object as a copy of the given model. This may be
|
||||
* computationally intensive!
|
||||
*
|
||||
* @param other KDE object to copy.
|
||||
*/
|
||||
KDE(const KDE& other);
|
||||
|
||||
/**
|
||||
* Construct KDE object taking ownership of the given model.
|
||||
*
|
||||
* @param other KDE object to take ownership of.
|
||||
*/
|
||||
KDE(KDE&& other);
|
||||
|
||||
/**
|
||||
* Copy a KDE model.
|
||||
*
|
||||
* Use std::move if the object to copy is no longer needed.
|
||||
*
|
||||
* @param other KDE model to copy.
|
||||
*/
|
||||
KDE& operator=(KDE other);
|
||||
|
||||
/**
|
||||
* Destroy the KDE object. If this object created any trees, they will be
|
||||
* deleted. If you created the trees then you have to delete them yourself.
|
||||
*/
|
||||
~KDE();
|
||||
|
||||
/**
|
||||
* Trains the KDE model. It builds a tree using a reference set.
|
||||
*
|
||||
* Use std::move if the reference set is no longer needed.
|
||||
*
|
||||
* @param referenceSet Set of reference data.
|
||||
*/
|
||||
void Train(MatType referenceSet);
|
||||
|
||||
/**
|
||||
* Trains the KDE model. Sets the reference tree to an already created tree.
|
||||
*
|
||||
* - If TreeTraits<TreeType>::RearrangesDataset is false then it is possible
|
||||
* to use an empty oldFromNewReferences vector.
|
||||
*
|
||||
* @param referenceTree Built reference tree.
|
||||
* @param oldFromNewReferences Permutations of reference points obtained
|
||||
* during tree generation.
|
||||
*/
|
||||
void Train(Tree* referenceTree, std::vector<size_t>* oldFromNewReferences);
|
||||
|
||||
/**
|
||||
* Estimate density of each point in the query set given the data of the
|
||||
* reference set. The result is stored in an estimations vector.
|
||||
* Estimations might not be normalized.
|
||||
*
|
||||
* - Dimension of each point in the query set must match the dimension of each
|
||||
* point in the reference set.
|
||||
*
|
||||
* - Use std::move if the query set is no longer needed.
|
||||
*
|
||||
* @pre The model has to be previously trained.
|
||||
* @param querySet Set of query points to get the density of.
|
||||
* @param estimations Object which will hold the density of each query point.
|
||||
*/
|
||||
void Evaluate(MatType querySet, arma::vec& estimations);
|
||||
|
||||
/**
|
||||
* Estimate density of each point in the query set given the data of an
|
||||
* already created query tree. The result is stored in an estimations vector.
|
||||
* Estimations might not be normalized.
|
||||
*
|
||||
* - Dimension of each point in the queryTree dataset must match the dimension
|
||||
* of each point in the reference set.
|
||||
*
|
||||
* - Use std::move if the query tree is no longer needed.
|
||||
*
|
||||
* @pre The model has to be previously trained and mode has to be dual-tree.
|
||||
* @param queryTree Tree of query points to get the density of.
|
||||
* @param oldFromNewQueries Mappings of query points to the tree dataset.
|
||||
* @param estimations Object which will hold the density of each query point.
|
||||
*/
|
||||
void Evaluate(Tree* queryTree,
|
||||
const std::vector<size_t>& oldFromNewQueries,
|
||||
arma::vec& estimations);
|
||||
|
||||
/**
|
||||
* Estimate density of each point in the reference set given the data of the
|
||||
* reference set. It does not compute the estimation of a point with itself.
|
||||
* The result is stored in an estimations vector. Estimations might not be
|
||||
* normalized.
|
||||
*
|
||||
* @pre The model has to be previously trained.
|
||||
* @param estimations Object which will hold the density of each reference
|
||||
* point.
|
||||
*/
|
||||
void Evaluate(arma::vec& estimations);
|
||||
|
||||
//! Get the kernel.
|
||||
const KernelType& Kernel() const { return kernel; }
|
||||
|
||||
//! Modify the kernel.
|
||||
KernelType& Kernel() { return kernel; }
|
||||
|
||||
//! Get the metric.
|
||||
const MetricType& Metric() const { return metric; }
|
||||
|
||||
//! Modify the metric.
|
||||
MetricType& Metric() { return metric; }
|
||||
|
||||
//! Get the reference tree.
|
||||
Tree* ReferenceTree() { return referenceTree; }
|
||||
|
||||
//! Get relative error tolerance.
|
||||
double RelativeError() const { return relError; }
|
||||
|
||||
//! Modify relative error tolerance (0 <= newError <= 1).
|
||||
void RelativeError(const double newError);
|
||||
|
||||
//! Get absolute error tolerance.
|
||||
double AbsoluteError() const { return absError; }
|
||||
|
||||
//! Modify absolute error tolerance (0 <= newError).
|
||||
void AbsoluteError(const double newError);
|
||||
|
||||
//! Check whether reference tree is owned by the KDE model.
|
||||
bool OwnsReferenceTree() const { return ownsReferenceTree; }
|
||||
|
||||
//! Check whether KDE model is trained or not.
|
||||
bool IsTrained() const { return trained; }
|
||||
|
||||
//! Get the mode of KDE.
|
||||
KDEMode Mode() const { return mode; }
|
||||
|
||||
//! Modify the mode of KDE.
|
||||
KDEMode& Mode() { return mode; }
|
||||
|
||||
//! Serialize the model.
|
||||
template<typename Archive>
|
||||
void serialize(Archive& ar, const unsigned int /* version */);
|
||||
|
||||
private:
|
||||
//! Kernel.
|
||||
KernelType kernel;
|
||||
|
||||
//! Metric.
|
||||
MetricType metric;
|
||||
|
||||
//! Reference tree.
|
||||
Tree* referenceTree;
|
||||
|
||||
//! Permutations of reference points.
|
||||
std::vector<size_t>* oldFromNewReferences;
|
||||
|
||||
//! Relative error tolerance.
|
||||
double relError;
|
||||
|
||||
//! Absolute error tolerance.
|
||||
double absError;
|
||||
|
||||
//! If true, the KDE object is responsible for deleting the reference tree.
|
||||
bool ownsReferenceTree;
|
||||
|
||||
//! If true, the KDE object is trained.
|
||||
bool trained;
|
||||
|
||||
//! Mode of the KDE algorithm.
|
||||
KDEMode mode;
|
||||
|
||||
//! Check whether absolute and relative error values are compatible.
|
||||
static void CheckErrorValues(const double relError, const double absError);
|
||||
|
||||
//! Rearrange estimations vector if required.
|
||||
static void RearrangeEstimations(const std::vector<size_t>& oldFromNew,
|
||||
arma::vec& estimations);
|
||||
};
|
||||
|
||||
} // namespace kde
|
||||
} // namespace mlpack
|
||||
|
||||
// Include implementation.
|
||||
#include "kde_impl.hpp"
|
||||
|
||||
#endif // MLPACK_METHODS_KDE_KDE_HPP
|
||||
@@ -0,0 +1,651 @@
|
||||
/**
|
||||
* @file kde_impl.hpp
|
||||
* @author Roberto Hueso
|
||||
*
|
||||
* Implementation of Kernel Density Estimation.
|
||||
*
|
||||
* 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 "kde.hpp"
|
||||
#include "kde_rules.hpp"
|
||||
|
||||
namespace mlpack {
|
||||
namespace kde {
|
||||
|
||||
//! Construct tree that rearranges the dataset.
|
||||
template<typename TreeType, typename MatType>
|
||||
TreeType* BuildTree(
|
||||
MatType&& dataset,
|
||||
std::vector<size_t>& oldFromNew,
|
||||
const typename std::enable_if<
|
||||
tree::TreeTraits<TreeType>::RearrangesDataset>::type* = 0)
|
||||
{
|
||||
return new TreeType(std::forward<MatType>(dataset), oldFromNew);
|
||||
}
|
||||
|
||||
//! Construct tree that doesn't rearrange the dataset.
|
||||
template<typename TreeType, typename MatType>
|
||||
TreeType* BuildTree(
|
||||
MatType&& dataset,
|
||||
const std::vector<size_t>& /* oldFromNew */,
|
||||
const typename std::enable_if<
|
||||
!tree::TreeTraits<TreeType>::RearrangesDataset>::type* = 0)
|
||||
{
|
||||
return new TreeType(std::forward<MatType>(dataset));
|
||||
}
|
||||
|
||||
template<typename KernelType,
|
||||
typename MetricType,
|
||||
typename MatType,
|
||||
template<typename TreeMetricType,
|
||||
typename TreeStatType,
|
||||
typename TreeMatType> class TreeType,
|
||||
template<typename> class DualTreeTraversalType,
|
||||
template<typename> class SingleTreeTraversalType>
|
||||
KDE<KernelType,
|
||||
MetricType,
|
||||
MatType,
|
||||
TreeType,
|
||||
DualTreeTraversalType,
|
||||
SingleTreeTraversalType>::
|
||||
KDE(const double relError,
|
||||
const double absError,
|
||||
KernelType kernel,
|
||||
const KDEMode mode,
|
||||
MetricType metric) :
|
||||
kernel(kernel),
|
||||
metric(metric),
|
||||
referenceTree(nullptr),
|
||||
oldFromNewReferences(nullptr),
|
||||
relError(relError),
|
||||
absError(absError),
|
||||
ownsReferenceTree(false),
|
||||
trained(false),
|
||||
mode(mode)
|
||||
{
|
||||
CheckErrorValues(relError, absError);
|
||||
}
|
||||
|
||||
template<typename KernelType,
|
||||
typename MetricType,
|
||||
typename MatType,
|
||||
template<typename TreeMetricType,
|
||||
typename TreeStatType,
|
||||
typename TreeMatType> class TreeType,
|
||||
template<typename> class DualTreeTraversalType,
|
||||
template<typename> class SingleTreeTraversalType>
|
||||
KDE<KernelType,
|
||||
MetricType,
|
||||
MatType,
|
||||
TreeType,
|
||||
DualTreeTraversalType,
|
||||
SingleTreeTraversalType>::
|
||||
KDE(const KDE& other) :
|
||||
kernel(KernelType(other.kernel)),
|
||||
metric(MetricType(other.metric)),
|
||||
relError(other.relError),
|
||||
absError(other.absError),
|
||||
ownsReferenceTree(other.ownsReferenceTree),
|
||||
trained(other.trained),
|
||||
mode(other.mode)
|
||||
{
|
||||
if (trained)
|
||||
{
|
||||
if (ownsReferenceTree)
|
||||
{
|
||||
oldFromNewReferences =
|
||||
new std::vector<size_t>(*other.oldFromNewReferences);
|
||||
referenceTree = new Tree(*other.referenceTree);
|
||||
}
|
||||
else
|
||||
{
|
||||
oldFromNewReferences = other.oldFromNewReferences;
|
||||
referenceTree = other.referenceTree;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename KernelType,
|
||||
typename MetricType,
|
||||
typename MatType,
|
||||
template<typename TreeMetricType,
|
||||
typename TreeStatType,
|
||||
typename TreeMatType> class TreeType,
|
||||
template<typename> class DualTreeTraversalType,
|
||||
template<typename> class SingleTreeTraversalType>
|
||||
KDE<KernelType,
|
||||
MetricType,
|
||||
MatType,
|
||||
TreeType,
|
||||
DualTreeTraversalType,
|
||||
SingleTreeTraversalType>::
|
||||
KDE(KDE&& other) :
|
||||
kernel(std::move(other.kernel)),
|
||||
metric(std::move(other.metric)),
|
||||
referenceTree(other.referenceTree),
|
||||
oldFromNewReferences(other.oldFromNewReferences),
|
||||
relError(other.relError),
|
||||
absError(other.absError),
|
||||
ownsReferenceTree(other.ownsReferenceTree),
|
||||
trained(other.trained),
|
||||
mode(other.mode)
|
||||
{
|
||||
other.kernel = std::move(KernelType());
|
||||
other.metric = std::move(MetricType());
|
||||
other.referenceTree = nullptr;
|
||||
other.oldFromNewReferences = nullptr;
|
||||
other.ownsReferenceTree = false;
|
||||
other.trained = false;
|
||||
}
|
||||
|
||||
template<typename KernelType,
|
||||
typename MetricType,
|
||||
typename MatType,
|
||||
template<typename TreeMetricType,
|
||||
typename TreeStatType,
|
||||
typename TreeMatType> class TreeType,
|
||||
template<typename> class DualTreeTraversalType,
|
||||
template<typename> class SingleTreeTraversalType>
|
||||
KDE<KernelType,
|
||||
MetricType,
|
||||
MatType,
|
||||
TreeType,
|
||||
DualTreeTraversalType,
|
||||
SingleTreeTraversalType>&
|
||||
KDE<KernelType,
|
||||
MetricType,
|
||||
MatType,
|
||||
TreeType,
|
||||
DualTreeTraversalType,
|
||||
SingleTreeTraversalType>::
|
||||
operator=(KDE other)
|
||||
{
|
||||
// Clean memory.
|
||||
if (ownsReferenceTree)
|
||||
{
|
||||
delete referenceTree;
|
||||
delete oldFromNewReferences;
|
||||
}
|
||||
|
||||
// Move the other object.
|
||||
this->kernel = std::move(other.kernel);
|
||||
this->metric = std::move(other.metric);
|
||||
this->referenceTree = std::move(other.referenceTree);
|
||||
this->oldFromNewReferences = std::move(other.oldFromNewReferences);
|
||||
this->relError = other.relError;
|
||||
this->absError = other.absError;
|
||||
this->ownsReferenceTree = other.ownsReferenceTree;
|
||||
this->trained = other.trained;
|
||||
this->mode = other.mode;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename KernelType,
|
||||
typename MetricType,
|
||||
typename MatType,
|
||||
template<typename TreeMetricType,
|
||||
typename TreeStatType,
|
||||
typename TreeMatType> class TreeType,
|
||||
template<typename> class DualTreeTraversalType,
|
||||
template<typename> class SingleTreeTraversalType>
|
||||
KDE<KernelType,
|
||||
MetricType,
|
||||
MatType,
|
||||
TreeType,
|
||||
DualTreeTraversalType,
|
||||
SingleTreeTraversalType>::
|
||||
~KDE()
|
||||
{
|
||||
if (ownsReferenceTree)
|
||||
{
|
||||
delete referenceTree;
|
||||
delete oldFromNewReferences;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename KernelType,
|
||||
typename MetricType,
|
||||
typename MatType,
|
||||
template<typename TreeMetricType,
|
||||
typename TreeStatType,
|
||||
typename TreeMatType> class TreeType,
|
||||
template<typename> class DualTreeTraversalType,
|
||||
template<typename> class SingleTreeTraversalType>
|
||||
void KDE<KernelType,
|
||||
MetricType,
|
||||
MatType,
|
||||
TreeType,
|
||||
DualTreeTraversalType,
|
||||
SingleTreeTraversalType>::
|
||||
Train(MatType referenceSet)
|
||||
{
|
||||
// Check if referenceSet is not an empty set.
|
||||
if (referenceSet.n_cols == 0)
|
||||
{
|
||||
throw std::invalid_argument("cannot train KDE model with an empty "
|
||||
"reference set");
|
||||
}
|
||||
|
||||
if (ownsReferenceTree)
|
||||
{
|
||||
delete referenceTree;
|
||||
delete oldFromNewReferences;
|
||||
}
|
||||
|
||||
this->ownsReferenceTree = true;
|
||||
Timer::Start("building_reference_tree");
|
||||
this->oldFromNewReferences = new std::vector<size_t>;
|
||||
this->referenceTree = BuildTree<Tree>(std::move(referenceSet),
|
||||
*oldFromNewReferences);
|
||||
Timer::Stop("building_reference_tree");
|
||||
this->trained = true;
|
||||
}
|
||||
|
||||
template<typename KernelType,
|
||||
typename MetricType,
|
||||
typename MatType,
|
||||
template<typename TreeMetricType,
|
||||
typename TreeStatType,
|
||||
typename TreeMatType> class TreeType,
|
||||
template<typename> class DualTreeTraversalType,
|
||||
template<typename> class SingleTreeTraversalType>
|
||||
void KDE<KernelType,
|
||||
MetricType,
|
||||
MatType,
|
||||
TreeType,
|
||||
DualTreeTraversalType,
|
||||
SingleTreeTraversalType>::
|
||||
Train(Tree* referenceTree, std::vector<size_t>* oldFromNewReferences)
|
||||
{
|
||||
// Check if referenceTree dataset is not an empty set.
|
||||
if (referenceTree->Dataset().n_cols == 0)
|
||||
{
|
||||
throw std::invalid_argument("cannot train KDE model with an empty "
|
||||
"reference set");
|
||||
}
|
||||
|
||||
if (ownsReferenceTree == true)
|
||||
{
|
||||
delete this->referenceTree;
|
||||
delete this->oldFromNewReferences;
|
||||
}
|
||||
|
||||
this->ownsReferenceTree = false;
|
||||
this->referenceTree = referenceTree;
|
||||
this->oldFromNewReferences = oldFromNewReferences;
|
||||
this->trained = true;
|
||||
}
|
||||
|
||||
template<typename KernelType,
|
||||
typename MetricType,
|
||||
typename MatType,
|
||||
template<typename TreeMetricType,
|
||||
typename TreeStatType,
|
||||
typename TreeMatType> class TreeType,
|
||||
template<typename> class DualTreeTraversalType,
|
||||
template<typename> class SingleTreeTraversalType>
|
||||
void KDE<KernelType,
|
||||
MetricType,
|
||||
MatType,
|
||||
TreeType,
|
||||
DualTreeTraversalType,
|
||||
SingleTreeTraversalType>::
|
||||
Evaluate(MatType querySet, arma::vec& estimations)
|
||||
{
|
||||
if (mode == DUAL_TREE_MODE)
|
||||
{
|
||||
Timer::Start("building_query_tree");
|
||||
std::vector<size_t> oldFromNewQueries;
|
||||
Tree* queryTree = BuildTree<Tree>(std::move(querySet), oldFromNewQueries);
|
||||
Timer::Stop("building_query_tree");
|
||||
this->Evaluate(queryTree, oldFromNewQueries, estimations);
|
||||
delete queryTree;
|
||||
}
|
||||
else if (mode == SINGLE_TREE_MODE)
|
||||
{
|
||||
// Get estimations vector ready.
|
||||
estimations.clear();
|
||||
estimations.set_size(querySet.n_cols);
|
||||
estimations.fill(arma::fill::zeros);
|
||||
|
||||
// Check whether has already been trained.
|
||||
if (!trained)
|
||||
{
|
||||
throw std::runtime_error("cannot evaluate KDE model: model needs to be "
|
||||
"trained before evaluation");
|
||||
}
|
||||
|
||||
// Check querySet has at least 1 element to evaluate.
|
||||
if (querySet.n_cols == 0)
|
||||
{
|
||||
Log::Warn << "KDE::Evaluate(): querySet is empty, no predictions will "
|
||||
<< "be returned" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// Check whether dimensions match.
|
||||
if (querySet.n_rows != referenceTree->Dataset().n_rows)
|
||||
{
|
||||
throw std::invalid_argument("cannot evaluate KDE model: querySet and "
|
||||
"referenceSet dimensions don't match");
|
||||
}
|
||||
|
||||
Timer::Start("computing_kde");
|
||||
|
||||
// Evaluate.
|
||||
typedef KDERules<MetricType, KernelType, Tree> RuleType;
|
||||
RuleType rules = RuleType(referenceTree->Dataset(),
|
||||
querySet,
|
||||
estimations,
|
||||
relError,
|
||||
absError,
|
||||
metric,
|
||||
kernel,
|
||||
false);
|
||||
|
||||
// Create traverser.
|
||||
SingleTreeTraversalType<RuleType> traverser(rules);
|
||||
|
||||
// Traverse for each point.
|
||||
for (size_t i = 0; i < querySet.n_cols; ++i)
|
||||
traverser.Traverse(i, *referenceTree);
|
||||
|
||||
estimations /= referenceTree->Dataset().n_cols;
|
||||
Timer::Stop("computing_kde");
|
||||
|
||||
Log::Info << rules.Scores() << " node combinations were scored."
|
||||
<< std::endl;
|
||||
Log::Info << rules.BaseCases() << " base cases were calculated."
|
||||
<< std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename KernelType,
|
||||
typename MetricType,
|
||||
typename MatType,
|
||||
template<typename TreeMetricType,
|
||||
typename TreeStatType,
|
||||
typename TreeMatType> class TreeType,
|
||||
template<typename> class DualTreeTraversalType,
|
||||
template<typename> class SingleTreeTraversalType>
|
||||
void KDE<KernelType,
|
||||
MetricType,
|
||||
MatType,
|
||||
TreeType,
|
||||
DualTreeTraversalType,
|
||||
SingleTreeTraversalType>::
|
||||
Evaluate(Tree* queryTree,
|
||||
const std::vector<size_t>& oldFromNewQueries,
|
||||
arma::vec& estimations)
|
||||
{
|
||||
// Get estimations vector ready.
|
||||
estimations.clear();
|
||||
estimations.set_size(queryTree->Dataset().n_cols);
|
||||
estimations.fill(arma::fill::zeros);
|
||||
|
||||
// Check whether has already been trained.
|
||||
if (!trained)
|
||||
{
|
||||
throw std::runtime_error("cannot evaluate KDE model: model needs to be "
|
||||
"trained before evaluation");
|
||||
}
|
||||
|
||||
// Check querySet has at least 1 element to evaluate.
|
||||
if (queryTree->Dataset().n_cols == 0)
|
||||
{
|
||||
Log::Warn << "KDE::Evaluate(): querySet is empty, no predictions will "
|
||||
<< "be returned" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// Check whether dimensions match.
|
||||
if (queryTree->Dataset().n_rows != referenceTree->Dataset().n_rows)
|
||||
{
|
||||
throw std::invalid_argument("cannot evaluate KDE model: querySet and "
|
||||
"referenceSet dimensions don't match");
|
||||
}
|
||||
|
||||
// Check the mode is correct.
|
||||
if (mode != DUAL_TREE_MODE)
|
||||
{
|
||||
throw std::invalid_argument("cannot evaluate KDE model: cannot use "
|
||||
"a query tree when mode is different from "
|
||||
"dual-tree");
|
||||
}
|
||||
|
||||
Timer::Start("computing_kde");
|
||||
|
||||
// Evaluate.
|
||||
typedef KDERules<MetricType, KernelType, Tree> RuleType;
|
||||
RuleType rules = RuleType(referenceTree->Dataset(),
|
||||
queryTree->Dataset(),
|
||||
estimations,
|
||||
relError,
|
||||
absError,
|
||||
metric,
|
||||
kernel,
|
||||
false);
|
||||
|
||||
// Create traverser.
|
||||
DualTreeTraversalType<RuleType> traverser(rules);
|
||||
traverser.Traverse(*queryTree, *referenceTree);
|
||||
estimations /= referenceTree->Dataset().n_cols;
|
||||
Timer::Stop("computing_kde");
|
||||
|
||||
// Rearrange if necessary.
|
||||
RearrangeEstimations(oldFromNewQueries, estimations);
|
||||
|
||||
Log::Info << rules.Scores() << " node combinations were scored." << std::endl;
|
||||
Log::Info << rules.BaseCases() << " base cases were calculated." << std::endl;
|
||||
}
|
||||
|
||||
template<typename KernelType,
|
||||
typename MetricType,
|
||||
typename MatType,
|
||||
template<typename TreeMetricType,
|
||||
typename TreeStatType,
|
||||
typename TreeMatType> class TreeType,
|
||||
template<typename> class DualTreeTraversalType,
|
||||
template<typename> class SingleTreeTraversalType>
|
||||
void KDE<KernelType,
|
||||
MetricType,
|
||||
MatType,
|
||||
TreeType,
|
||||
DualTreeTraversalType,
|
||||
SingleTreeTraversalType>::
|
||||
Evaluate(arma::vec& estimations)
|
||||
{
|
||||
// Check whether has already been trained.
|
||||
if (!trained)
|
||||
{
|
||||
throw std::runtime_error("cannot evaluate KDE model: model needs to be "
|
||||
"trained before evaluation");
|
||||
}
|
||||
|
||||
// Get estimations vector ready.
|
||||
estimations.clear();
|
||||
estimations.set_size(referenceTree->Dataset().n_cols);
|
||||
estimations.fill(arma::fill::zeros);
|
||||
|
||||
Timer::Start("computing_kde");
|
||||
|
||||
// Evaluate.
|
||||
typedef KDERules<MetricType, KernelType, Tree> RuleType;
|
||||
RuleType rules = RuleType(referenceTree->Dataset(),
|
||||
referenceTree->Dataset(),
|
||||
estimations,
|
||||
relError,
|
||||
absError,
|
||||
metric,
|
||||
kernel,
|
||||
true);
|
||||
|
||||
if (mode == DUAL_TREE_MODE)
|
||||
{
|
||||
// Create traverser.
|
||||
DualTreeTraversalType<RuleType> traverser(rules);
|
||||
traverser.Traverse(*referenceTree, *referenceTree);
|
||||
}
|
||||
else if (mode == SINGLE_TREE_MODE)
|
||||
{
|
||||
SingleTreeTraversalType<RuleType> traverser(rules);
|
||||
for (size_t i = 0; i < referenceTree->Dataset().n_cols; ++i)
|
||||
traverser.Traverse(i, *referenceTree);
|
||||
}
|
||||
|
||||
estimations /= referenceTree->Dataset().n_cols;
|
||||
// Rearrange if necessary.
|
||||
RearrangeEstimations(*oldFromNewReferences, estimations);
|
||||
Timer::Stop("computing_kde");
|
||||
|
||||
Log::Info << rules.Scores() << " node combinations were scored." << std::endl;
|
||||
Log::Info << rules.BaseCases() << " base cases were calculated." << std::endl;
|
||||
}
|
||||
|
||||
template<typename KernelType,
|
||||
typename MetricType,
|
||||
typename MatType,
|
||||
template<typename TreeMetricType,
|
||||
typename TreeStatType,
|
||||
typename TreeMatType> class TreeType,
|
||||
template<typename> class DualTreeTraversalType,
|
||||
template<typename> class SingleTreeTraversalType>
|
||||
void KDE<KernelType,
|
||||
MetricType,
|
||||
MatType,
|
||||
TreeType,
|
||||
DualTreeTraversalType,
|
||||
SingleTreeTraversalType>::
|
||||
RelativeError(const double newError)
|
||||
{
|
||||
CheckErrorValues(newError, absError);
|
||||
relError = newError;
|
||||
}
|
||||
|
||||
template<typename KernelType,
|
||||
typename MetricType,
|
||||
typename MatType,
|
||||
template<typename TreeMetricType,
|
||||
typename TreeStatType,
|
||||
typename TreeMatType> class TreeType,
|
||||
template<typename> class DualTreeTraversalType,
|
||||
template<typename> class SingleTreeTraversalType>
|
||||
void KDE<KernelType,
|
||||
MetricType,
|
||||
MatType,
|
||||
TreeType,
|
||||
DualTreeTraversalType,
|
||||
SingleTreeTraversalType>::
|
||||
AbsoluteError(const double newError)
|
||||
{
|
||||
CheckErrorValues(relError, newError);
|
||||
absError = newError;
|
||||
}
|
||||
|
||||
template<typename KernelType,
|
||||
typename MetricType,
|
||||
typename MatType,
|
||||
template<typename TreeMetricType,
|
||||
typename TreeStatType,
|
||||
typename TreeMatType> class TreeType,
|
||||
template<typename> class DualTreeTraversalType,
|
||||
template<typename> class SingleTreeTraversalType>
|
||||
template<typename Archive>
|
||||
void KDE<KernelType,
|
||||
MetricType,
|
||||
MatType,
|
||||
TreeType,
|
||||
DualTreeTraversalType,
|
||||
SingleTreeTraversalType>::
|
||||
serialize(Archive& ar, const unsigned int /* version */)
|
||||
{
|
||||
// Serialize preferences.
|
||||
ar & BOOST_SERIALIZATION_NVP(relError);
|
||||
ar & BOOST_SERIALIZATION_NVP(absError);
|
||||
ar & BOOST_SERIALIZATION_NVP(trained);
|
||||
ar & BOOST_SERIALIZATION_NVP(mode);
|
||||
|
||||
// If we are loading, clean up memory if necessary.
|
||||
if (Archive::is_loading::value)
|
||||
{
|
||||
if (ownsReferenceTree && referenceTree)
|
||||
{
|
||||
delete referenceTree;
|
||||
delete oldFromNewReferences;
|
||||
}
|
||||
// After loading tree, we own it.
|
||||
ownsReferenceTree = true;
|
||||
}
|
||||
|
||||
// Serialize the rest of values.
|
||||
ar & BOOST_SERIALIZATION_NVP(kernel);
|
||||
ar & BOOST_SERIALIZATION_NVP(metric);
|
||||
ar & BOOST_SERIALIZATION_NVP(referenceTree);
|
||||
ar & BOOST_SERIALIZATION_NVP(oldFromNewReferences);
|
||||
}
|
||||
|
||||
template<typename KernelType,
|
||||
typename MetricType,
|
||||
typename MatType,
|
||||
template<typename TreeMetricType,
|
||||
typename TreeStatType,
|
||||
typename TreeMatType> class TreeType,
|
||||
template<typename> class DualTreeTraversalType,
|
||||
template<typename> class SingleTreeTraversalType>
|
||||
void KDE<KernelType,
|
||||
MetricType,
|
||||
MatType,
|
||||
TreeType,
|
||||
DualTreeTraversalType,
|
||||
SingleTreeTraversalType>::
|
||||
CheckErrorValues(const double relError, const double absError)
|
||||
{
|
||||
if (relError < 0 || relError > 1)
|
||||
{
|
||||
throw std::invalid_argument("Relative error tolerance must be a value "
|
||||
"between 0 and 1");
|
||||
}
|
||||
if (absError < 0)
|
||||
{
|
||||
throw std::invalid_argument("Absolute error tolerance must be a value "
|
||||
"greater or equal to 0");
|
||||
}
|
||||
}
|
||||
|
||||
template<typename KernelType,
|
||||
typename MetricType,
|
||||
typename MatType,
|
||||
template<typename TreeMetricType,
|
||||
typename TreeStatType,
|
||||
typename TreeMatType> class TreeType,
|
||||
template<typename> class DualTreeTraversalType,
|
||||
template<typename> class SingleTreeTraversalType>
|
||||
void KDE<KernelType,
|
||||
MetricType,
|
||||
MatType,
|
||||
TreeType,
|
||||
DualTreeTraversalType,
|
||||
SingleTreeTraversalType>::
|
||||
RearrangeEstimations(const std::vector<size_t>& oldFromNew,
|
||||
arma::vec& estimations)
|
||||
{
|
||||
if (tree::TreeTraits<Tree>::RearrangesDataset)
|
||||
{
|
||||
const size_t nQueries = oldFromNew.size();
|
||||
arma::vec rearrangedEstimations(nQueries);
|
||||
|
||||
// Remap vector.
|
||||
for (size_t i = 0; i < nQueries; ++i)
|
||||
rearrangedEstimations(oldFromNew.at(i)) = estimations(i);
|
||||
|
||||
estimations = std::move(rearrangedEstimations);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace kde
|
||||
} // namespace mlpack
|
||||
@@ -0,0 +1,208 @@
|
||||
/**
|
||||
* @file kde_main.cpp
|
||||
* @author Roberto Hueso
|
||||
*
|
||||
* Executable for running Kernel Density Estimation.
|
||||
*
|
||||
* 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 <mlpack/core/util/mlpack_main.hpp>
|
||||
|
||||
#include "kde.hpp"
|
||||
#include "kde_model.hpp"
|
||||
|
||||
using namespace mlpack;
|
||||
using namespace mlpack::kde;
|
||||
using namespace mlpack::util;
|
||||
using namespace std;
|
||||
|
||||
// Define parameters for the executable.
|
||||
PROGRAM_INFO("Kernel Density Estimation",
|
||||
"This program performs a Kernel Density Estimation. KDE is a "
|
||||
"non-parametric way of estimating probability density function. "
|
||||
"For each query point the program will estimate its probability density "
|
||||
"by applying a kernel function to each reference point. The computational "
|
||||
"complexity of this is O(N^2) where there are N query points and N "
|
||||
"reference points, but this implementation will typically see better "
|
||||
"performance as it uses an approximate dual or single tree algorithm for "
|
||||
"acceleration."
|
||||
"\n\n"
|
||||
"Dual or single tree optimization allows to avoid lots of barely relevant "
|
||||
"calculations (as kernel function values decrease with distance), so it is "
|
||||
"an approximate computation. You can specify the maximum relative error "
|
||||
"tolerance for each query value with " + PRINT_PARAM_STRING("rel_error") +
|
||||
" as well as the maximum absolute error tolerance with the parameter " +
|
||||
PRINT_PARAM_STRING("abs_error") + ". This program runs using an Euclidean "
|
||||
"metric. Kernel function can be selected using the " +
|
||||
PRINT_PARAM_STRING("kernel") + " option. You can also choose what which "
|
||||
"type of tree to use for the dual-tree algorithm with " +
|
||||
PRINT_PARAM_STRING("tree") + ". It is also possible to select whether to "
|
||||
"use dual-tree algorithm or single-tree algorithm using the " +
|
||||
PRINT_PARAM_STRING("algorithm") + " option."
|
||||
"\n\n"
|
||||
"For example, the following will run KDE using the data in " +
|
||||
PRINT_DATASET("ref_data") + " for training and the data in " +
|
||||
PRINT_DATASET("qu_data") + " as query data. It will apply an Epanechnikov "
|
||||
"kernel with a 0.2 bandwidth to each reference point and use a KD-Tree for "
|
||||
"the dual-tree optimization. The returned predictions will be within 5% of "
|
||||
"the real KDE value for each query point."
|
||||
"\n\n" +
|
||||
PRINT_CALL("kde", "reference", "ref_data", "query", "qu_data", "bandwidth",
|
||||
0.2, "kernel", "epanechnikov", "tree", "kd-tree", "rel_error",
|
||||
0.05, "predictions", "out_data") +
|
||||
"\n\n"
|
||||
"the predicted density estimations will be stored in " +
|
||||
PRINT_DATASET("out_data") + "."
|
||||
"\n"
|
||||
"If no " + PRINT_PARAM_STRING("query") + " is provided, then KDE will be "
|
||||
"computed on the " + PRINT_PARAM_STRING("reference") + " dataset."
|
||||
"\n"
|
||||
"It is possible to select either a reference dataset or an input model "
|
||||
"but not both at the same time.");
|
||||
|
||||
// Required options.
|
||||
PARAM_MATRIX_IN("reference", "Input reference dataset use for KDE.", "r");
|
||||
PARAM_MATRIX_IN("query", "Query dataset to KDE on.", "q");
|
||||
PARAM_DOUBLE_IN("bandwidth", "Bandwidth of the kernel.", "b", 1.0);
|
||||
|
||||
// Load or save models.
|
||||
PARAM_MODEL_IN(KDEModel,
|
||||
"input_model",
|
||||
"Contains pre-trained KDE model.",
|
||||
"m");
|
||||
PARAM_MODEL_OUT(KDEModel,
|
||||
"output_model",
|
||||
"If specified, the KDE model will be saved here.",
|
||||
"M");
|
||||
|
||||
// Configuration options.
|
||||
PARAM_STRING_IN("kernel", "Kernel to use for the prediction."
|
||||
"('gaussian', 'epanechnikov', 'laplacian', 'spherical', 'triangular').",
|
||||
"k", "gaussian");
|
||||
PARAM_STRING_IN("tree", "Tree to use for the prediction."
|
||||
"('kd-tree', 'ball-tree', 'cover-tree', 'octree', 'r-tree').",
|
||||
"t", "kd-tree");
|
||||
PARAM_STRING_IN("algorithm", "Algorithm to use for the prediction."
|
||||
"('dual-tree', 'single-tree').",
|
||||
"a", "dual-tree");
|
||||
PARAM_DOUBLE_IN("rel_error",
|
||||
"Relative error tolerance for the prediction.",
|
||||
"e",
|
||||
0.05);
|
||||
PARAM_DOUBLE_IN("abs_error",
|
||||
"Relative error tolerance for the prediction.",
|
||||
"E",
|
||||
0.0);
|
||||
|
||||
// Output predictions options.
|
||||
PARAM_COL_OUT("predictions", "Vector to store density predictions.",
|
||||
"p");
|
||||
|
||||
// Maybe, in the future, it could be interesting to implement different metrics.
|
||||
|
||||
static void mlpackMain()
|
||||
{
|
||||
// Get some parameters.
|
||||
const double bandwidth = CLI::GetParam<double>("bandwidth");
|
||||
const std::string kernelStr = CLI::GetParam<std::string>("kernel");
|
||||
const std::string treeStr = CLI::GetParam<std::string>("tree");
|
||||
const std::string modeStr = CLI::GetParam<std::string>("algorithm");
|
||||
const double relError = CLI::GetParam<double>("rel_error");
|
||||
const double absError = CLI::GetParam<double>("abs_error");
|
||||
|
||||
// Initialize results vector.
|
||||
arma::vec estimations;
|
||||
|
||||
// You can only specify reference data or a pre-trained model.
|
||||
RequireOnlyOnePassed({ "reference", "input_model" }, true);
|
||||
ReportIgnoredParam({{ "input_model", true }}, "tree");
|
||||
ReportIgnoredParam({{ "input_model", true }}, "kernel");
|
||||
ReportIgnoredParam({{ "input_model", true }}, "rel_error");
|
||||
ReportIgnoredParam({{ "input_model", true }}, "abs_error");
|
||||
|
||||
// Requirements for parameter values.
|
||||
RequireParamInSet<string>("kernel", { "gaussian", "epanechnikov",
|
||||
"laplacian", "spherical", "triangular" }, true, "unknown kernel type");
|
||||
RequireParamInSet<string>("tree", { "kd-tree", "ball-tree", "cover-tree",
|
||||
"octree", "r-tree"}, true, "unknown tree type");
|
||||
RequireParamInSet<string>("algorithm", { "dual-tree", "single-tree"},
|
||||
true, "unknown algorithm");
|
||||
RequireParamValue<double>("rel_error", [](double x){return x >= 0 && x <= 1;},
|
||||
true, "relative error must be between 0 and 1");
|
||||
RequireParamValue<double>("abs_error", [](double x){return x >= 0;},
|
||||
true, "absolute error must be equal or greater than 0");
|
||||
|
||||
KDEModel* kde;
|
||||
|
||||
if (CLI::HasParam("reference"))
|
||||
{
|
||||
arma::mat reference = std::move(CLI::GetParam<arma::mat>("reference"));
|
||||
|
||||
kde = new KDEModel();
|
||||
// Set parameters.
|
||||
kde->Bandwidth() = bandwidth;
|
||||
kde->RelativeError() = relError;
|
||||
kde->AbsoluteError() = absError;
|
||||
|
||||
// Set KernelType.
|
||||
if (kernelStr == "gaussian")
|
||||
kde->KernelType() = KDEModel::GAUSSIAN_KERNEL;
|
||||
else if (kernelStr == "epanechnikov")
|
||||
kde->KernelType() = KDEModel::EPANECHNIKOV_KERNEL;
|
||||
else if (kernelStr == "laplacian")
|
||||
kde->KernelType() = KDEModel::LAPLACIAN_KERNEL;
|
||||
else if (kernelStr == "spherical")
|
||||
kde->KernelType() = KDEModel::SPHERICAL_KERNEL;
|
||||
else if (kernelStr == "triangular")
|
||||
kde->KernelType() = KDEModel::TRIANGULAR_KERNEL;
|
||||
|
||||
// Set TreeType.
|
||||
if (treeStr == "kd-tree")
|
||||
kde->TreeType() = KDEModel::KD_TREE;
|
||||
else if (treeStr == "ball-tree")
|
||||
kde->TreeType() = KDEModel::BALL_TREE;
|
||||
else if (treeStr == "cover-tree")
|
||||
kde->TreeType() = KDEModel::COVER_TREE;
|
||||
else if (treeStr == "octree")
|
||||
kde->TreeType() = KDEModel::OCTREE;
|
||||
else if (treeStr == "r-tree")
|
||||
kde->TreeType() = KDEModel::R_TREE;
|
||||
|
||||
// Build model.
|
||||
kde->BuildModel(std::move(reference));
|
||||
|
||||
// Set Mode.
|
||||
if (modeStr == "dual-tree")
|
||||
kde->Mode() = KDEMode::DUAL_TREE_MODE;
|
||||
else if (modeStr == "single-tree")
|
||||
kde->Mode() = KDEMode::SINGLE_TREE_MODE;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Load model.
|
||||
kde = CLI::GetParam<KDEModel*>("input_model");
|
||||
}
|
||||
|
||||
// Evaluation.
|
||||
if (CLI::HasParam("query"))
|
||||
{
|
||||
arma::mat query = std::move(CLI::GetParam<arma::mat>("query"));
|
||||
kde->Evaluate(std::move(query), estimations);
|
||||
}
|
||||
else
|
||||
{
|
||||
kde->Evaluate(estimations);
|
||||
}
|
||||
|
||||
// Output predictions if needed.
|
||||
if (CLI::HasParam("predictions"))
|
||||
CLI::GetParam<arma::vec>("predictions") = std::move(estimations);
|
||||
|
||||
// Save model.
|
||||
if (CLI::HasParam("output_model"))
|
||||
CLI::GetParam<KDEModel*>("output_model") = kde;
|
||||
}
|
||||
@@ -0,0 +1,382 @@
|
||||
/**
|
||||
* @file kde_model.hpp
|
||||
* @author Roberto Hueso
|
||||
*
|
||||
* Model for KDE. It abstracts different types of tree, kernels, etc.
|
||||
*
|
||||
* 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_KDE_MODEL_HPP
|
||||
#define MLPACK_METHODS_KDE_MODEL_HPP
|
||||
|
||||
// Include trees.
|
||||
#include <mlpack/core/tree/binary_space_tree.hpp>
|
||||
#include <mlpack/core/tree/octree.hpp>
|
||||
#include <mlpack/core/tree/cover_tree.hpp>
|
||||
#include <mlpack/core/tree/rectangle_tree.hpp>
|
||||
|
||||
// Include core.
|
||||
#include <mlpack/core.hpp>
|
||||
|
||||
// Remaining includes.
|
||||
#include <boost/variant.hpp>
|
||||
#include "kde.hpp"
|
||||
|
||||
namespace mlpack {
|
||||
namespace kde {
|
||||
|
||||
//! Alias template.
|
||||
template<typename KernelType,
|
||||
template<typename TreeMetricType,
|
||||
typename TreeStatType,
|
||||
typename TreeMatType> class TreeType>
|
||||
using KDEType = KDE<KernelType,
|
||||
metric::EuclideanDistance,
|
||||
arma::mat,
|
||||
TreeType,
|
||||
TreeType<metric::EuclideanDistance,
|
||||
kde::KDEStat,
|
||||
arma::mat>::template DualTreeTraverser,
|
||||
TreeType<metric::EuclideanDistance,
|
||||
kde::KDEStat,
|
||||
arma::mat>::template SingleTreeTraverser>;
|
||||
|
||||
/**
|
||||
* KernelNormalizer holds a set of methods to normalize estimations applying
|
||||
* in each case the appropiate kernel normalizer function.
|
||||
*/
|
||||
class KernelNormalizer
|
||||
{
|
||||
private:
|
||||
// SFINAE check if Normalizer function is present.
|
||||
HAS_MEM_FUNC(Normalizer, HasNormalizer);
|
||||
|
||||
public:
|
||||
//! Normalization not needed.
|
||||
template<typename KernelType>
|
||||
static void ApplyNormalizer(
|
||||
KernelType& /* kernel */,
|
||||
const size_t /* dimension */,
|
||||
arma::vec& /* estimations */,
|
||||
const typename std::enable_if<
|
||||
!HasNormalizer<KernelType, double(KernelType::*)(size_t)>::value>::
|
||||
type* = 0)
|
||||
{ return; }
|
||||
|
||||
//! Normalize kernels that have normalizer.
|
||||
template<typename KernelType>
|
||||
static void ApplyNormalizer(
|
||||
KernelType& kernel,
|
||||
const size_t dimension,
|
||||
arma::vec& estimations,
|
||||
const typename std::enable_if<
|
||||
HasNormalizer<KernelType, double(KernelType::*)(size_t)>::value>::
|
||||
type* = 0)
|
||||
{
|
||||
estimations /= kernel.Normalizer(dimension);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* DualMonoKDE computes a Kernel Density Estimation on the given KDEType.
|
||||
* It performs a monochromatic KDE.
|
||||
*/
|
||||
class DualMonoKDE : public boost::static_visitor<void>
|
||||
{
|
||||
private:
|
||||
//! Vector to store the KDE results.
|
||||
arma::vec& estimations;
|
||||
|
||||
public:
|
||||
//! Alias template necessary for Visual C++ compiler.
|
||||
template<typename KernelType,
|
||||
template<typename TreeMetricType,
|
||||
typename TreeStatType,
|
||||
typename TreeMatType> class TreeType>
|
||||
using KDETypeT = KDEType<KernelType, TreeType>;
|
||||
|
||||
//! Default DualMonoKDE on some KDEType.
|
||||
template<typename KernelType,
|
||||
template<typename TreeMetricType,
|
||||
typename TreeStatType,
|
||||
typename TreeMatType> class TreeType>
|
||||
void operator()(KDETypeT<KernelType, TreeType>* kde) const;
|
||||
|
||||
// TODO Implement specific cases where a leaf size can be selected.
|
||||
|
||||
//! DualMonoKDE constructor.
|
||||
DualMonoKDE(arma::vec& estimations);
|
||||
};
|
||||
|
||||
/**
|
||||
* DualBiKDE computes a Kernel Density Estimation on the given KDEType.
|
||||
* It performs a bichromatic KDE.
|
||||
*/
|
||||
class DualBiKDE : public boost::static_visitor<void>
|
||||
{
|
||||
private:
|
||||
//! Query set dimensionality.
|
||||
const size_t dimension;
|
||||
|
||||
//! The query set for the KDE.
|
||||
const arma::mat& querySet;
|
||||
|
||||
//! Vector to store the KDE results.
|
||||
arma::vec& estimations;
|
||||
|
||||
public:
|
||||
//! Alias template necessary for Visual C++ compiler.
|
||||
template<typename KernelType,
|
||||
template<typename TreeMetricType,
|
||||
typename TreeStatType,
|
||||
typename TreeMatType> class TreeType>
|
||||
using KDETypeT = KDEType<KernelType, TreeType>;
|
||||
|
||||
//! Default DualBiKDE on some KDEType.
|
||||
template<typename KernelType,
|
||||
template<typename TreeMetricType,
|
||||
typename TreeStatType,
|
||||
typename TreeMatType> class TreeType>
|
||||
void operator()(KDETypeT<KernelType, TreeType>* kde) const;
|
||||
|
||||
// TODO Implement specific cases where a leaf size can be selected.
|
||||
|
||||
//! DualBiKDE constructor. Takes ownership of the given querySet.
|
||||
DualBiKDE(arma::mat&& querySet, arma::vec& estimations);
|
||||
};
|
||||
|
||||
/**
|
||||
* TrainVisitor trains a given KDEType using a reference set.
|
||||
*/
|
||||
class TrainVisitor : public boost::static_visitor<void>
|
||||
{
|
||||
private:
|
||||
//! The reference set used for training.
|
||||
arma::mat&& referenceSet;
|
||||
|
||||
public:
|
||||
//! Default TrainVisitor on some KDEType.
|
||||
template<typename KernelType,
|
||||
template<typename TreeMetricType,
|
||||
typename TreeStatType,
|
||||
typename TreeMatType> class TreeType>
|
||||
void operator()(KDEType<KernelType, TreeType>* kde) const;
|
||||
|
||||
// TODO Implement specific cases where a leaf size can be selected.
|
||||
|
||||
//! TrainVisitor constructor. Takes ownership of the given referenceSet.
|
||||
TrainVisitor(arma::mat&& referenceSet);
|
||||
};
|
||||
|
||||
/**
|
||||
* ModeVisitor exposes the Mode() method of the KDEType.
|
||||
*/
|
||||
class ModeVisitor : public boost::static_visitor<KDEMode&>
|
||||
{
|
||||
public:
|
||||
//! Return mode of KDEType instance.
|
||||
template<typename KDEType>
|
||||
KDEMode& operator()(KDEType* kde) const;
|
||||
};
|
||||
|
||||
class DeleteVisitor : public boost::static_visitor<void>
|
||||
{
|
||||
public:
|
||||
//! Delete KDEType instance.
|
||||
template<typename KDEType>
|
||||
void operator()(KDEType* kde) const;
|
||||
};
|
||||
|
||||
class KDEModel
|
||||
{
|
||||
public:
|
||||
enum TreeTypes
|
||||
{
|
||||
KD_TREE,
|
||||
BALL_TREE,
|
||||
COVER_TREE,
|
||||
OCTREE,
|
||||
R_TREE
|
||||
};
|
||||
|
||||
enum KernelTypes
|
||||
{
|
||||
GAUSSIAN_KERNEL,
|
||||
EPANECHNIKOV_KERNEL,
|
||||
LAPLACIAN_KERNEL,
|
||||
SPHERICAL_KERNEL,
|
||||
TRIANGULAR_KERNEL
|
||||
};
|
||||
|
||||
private:
|
||||
//! Bandwidth of the kernel.
|
||||
double bandwidth;
|
||||
|
||||
//! Relative error tolerance.
|
||||
double relError;
|
||||
|
||||
//! Absolute error tolerance.
|
||||
double absError;
|
||||
|
||||
//! Type of kernel.
|
||||
KernelTypes kernelType;
|
||||
|
||||
//! Type of tree.
|
||||
TreeTypes treeType;
|
||||
|
||||
/**
|
||||
* kdeModel holds an instance of each possible combination of KernelType and
|
||||
* TreeType. It is initialized using BuildModel.
|
||||
*/
|
||||
boost::variant<KDEType<kernel::GaussianKernel, tree::KDTree>*,
|
||||
KDEType<kernel::GaussianKernel, tree::BallTree>*,
|
||||
KDEType<kernel::GaussianKernel, tree::StandardCoverTree>*,
|
||||
KDEType<kernel::GaussianKernel, tree::Octree>*,
|
||||
KDEType<kernel::GaussianKernel, tree::RTree>*,
|
||||
KDEType<kernel::EpanechnikovKernel, tree::KDTree>*,
|
||||
KDEType<kernel::EpanechnikovKernel, tree::BallTree>*,
|
||||
KDEType<kernel::EpanechnikovKernel, tree::StandardCoverTree>*,
|
||||
KDEType<kernel::EpanechnikovKernel, tree::Octree>*,
|
||||
KDEType<kernel::EpanechnikovKernel, tree::RTree>*,
|
||||
KDEType<kernel::LaplacianKernel, tree::KDTree>*,
|
||||
KDEType<kernel::LaplacianKernel, tree::BallTree>*,
|
||||
KDEType<kernel::LaplacianKernel, tree::StandardCoverTree>*,
|
||||
KDEType<kernel::LaplacianKernel, tree::Octree>*,
|
||||
KDEType<kernel::LaplacianKernel, tree::RTree>*,
|
||||
KDEType<kernel::SphericalKernel, tree::KDTree>*,
|
||||
KDEType<kernel::SphericalKernel, tree::BallTree>*,
|
||||
KDEType<kernel::SphericalKernel, tree::StandardCoverTree>*,
|
||||
KDEType<kernel::SphericalKernel, tree::Octree>*,
|
||||
KDEType<kernel::SphericalKernel, tree::RTree>*,
|
||||
KDEType<kernel::TriangularKernel, tree::KDTree>*,
|
||||
KDEType<kernel::TriangularKernel, tree::BallTree>*,
|
||||
KDEType<kernel::TriangularKernel, tree::StandardCoverTree>*,
|
||||
KDEType<kernel::TriangularKernel, tree::Octree>*,
|
||||
KDEType<kernel::TriangularKernel, tree::RTree>*> kdeModel;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Initialize KDEModel.
|
||||
*
|
||||
* @param bandwidth Bandwidth to use for the kernel.
|
||||
* @param relError Maximum relative error tolerance for each point in the
|
||||
* model. For example, 0.05 means that each value must be
|
||||
* within 5% of the true KDE value.
|
||||
* @param absError Maximum absolute error tolerance for each point in the
|
||||
* model. For example, 0.1 means that for each point the
|
||||
* value can have a maximum error of 0.1 units.
|
||||
* @param kernelType Type of kernel to use.
|
||||
* @param treeType Type of tree to use.
|
||||
*/
|
||||
KDEModel(const double bandwidth = 1.0,
|
||||
const double relError = 0.05,
|
||||
const double absError = 0,
|
||||
const KernelTypes kernelType = KernelTypes::GAUSSIAN_KERNEL,
|
||||
const TreeTypes treeType = TreeTypes::KD_TREE);
|
||||
|
||||
//! Copy constructor of the given model.
|
||||
KDEModel(const KDEModel& other);
|
||||
|
||||
//! Move constructor of the given model. Takes ownership of the model.
|
||||
KDEModel(KDEModel&& other);
|
||||
|
||||
/**
|
||||
* Copy the given model.
|
||||
*
|
||||
* Use std::move if the object to copy is no longer needed.
|
||||
*
|
||||
* @param other KDEModel to copy.
|
||||
*/
|
||||
KDEModel& operator=(KDEModel other);
|
||||
|
||||
//! Destroy the KDEModel object.
|
||||
~KDEModel();
|
||||
|
||||
//! Serialize the KDE model.
|
||||
template<typename Archive>
|
||||
void serialize(Archive& ar, const unsigned int /* version */);
|
||||
|
||||
//! Get the bandwidth of the kernel.
|
||||
double Bandwidth() const { return bandwidth; }
|
||||
|
||||
//! Modify the bandwidth of the kernel.
|
||||
double& Bandwidth() { return bandwidth; }
|
||||
|
||||
//! Get the relative error tolerance.
|
||||
double RelativeError() const { return relError; }
|
||||
|
||||
//! Modify the relative error tolerance.
|
||||
double& RelativeError() { return relError; }
|
||||
|
||||
//! Get the absolute error tolerance.
|
||||
double AbsoluteError() const { return absError; }
|
||||
|
||||
//! Modify the absolute error tolerance.
|
||||
double& AbsoluteError() { return absError; }
|
||||
|
||||
//! Get the tree type of the model.
|
||||
TreeTypes TreeType() const { return treeType; }
|
||||
|
||||
//! Modify the tree type of the model.
|
||||
TreeTypes& TreeType() { return treeType; }
|
||||
|
||||
//! Get the kernel type of the model.
|
||||
KernelTypes KernelType() const { return kernelType; }
|
||||
|
||||
//! Modify the kernel type of the model.
|
||||
KernelTypes& KernelType() { return kernelType; }
|
||||
|
||||
//! Get the mode of the model.
|
||||
KDEMode Mode() const;
|
||||
|
||||
//! Modify the mode of the model.
|
||||
KDEMode& Mode();
|
||||
|
||||
/**
|
||||
* Build the KDE model with the given parameters and then trains it with the
|
||||
* given reference data.
|
||||
* Takes possession of the reference set to avoid a copy, so the reference set
|
||||
* will not be usable after this.
|
||||
*
|
||||
* @param referenceSet Set of reference points.
|
||||
*/
|
||||
void BuildModel(arma::mat&& referenceSet);
|
||||
|
||||
/**
|
||||
* Perform kernel density estimation on the given query set.
|
||||
* Takes possession of the query set to avoid a copy, so the query set
|
||||
* will not be usable after this. If possible, it returns normalized
|
||||
* estimations.
|
||||
*
|
||||
* @pre The model has to be previously created with BuildModel.
|
||||
* @param querySet Set of query points.
|
||||
* @param estimations Vector where the results will be stored in the same
|
||||
* order as the query points.
|
||||
*/
|
||||
void Evaluate(arma::mat&& querySet, arma::vec& estimations);
|
||||
|
||||
/**
|
||||
* Perform kernel density estimation on the reference set.
|
||||
* If possible, it returns normalized estimations.
|
||||
*
|
||||
* @pre The model has to be previously created with BuildModel.
|
||||
* @param estimations Vector where the results will be stored in the same
|
||||
* order as the query points.
|
||||
*/
|
||||
void Evaluate(arma::vec& estimations);
|
||||
|
||||
|
||||
private:
|
||||
//! Clean memory.
|
||||
void CleanMemory();
|
||||
};
|
||||
|
||||
} // namespace kde
|
||||
} // namespace mlpack
|
||||
|
||||
#include "kde_model_impl.hpp"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,365 @@
|
||||
/**
|
||||
* @file kde_model_impl.hpp
|
||||
* @author Roberto Hueso
|
||||
*
|
||||
* Implementation of KDE Model.
|
||||
*
|
||||
* 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_KDE_MODEL_IMPL_HPP
|
||||
#define MLPACK_METHODS_KDE_MODEL_IMPL_HPP
|
||||
|
||||
// In case it hasn't been included yet.
|
||||
#include "kde_model.hpp"
|
||||
|
||||
#include <boost/serialization/variant.hpp>
|
||||
|
||||
namespace mlpack {
|
||||
namespace kde {
|
||||
|
||||
//! Initialize the KDEModel with the given parameters.
|
||||
inline KDEModel::KDEModel(const double bandwidth,
|
||||
const double relError,
|
||||
const double absError,
|
||||
const KernelTypes kernelType,
|
||||
const TreeTypes treeType) :
|
||||
bandwidth(bandwidth),
|
||||
relError(relError),
|
||||
absError(absError),
|
||||
kernelType(kernelType),
|
||||
treeType(treeType)
|
||||
{
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
// Copy constructor.
|
||||
inline KDEModel::KDEModel(const KDEModel& other) :
|
||||
bandwidth(other.bandwidth),
|
||||
relError(other.relError),
|
||||
absError(other.absError),
|
||||
kernelType(other.kernelType),
|
||||
treeType(other.treeType)
|
||||
{
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
// Move constructor.
|
||||
inline KDEModel::KDEModel(KDEModel&& other) :
|
||||
bandwidth(other.bandwidth),
|
||||
relError(other.relError),
|
||||
absError(other.absError),
|
||||
kernelType(other.kernelType),
|
||||
treeType(other.treeType),
|
||||
kdeModel(std::move(other.kdeModel))
|
||||
{
|
||||
// Reset other model.
|
||||
other.bandwidth = 1.0;
|
||||
other.relError = 0.05;
|
||||
other.absError = 0;
|
||||
other.kernelType = KernelTypes::GAUSSIAN_KERNEL;
|
||||
other.treeType = TreeTypes::KD_TREE;
|
||||
other.kdeModel = decltype(other.kdeModel)();
|
||||
}
|
||||
|
||||
inline KDEModel& KDEModel::operator=(KDEModel other)
|
||||
{
|
||||
boost::apply_visitor(DeleteVisitor(), kdeModel);
|
||||
bandwidth = other.bandwidth;
|
||||
relError = other.relError;
|
||||
absError = other.absError;
|
||||
kernelType = other.kernelType;
|
||||
treeType = other.treeType;
|
||||
kdeModel = std::move(other.kdeModel);
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Clean memory.
|
||||
inline KDEModel::~KDEModel()
|
||||
{
|
||||
boost::apply_visitor(DeleteVisitor(), kdeModel);
|
||||
}
|
||||
|
||||
inline void KDEModel::BuildModel(arma::mat&& referenceSet)
|
||||
{
|
||||
// Clean memory, if necessary.
|
||||
boost::apply_visitor(DeleteVisitor(), kdeModel);
|
||||
|
||||
// Build the actual model.
|
||||
if (kernelType == GAUSSIAN_KERNEL && treeType == KD_TREE)
|
||||
{
|
||||
kdeModel = new KDEType<kernel::GaussianKernel, tree::KDTree>
|
||||
(relError, absError, kernel::GaussianKernel(bandwidth));
|
||||
}
|
||||
else if (kernelType == GAUSSIAN_KERNEL && treeType == BALL_TREE)
|
||||
{
|
||||
kdeModel = new KDEType<kernel::GaussianKernel, tree::BallTree>
|
||||
(relError, absError, kernel::GaussianKernel(bandwidth));
|
||||
}
|
||||
else if (kernelType == GAUSSIAN_KERNEL && treeType == COVER_TREE)
|
||||
{
|
||||
kdeModel = new KDEType<kernel::GaussianKernel, tree::StandardCoverTree>
|
||||
(relError, absError, kernel::GaussianKernel(bandwidth));
|
||||
}
|
||||
else if (kernelType == GAUSSIAN_KERNEL && treeType == OCTREE)
|
||||
{
|
||||
kdeModel = new KDEType<kernel::GaussianKernel, tree::Octree>
|
||||
(relError, absError, kernel::GaussianKernel(bandwidth));
|
||||
}
|
||||
else if (kernelType == GAUSSIAN_KERNEL && treeType == R_TREE)
|
||||
{
|
||||
kdeModel = new KDEType<kernel::GaussianKernel, tree::RTree>
|
||||
(relError, absError, kernel::GaussianKernel(bandwidth));
|
||||
}
|
||||
else if (kernelType == EPANECHNIKOV_KERNEL && treeType == KD_TREE)
|
||||
{
|
||||
kdeModel = new KDEType<kernel::EpanechnikovKernel, tree::KDTree>
|
||||
(relError, absError, kernel::EpanechnikovKernel(bandwidth));
|
||||
}
|
||||
else if (kernelType == EPANECHNIKOV_KERNEL && treeType == BALL_TREE)
|
||||
{
|
||||
kdeModel = new KDEType<kernel::EpanechnikovKernel, tree::BallTree>
|
||||
(relError, absError, kernel::EpanechnikovKernel(bandwidth));
|
||||
}
|
||||
else if (kernelType == EPANECHNIKOV_KERNEL && treeType == COVER_TREE)
|
||||
{
|
||||
kdeModel = new KDEType<kernel::EpanechnikovKernel, tree::StandardCoverTree>
|
||||
(relError, absError, kernel::EpanechnikovKernel(bandwidth));
|
||||
}
|
||||
else if (kernelType == EPANECHNIKOV_KERNEL && treeType == OCTREE)
|
||||
{
|
||||
kdeModel = new KDEType<kernel::EpanechnikovKernel, tree::Octree>
|
||||
(relError, absError, kernel::EpanechnikovKernel(bandwidth));
|
||||
}
|
||||
else if (kernelType == EPANECHNIKOV_KERNEL && treeType == R_TREE)
|
||||
{
|
||||
kdeModel = new KDEType<kernel::EpanechnikovKernel, tree::RTree>
|
||||
(relError, absError, kernel::EpanechnikovKernel(bandwidth));
|
||||
}
|
||||
else if (kernelType == LAPLACIAN_KERNEL && treeType == KD_TREE)
|
||||
{
|
||||
kdeModel = new KDEType<kernel::LaplacianKernel, tree::KDTree>
|
||||
(relError, absError, kernel::LaplacianKernel(bandwidth));
|
||||
}
|
||||
else if (kernelType == LAPLACIAN_KERNEL && treeType == BALL_TREE)
|
||||
{
|
||||
kdeModel = new KDEType<kernel::LaplacianKernel, tree::BallTree>
|
||||
(relError, absError, kernel::LaplacianKernel(bandwidth));
|
||||
}
|
||||
else if (kernelType == LAPLACIAN_KERNEL && treeType == COVER_TREE)
|
||||
{
|
||||
kdeModel = new KDEType<kernel::LaplacianKernel, tree::StandardCoverTree>
|
||||
(relError, absError, kernel::LaplacianKernel(bandwidth));
|
||||
}
|
||||
else if (kernelType == LAPLACIAN_KERNEL && treeType == OCTREE)
|
||||
{
|
||||
kdeModel = new KDEType<kernel::LaplacianKernel, tree::Octree>
|
||||
(relError, absError, kernel::LaplacianKernel(bandwidth));
|
||||
}
|
||||
else if (kernelType == LAPLACIAN_KERNEL && treeType == R_TREE)
|
||||
{
|
||||
kdeModel = new KDEType<kernel::LaplacianKernel, tree::RTree>
|
||||
(relError, absError, kernel::LaplacianKernel(bandwidth));
|
||||
}
|
||||
else if (kernelType == SPHERICAL_KERNEL && treeType == KD_TREE)
|
||||
{
|
||||
kdeModel = new KDEType<kernel::SphericalKernel, tree::KDTree>
|
||||
(relError, absError, kernel::SphericalKernel(bandwidth));
|
||||
}
|
||||
else if (kernelType == SPHERICAL_KERNEL && treeType == BALL_TREE)
|
||||
{
|
||||
kdeModel = new KDEType<kernel::SphericalKernel, tree::BallTree>
|
||||
(relError, absError, kernel::SphericalKernel(bandwidth));
|
||||
}
|
||||
else if (kernelType == SPHERICAL_KERNEL && treeType == COVER_TREE)
|
||||
{
|
||||
kdeModel = new KDEType<kernel::SphericalKernel, tree::StandardCoverTree>
|
||||
(relError, absError, kernel::SphericalKernel(bandwidth));
|
||||
}
|
||||
else if (kernelType == SPHERICAL_KERNEL && treeType == OCTREE)
|
||||
{
|
||||
kdeModel = new KDEType<kernel::SphericalKernel, tree::Octree>
|
||||
(relError, absError, kernel::SphericalKernel(bandwidth));
|
||||
}
|
||||
else if (kernelType == SPHERICAL_KERNEL && treeType == R_TREE)
|
||||
{
|
||||
kdeModel = new KDEType<kernel::SphericalKernel, tree::RTree>
|
||||
(relError, absError, kernel::SphericalKernel(bandwidth));
|
||||
}
|
||||
else if (kernelType == TRIANGULAR_KERNEL && treeType == KD_TREE)
|
||||
{
|
||||
kdeModel = new KDEType<kernel::TriangularKernel, tree::KDTree>
|
||||
(relError, absError, kernel::TriangularKernel(bandwidth));
|
||||
}
|
||||
else if (kernelType == TRIANGULAR_KERNEL && treeType == BALL_TREE)
|
||||
{
|
||||
kdeModel = new KDEType<kernel::TriangularKernel, tree::BallTree>
|
||||
(relError, absError, kernel::TriangularKernel(bandwidth));
|
||||
}
|
||||
else if (kernelType == TRIANGULAR_KERNEL && treeType == COVER_TREE)
|
||||
{
|
||||
kdeModel = new KDEType<kernel::TriangularKernel, tree::StandardCoverTree>
|
||||
(relError, absError, kernel::TriangularKernel(bandwidth));
|
||||
}
|
||||
else if (kernelType == TRIANGULAR_KERNEL && treeType == OCTREE)
|
||||
{
|
||||
kdeModel = new KDEType<kernel::TriangularKernel, tree::Octree>
|
||||
(relError, absError, kernel::TriangularKernel(bandwidth));
|
||||
}
|
||||
else if (kernelType == TRIANGULAR_KERNEL && treeType == R_TREE)
|
||||
{
|
||||
kdeModel = new KDEType<kernel::TriangularKernel, tree::RTree>
|
||||
(relError, absError, kernel::TriangularKernel(bandwidth));
|
||||
}
|
||||
|
||||
// Train the model.
|
||||
TrainVisitor train(std::move(referenceSet));
|
||||
boost::apply_visitor(train, kdeModel);
|
||||
}
|
||||
|
||||
// Perform bichromatic evaluation.
|
||||
inline void KDEModel::Evaluate(arma::mat&& querySet, arma::vec& estimations)
|
||||
{
|
||||
Log::Info << "Evaluating KDE..." << std::endl;
|
||||
DualBiKDE eval(std::move(querySet), estimations);
|
||||
boost::apply_visitor(eval, kdeModel);
|
||||
}
|
||||
|
||||
// Perform monochromatic evaluation.
|
||||
inline void KDEModel::Evaluate(arma::vec& estimations)
|
||||
{
|
||||
Log::Info << "Evaluating KDE..." << std::endl;
|
||||
DualMonoKDE eval(estimations);
|
||||
boost::apply_visitor(eval, kdeModel);
|
||||
}
|
||||
|
||||
// Clean memory.
|
||||
inline void KDEModel::CleanMemory()
|
||||
{
|
||||
boost::apply_visitor(DeleteVisitor(), kdeModel);
|
||||
}
|
||||
|
||||
// Parameters for KDE evaluation.
|
||||
DualMonoKDE::DualMonoKDE(arma::vec& estimations):
|
||||
estimations(estimations)
|
||||
{}
|
||||
|
||||
// Default KDE evaluation.
|
||||
template<typename KernelType,
|
||||
template<typename TreeMetricType,
|
||||
typename TreeStatType,
|
||||
typename TreeMatType> class TreeType>
|
||||
void DualMonoKDE::operator()(KDETypeT<KernelType, TreeType>* kde) const
|
||||
{
|
||||
if (kde)
|
||||
{
|
||||
kde->Evaluate(estimations);
|
||||
const size_t dimension = (kde->ReferenceTree())->Dataset().n_rows;
|
||||
KernelNormalizer::ApplyNormalizer<KernelType>(kde->Kernel(),
|
||||
dimension,
|
||||
estimations);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("no KDE model initialized");
|
||||
}
|
||||
}
|
||||
|
||||
// Parameters for KDE evaluation.
|
||||
DualBiKDE::DualBiKDE(arma::mat&& querySet, arma::vec& estimations):
|
||||
dimension(querySet.n_rows),
|
||||
querySet(std::move(querySet)),
|
||||
estimations(estimations)
|
||||
{}
|
||||
|
||||
// Default KDE evaluation.
|
||||
template<typename KernelType,
|
||||
template<typename TreeMetricType,
|
||||
typename TreeStatType,
|
||||
typename TreeMatType> class TreeType>
|
||||
void DualBiKDE::operator()(KDETypeT<KernelType, TreeType>* kde) const
|
||||
{
|
||||
if (kde)
|
||||
{
|
||||
kde->Evaluate(std::move(querySet), estimations);
|
||||
KernelNormalizer::ApplyNormalizer<KernelType>(kde->Kernel(),
|
||||
dimension,
|
||||
estimations);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("no KDE model initialized");
|
||||
}
|
||||
}
|
||||
|
||||
// Parameters for Train.
|
||||
TrainVisitor::TrainVisitor(arma::mat&& referenceSet) :
|
||||
referenceSet(std::move(referenceSet))
|
||||
{}
|
||||
|
||||
// Default Train.
|
||||
template<typename KernelType,
|
||||
template<typename TreeMetricType,
|
||||
typename TreeStatType,
|
||||
typename TreeMatType> class TreeType>
|
||||
void TrainVisitor::operator()(KDEType<KernelType, TreeType>* kde) const
|
||||
{
|
||||
Log::Info << "Training KDE model..." << std::endl;
|
||||
if (kde)
|
||||
kde->Train(std::move(referenceSet));
|
||||
else
|
||||
throw std::runtime_error("no KDE model initialized");
|
||||
}
|
||||
|
||||
// Delete model.
|
||||
template<typename KDEType>
|
||||
void DeleteVisitor::operator()(KDEType* kde) const
|
||||
{
|
||||
if (kde)
|
||||
delete kde;
|
||||
}
|
||||
|
||||
// Mode of model.
|
||||
template<typename KDEType>
|
||||
KDEMode& ModeVisitor::operator()(KDEType* kde) const
|
||||
{
|
||||
if (kde)
|
||||
return kde->Mode();
|
||||
else
|
||||
throw std::runtime_error("no KDE model initialized");
|
||||
}
|
||||
|
||||
// Get mode of model.
|
||||
KDEMode KDEModel::Mode() const
|
||||
{
|
||||
return boost::apply_visitor(ModeVisitor(), kdeModel);
|
||||
}
|
||||
|
||||
// Modify mode of model.
|
||||
KDEMode& KDEModel::Mode()
|
||||
{
|
||||
return boost::apply_visitor(ModeVisitor(), kdeModel);
|
||||
}
|
||||
|
||||
// Serialize the model.
|
||||
template<typename Archive>
|
||||
void KDEModel::serialize(Archive& ar, const unsigned int /* version */)
|
||||
{
|
||||
ar & BOOST_SERIALIZATION_NVP(bandwidth);
|
||||
ar & BOOST_SERIALIZATION_NVP(relError);
|
||||
ar & BOOST_SERIALIZATION_NVP(absError);
|
||||
ar & BOOST_SERIALIZATION_NVP(kernelType);
|
||||
ar & BOOST_SERIALIZATION_NVP(treeType);
|
||||
|
||||
if (Archive::is_loading::value)
|
||||
boost::apply_visitor(DeleteVisitor(), kdeModel);
|
||||
|
||||
ar & BOOST_SERIALIZATION_NVP(kdeModel);
|
||||
}
|
||||
|
||||
} // namespace kde
|
||||
} // namespace mlpack
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,139 @@
|
||||
/**
|
||||
* @file kde_rules.hpp
|
||||
* @author Roberto Hueso
|
||||
*
|
||||
* Rules for Kernel Density Estimation, so that it can be done with arbitrary
|
||||
* tree types.
|
||||
*
|
||||
* 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_KDE_RULES_HPP
|
||||
#define MLPACK_METHODS_KDE_RULES_HPP
|
||||
|
||||
#include <mlpack/core/tree/traversal_info.hpp>
|
||||
|
||||
namespace mlpack {
|
||||
namespace kde {
|
||||
|
||||
/**
|
||||
* A dual-tree traversal Rules class for kernel density estimation. This
|
||||
* contains the Score() and BaseCase() implementations.
|
||||
*/
|
||||
template<typename MetricType, typename KernelType, typename TreeType>
|
||||
class KDERules
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Construct KDERules.
|
||||
*
|
||||
* @param referenceSet Reference set data.
|
||||
* @param querySet Query set data.
|
||||
* @param densities Vector where estimations will be written.
|
||||
* @param relError Relative error tolerance.
|
||||
* @param absError Absolute error tolerance.
|
||||
* @param metric Instantiated metric.
|
||||
* @param kernel Instantiated kernel.
|
||||
* @param sameSet True if query and reference sets are the same
|
||||
* (monochromatic evaluation).
|
||||
*/
|
||||
KDERules(const arma::mat& referenceSet,
|
||||
const arma::mat& querySet,
|
||||
arma::vec& densities,
|
||||
const double relError,
|
||||
const double absError,
|
||||
MetricType& metric,
|
||||
KernelType& kernel,
|
||||
const bool sameSet);
|
||||
|
||||
//! Base Case.
|
||||
double BaseCase(const size_t queryIndex, const size_t referenceIndex);
|
||||
|
||||
//! SingleTree Rescore.
|
||||
double Score(const size_t queryIndex, TreeType& referenceNode);
|
||||
|
||||
//! SingleTree Score.
|
||||
double Rescore(const size_t queryIndex,
|
||||
TreeType& referenceNode,
|
||||
const double oldScore) const;
|
||||
|
||||
//! DoubleTree Score.
|
||||
double Score(TreeType& queryNode, TreeType& referenceNode);
|
||||
|
||||
//! DoubleTree Rescore.
|
||||
double Rescore(TreeType& queryNode,
|
||||
TreeType& referenceNode,
|
||||
const double oldScore) const;
|
||||
|
||||
typedef typename tree::TraversalInfo<TreeType> TraversalInfoType;
|
||||
|
||||
//! Get traversal information.
|
||||
const TraversalInfoType& TraversalInfo() const { return traversalInfo; }
|
||||
|
||||
//! Modify traversal information.
|
||||
TraversalInfoType& TraversalInfo() { return traversalInfo; }
|
||||
|
||||
//! Get the number of base cases.
|
||||
size_t BaseCases() const { return baseCases; }
|
||||
|
||||
//! Get the number of scores.
|
||||
size_t Scores() const { return scores; }
|
||||
|
||||
private:
|
||||
//! Evaluate kernel value of 2 points given their indexes.
|
||||
double EvaluateKernel(const size_t queryIndex,
|
||||
const size_t referenceIndex) const;
|
||||
|
||||
//! Evaluate kernel value of 2 points.
|
||||
double EvaluateKernel(const arma::vec& query,
|
||||
const arma::vec& reference) const;
|
||||
|
||||
//! The reference set.
|
||||
const arma::mat& referenceSet;
|
||||
|
||||
//! The query set.
|
||||
const arma::mat& querySet;
|
||||
|
||||
//! Density values.
|
||||
arma::vec& densities;
|
||||
|
||||
//! Absolute error tolerance.
|
||||
const double absError;
|
||||
|
||||
//! Relatve error tolerance.
|
||||
const double relError;
|
||||
|
||||
//! Instantiated metric.
|
||||
MetricType& metric;
|
||||
|
||||
//! Instantiated kernel.
|
||||
KernelType& kernel;
|
||||
|
||||
//! Whether reference and query sets are the same.
|
||||
const bool sameSet;
|
||||
|
||||
//! The last query index.
|
||||
size_t lastQueryIndex;
|
||||
|
||||
//! The last reference index.
|
||||
size_t lastReferenceIndex;
|
||||
|
||||
//! Traversal information.
|
||||
TraversalInfoType traversalInfo;
|
||||
|
||||
//! The number of base cases.
|
||||
size_t baseCases;
|
||||
|
||||
//! The number of scores.
|
||||
size_t scores;
|
||||
};
|
||||
|
||||
} // namespace kde
|
||||
} // namespace mlpack
|
||||
|
||||
// Include implementation.
|
||||
#include "kde_rules_impl.hpp"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,246 @@
|
||||
/**
|
||||
* @file kde_rules_impl.hpp
|
||||
* @author Roberto Hueso
|
||||
*
|
||||
* Implementation of rules for Kernel Density Estimation with generic trees.
|
||||
*
|
||||
* 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_KDE_RULES_IMPL_HPP
|
||||
#define MLPACK_METHODS_KDE_RULES_IMPL_HPP
|
||||
|
||||
// In case it hasn't been included yet.
|
||||
#include "kde_rules.hpp"
|
||||
|
||||
namespace mlpack {
|
||||
namespace kde {
|
||||
|
||||
template<typename MetricType, typename KernelType, typename TreeType>
|
||||
KDERules<MetricType, KernelType, TreeType>::KDERules(
|
||||
const arma::mat& referenceSet,
|
||||
const arma::mat& querySet,
|
||||
arma::vec& densities,
|
||||
const double relError,
|
||||
const double absError,
|
||||
MetricType& metric,
|
||||
KernelType& kernel,
|
||||
const bool sameSet) :
|
||||
referenceSet(referenceSet),
|
||||
querySet(querySet),
|
||||
densities(densities),
|
||||
absError(absError),
|
||||
relError(relError),
|
||||
metric(metric),
|
||||
kernel(kernel),
|
||||
sameSet(sameSet),
|
||||
lastQueryIndex(querySet.n_cols),
|
||||
lastReferenceIndex(referenceSet.n_cols),
|
||||
baseCases(0),
|
||||
scores(0)
|
||||
{
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
//! The base case.
|
||||
template<typename MetricType, typename KernelType, typename TreeType>
|
||||
inline force_inline
|
||||
double KDERules<MetricType, KernelType, TreeType>::BaseCase(
|
||||
const size_t queryIndex,
|
||||
const size_t referenceIndex)
|
||||
{
|
||||
// If reference and query sets are the same we don't want to compute the
|
||||
// estimation of a point with itself.
|
||||
if (sameSet && (queryIndex == referenceIndex))
|
||||
return 0.0;
|
||||
|
||||
// Avoid duplicated calculations.
|
||||
if ((lastQueryIndex == queryIndex) && (lastReferenceIndex == referenceIndex))
|
||||
return 0.0;
|
||||
|
||||
// Calculations.
|
||||
const double distance = metric.Evaluate(querySet.col(queryIndex),
|
||||
referenceSet.col(referenceIndex));
|
||||
densities(queryIndex) += kernel.Evaluate(distance);
|
||||
|
||||
++baseCases;
|
||||
lastQueryIndex = queryIndex;
|
||||
lastReferenceIndex = referenceIndex;
|
||||
return distance;
|
||||
}
|
||||
|
||||
//! Single-tree scoring function.
|
||||
template<typename MetricType, typename KernelType, typename TreeType>
|
||||
inline double KDERules<MetricType, KernelType, TreeType>::
|
||||
Score(const size_t queryIndex, TreeType& referenceNode)
|
||||
{
|
||||
double score, maxKernel, minKernel, bound;
|
||||
const arma::vec& queryPoint = querySet.unsafe_col(queryIndex);
|
||||
const double minDistance = referenceNode.MinDistance(queryPoint);
|
||||
bool newCalculations = true;
|
||||
|
||||
if (tree::TreeTraits<TreeType>::FirstPointIsCentroid &&
|
||||
lastQueryIndex == queryIndex &&
|
||||
traversalInfo.LastReferenceNode() != NULL &&
|
||||
traversalInfo.LastReferenceNode()->Point(0) == referenceNode.Point(0))
|
||||
{
|
||||
// Don't duplicate calculations.
|
||||
newCalculations = false;
|
||||
lastQueryIndex = queryIndex;
|
||||
lastReferenceIndex = referenceNode.Point(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Calculations are new.
|
||||
maxKernel = kernel.Evaluate(minDistance);
|
||||
minKernel = kernel.Evaluate(referenceNode.MaxDistance(queryPoint));
|
||||
bound = maxKernel - minKernel;
|
||||
}
|
||||
|
||||
if (newCalculations &&
|
||||
bound <= (absError + relError * minKernel) / referenceSet.n_cols)
|
||||
{
|
||||
// Estimate values.
|
||||
double kernelValue;
|
||||
|
||||
// Calculate kernel value based on reference node centroid.
|
||||
if (tree::TreeTraits<TreeType>::FirstPointIsCentroid)
|
||||
{
|
||||
kernelValue = EvaluateKernel(queryIndex, referenceNode.Point(0));
|
||||
}
|
||||
else
|
||||
{
|
||||
kde::KDEStat& referenceStat = referenceNode.Stat();
|
||||
kernelValue = EvaluateKernel(queryPoint, referenceStat.Centroid());
|
||||
}
|
||||
|
||||
densities(queryIndex) += referenceNode.NumDescendants() * kernelValue;
|
||||
|
||||
// Don't explore this tree branch.
|
||||
score = DBL_MAX;
|
||||
}
|
||||
else
|
||||
{
|
||||
score = minDistance;
|
||||
}
|
||||
|
||||
++scores;
|
||||
traversalInfo.LastReferenceNode() = &referenceNode;
|
||||
traversalInfo.LastScore() = score;
|
||||
return score;
|
||||
}
|
||||
|
||||
template<typename MetricType, typename KernelType, typename TreeType>
|
||||
inline double KDERules<MetricType, KernelType, TreeType>::Rescore(
|
||||
const size_t /* queryIndex */,
|
||||
TreeType& /* referenceNode */,
|
||||
const double oldScore) const
|
||||
{
|
||||
// If it's pruned it continues to be pruned.
|
||||
return oldScore;
|
||||
}
|
||||
|
||||
//! Double-tree scoring function.
|
||||
template<typename MetricType, typename KernelType, typename TreeType>
|
||||
inline double KDERules<MetricType, KernelType, TreeType>::
|
||||
Score(TreeType& queryNode, TreeType& referenceNode)
|
||||
{
|
||||
double score, maxKernel, minKernel, bound;
|
||||
const double minDistance = queryNode.MinDistance(referenceNode);
|
||||
// Calculations are not duplicated.
|
||||
bool newCalculations = true;
|
||||
|
||||
if (tree::TreeTraits<TreeType>::FirstPointIsCentroid &&
|
||||
(traversalInfo.LastQueryNode() != NULL) &&
|
||||
(traversalInfo.LastReferenceNode() != NULL) &&
|
||||
(traversalInfo.LastQueryNode()->Point(0) == queryNode.Point(0)) &&
|
||||
(traversalInfo.LastReferenceNode()->Point(0) == referenceNode.Point(0)))
|
||||
{
|
||||
// Don't duplicate calculations.
|
||||
newCalculations = false;
|
||||
lastQueryIndex = queryNode.Point(0);
|
||||
lastReferenceIndex = referenceNode.Point(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Calculations are new.
|
||||
maxKernel = kernel.Evaluate(minDistance);
|
||||
minKernel = kernel.Evaluate(queryNode.MaxDistance(referenceNode));
|
||||
bound = maxKernel - minKernel;
|
||||
}
|
||||
|
||||
// If possible, avoid some calculations because of the error tolerance.
|
||||
if (newCalculations &&
|
||||
bound <= (absError + relError * minKernel) / referenceSet.n_cols)
|
||||
{
|
||||
// Auxiliary variables.
|
||||
double kernelValue;
|
||||
kde::KDEStat& referenceStat = referenceNode.Stat();
|
||||
kde::KDEStat& queryStat = queryNode.Stat();
|
||||
|
||||
// If calculating a center is not required.
|
||||
if (tree::TreeTraits<TreeType>::FirstPointIsCentroid)
|
||||
{
|
||||
kernelValue = EvaluateKernel(queryNode.Point(0), referenceNode.Point(0));
|
||||
}
|
||||
// Sadly, we have no choice but to calculate the center.
|
||||
else
|
||||
{
|
||||
kernelValue = EvaluateKernel(queryStat.Centroid(),
|
||||
referenceStat.Centroid());
|
||||
}
|
||||
|
||||
// Sum up estimations.
|
||||
for (size_t i = 0; i < queryNode.NumDescendants(); ++i)
|
||||
{
|
||||
densities(queryNode.Descendant(i)) +=
|
||||
referenceNode.NumDescendants() * kernelValue;
|
||||
}
|
||||
score = DBL_MAX;
|
||||
}
|
||||
else
|
||||
{
|
||||
score = minDistance;
|
||||
}
|
||||
|
||||
++scores;
|
||||
traversalInfo.LastQueryNode() = &queryNode;
|
||||
traversalInfo.LastReferenceNode() = &referenceNode;
|
||||
traversalInfo.LastScore() = score;
|
||||
return score;
|
||||
}
|
||||
|
||||
//! Double-tree rescore.
|
||||
template<typename MetricType, typename KernelType, typename TreeType>
|
||||
inline double KDERules<MetricType, KernelType, TreeType>::
|
||||
Rescore(TreeType& /*queryNode*/,
|
||||
TreeType& /*referenceNode*/,
|
||||
const double oldScore) const
|
||||
{
|
||||
// If a branch is pruned then it continues to be pruned.
|
||||
return oldScore;
|
||||
}
|
||||
|
||||
template<typename MetricType, typename KernelType, typename TreeType>
|
||||
inline force_inline double KDERules<MetricType, KernelType, TreeType>::
|
||||
EvaluateKernel(const size_t queryIndex,
|
||||
const size_t referenceIndex) const
|
||||
{
|
||||
return EvaluateKernel(querySet.unsafe_col(queryIndex),
|
||||
referenceSet.unsafe_col(referenceIndex));
|
||||
}
|
||||
|
||||
template<typename MetricType, typename KernelType, typename TreeType>
|
||||
inline force_inline double KDERules<MetricType, KernelType, TreeType>::
|
||||
EvaluateKernel(const arma::vec& query, const arma::vec& reference) const
|
||||
{
|
||||
return kernel.Evaluate(metric.Evaluate(query, reference));
|
||||
}
|
||||
|
||||
} // namespace kde
|
||||
} // namespace mlpack
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* @file kde_stat.hpp
|
||||
* @author Roberto Hueso
|
||||
*
|
||||
* Defines TreeStatType for KDE.
|
||||
*
|
||||
* 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_KDE_STAT_HPP
|
||||
#define MLPACK_METHODS_KDE_STAT_HPP
|
||||
|
||||
#include <mlpack/prereqs.hpp>
|
||||
|
||||
namespace mlpack {
|
||||
namespace kde {
|
||||
|
||||
/**
|
||||
* Extra data for each node in the tree for the task of kernel density
|
||||
* estimation.
|
||||
*/
|
||||
class KDEStat
|
||||
{
|
||||
public:
|
||||
//! Initialize the statistic.
|
||||
KDEStat() : validCentroid(false) { }
|
||||
|
||||
//! Initialization for a fully initialized node.
|
||||
template<typename TreeType>
|
||||
KDEStat(TreeType& node)
|
||||
{
|
||||
// Calculate centroid if necessary.
|
||||
if (!tree::TreeTraits<TreeType>::FirstPointIsCentroid)
|
||||
{
|
||||
node.Center(centroid);
|
||||
validCentroid = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
validCentroid = false;
|
||||
}
|
||||
}
|
||||
|
||||
//! Get the centroid of the node.
|
||||
inline const arma::vec& Centroid() const
|
||||
{
|
||||
if (validCentroid)
|
||||
return centroid;
|
||||
throw std::logic_error("Centroid must be assigned before requesting its "
|
||||
"value");
|
||||
}
|
||||
|
||||
//! Modify the centroid of the node.
|
||||
void SetCentroid(arma::vec newCentroid)
|
||||
{
|
||||
validCentroid = true;
|
||||
centroid = std::move(newCentroid);
|
||||
}
|
||||
|
||||
//! Get whether the centroid is valid.
|
||||
inline bool ValidCentroid() const { return validCentroid; }
|
||||
|
||||
//! Serialize the statistic to/from an archive.
|
||||
template<typename Archive>
|
||||
void serialize(Archive& ar, const unsigned int /* version */)
|
||||
{
|
||||
ar & BOOST_SERIALIZATION_NVP(centroid);
|
||||
ar & BOOST_SERIALIZATION_NVP(validCentroid);
|
||||
}
|
||||
|
||||
private:
|
||||
//! Node centroid.
|
||||
arma::vec centroid;
|
||||
|
||||
//! Whether the centroid is updated or is junk.
|
||||
bool validCentroid;
|
||||
};
|
||||
|
||||
} // namespace kde
|
||||
} // namespace mlpack
|
||||
|
||||
#endif
|
||||
@@ -95,6 +95,41 @@ PARAM_STRING_IN("update_rules", "Update rules for each iteration; ( multdist | "
|
||||
PARAM_MATRIX_IN("initial_w", "Initial W matrix.", "p");
|
||||
PARAM_MATRIX_IN("initial_h", "Initial H matrix.", "q");
|
||||
|
||||
void LoadInitialWH(const bool bindingTransposed, arma::mat& w, arma::mat& h)
|
||||
{
|
||||
// Note that these datasets will typically be transposed on load, since we are
|
||||
// likely receiving it from a row-major language, but we get it in a
|
||||
// column-major form. Therefore, we're actually decomposing V^T = W^T * H^T.
|
||||
// Effectively this means we are solving, for the user, V = H*W. Therefore,
|
||||
// we actually have to switch what we are saving, so we will save the W we get
|
||||
// from amf.Apply() as H, and vice versa.
|
||||
if (bindingTransposed)
|
||||
{
|
||||
w = CLI::GetParam<arma::mat>("initial_h");
|
||||
h = CLI::GetParam<arma::mat>("initial_w");
|
||||
}
|
||||
else
|
||||
{
|
||||
h = CLI::GetParam<arma::mat>("initial_h");
|
||||
w = CLI::GetParam<arma::mat>("initial_w");
|
||||
}
|
||||
}
|
||||
|
||||
void SaveWH(const bool bindingTransposed, arma::mat&& w, arma::mat&& h)
|
||||
{
|
||||
// The same transposition applies when saving.
|
||||
if (bindingTransposed)
|
||||
{
|
||||
CLI::GetParam<arma::mat>("w") = std::move(h);
|
||||
CLI::GetParam<arma::mat>("h") = std::move(w);
|
||||
}
|
||||
else
|
||||
{
|
||||
CLI::GetParam<arma::mat>("h") = std::move(h);
|
||||
CLI::GetParam<arma::mat>("w") = std::move(w);
|
||||
}
|
||||
}
|
||||
|
||||
static void mlpackMain()
|
||||
{
|
||||
// Initialize random seed.
|
||||
@@ -120,7 +155,8 @@ static void mlpackMain()
|
||||
RequireAtLeastOnePassed({ "h", "w" }, false, "no output will be saved");
|
||||
RequireNoneOrAllPassed({"initial_w", "initial_h"}, true);
|
||||
|
||||
// Load input dataset.
|
||||
// Load input dataset. We know if the data is transposed based on the
|
||||
// BINDING_MATRIX_TRANSPOSED macro, which will be 'true' or 'false'.
|
||||
arma::mat V = std::move(CLI::GetParam<arma::mat>("input"));
|
||||
|
||||
arma::mat W;
|
||||
@@ -136,9 +172,10 @@ static void mlpackMain()
|
||||
if (CLI::HasParam("initial_w"))
|
||||
{
|
||||
// Initialization with given W, H matrices.
|
||||
GivenInitialization ginit = GivenInitialization(
|
||||
std::move(CLI::GetParam<arma::mat>("initial_w")),
|
||||
std::move(CLI::GetParam<arma::mat>("initial_h")));
|
||||
arma::mat initialW, initialH;
|
||||
LoadInitialWH(BINDING_MATRIX_TRANSPOSED, initialW, initialH);
|
||||
GivenInitialization ginit = GivenInitialization(initialW, initialH);
|
||||
|
||||
AMF<SimpleResidueTermination,
|
||||
GivenInitialization> amf(srt, ginit);
|
||||
amf.Apply(V, r, W, H);
|
||||
@@ -158,9 +195,10 @@ static void mlpackMain()
|
||||
if (CLI::HasParam("initial_w"))
|
||||
{
|
||||
// Initialization with given W, H matrices.
|
||||
GivenInitialization ginit = GivenInitialization(
|
||||
std::move(CLI::GetParam<arma::mat>("initial_w")),
|
||||
std::move(CLI::GetParam<arma::mat>("initial_h")));
|
||||
arma::mat initialW, initialH;
|
||||
LoadInitialWH(BINDING_MATRIX_TRANSPOSED, initialW, initialH);
|
||||
GivenInitialization ginit = GivenInitialization(initialW, initialH);
|
||||
|
||||
AMF<SimpleResidueTermination,
|
||||
GivenInitialization,
|
||||
NMFMultiplicativeDivergenceUpdate> amf(srt, ginit);
|
||||
@@ -183,9 +221,10 @@ static void mlpackMain()
|
||||
if (CLI::HasParam("initial_w"))
|
||||
{
|
||||
// Initialization with given W, H matrices.
|
||||
GivenInitialization ginit = GivenInitialization(
|
||||
std::move(CLI::GetParam<arma::mat>("initial_w")),
|
||||
std::move(CLI::GetParam<arma::mat>("initial_h")));
|
||||
arma::mat initialW, initialH;
|
||||
LoadInitialWH(BINDING_MATRIX_TRANSPOSED, initialW, initialH);
|
||||
GivenInitialization ginit = GivenInitialization(initialW, initialH);
|
||||
|
||||
AMF<SimpleResidueTermination,
|
||||
GivenInitialization,
|
||||
NMFALSUpdate> amf(srt, ginit);
|
||||
@@ -200,9 +239,7 @@ static void mlpackMain()
|
||||
}
|
||||
}
|
||||
|
||||
// Save results.
|
||||
if (CLI::HasParam("w"))
|
||||
CLI::GetParam<arma::mat>("w") = std::move(W);
|
||||
if (CLI::HasParam("h"))
|
||||
CLI::GetParam<arma::mat>("h") = std::move(H);
|
||||
// Save results. Remember from our discussion in the comments earlier that we
|
||||
// may need to switch the names of the outputs.
|
||||
SaveWH(BINDING_MATRIX_TRANSPOSED, std::move(W), std::move(H));
|
||||
}
|
||||
|
||||
@@ -59,9 +59,13 @@ void NystroemMethod<KernelType, PointSelectionPolicy>::GetKernelMatrix(
|
||||
{
|
||||
// Assemble mini-kernel matrix.
|
||||
for (size_t i = 0; i < rank; ++i)
|
||||
{
|
||||
for (size_t j = 0; j < rank; ++j)
|
||||
{
|
||||
miniKernel(i, j) = kernel.Evaluate(data.col(selectedPoints(i)),
|
||||
data.col(selectedPoints(j)));
|
||||
}
|
||||
}
|
||||
|
||||
// Construct semi-kernel matrix with interactions between selected points and
|
||||
// all points.
|
||||
@@ -85,8 +89,13 @@ void NystroemMethod<KernelType, PointSelectionPolicy>::Apply(arma::mat& output)
|
||||
arma::vec s;
|
||||
arma::svd(U, s, V, miniKernel);
|
||||
|
||||
// Construct the output matrix.
|
||||
// Construct the output matrix. We need to have special handling when
|
||||
// miniKernel ended up being low-rank.
|
||||
arma::mat normalization = arma::diagmat(1.0 / sqrt(s));
|
||||
for (size_t i = 0; i < s.n_elem; ++i)
|
||||
if (std::abs(s[i]) <= 1e-20)
|
||||
normalization(i, i) = 0.0;
|
||||
|
||||
output = semiKernel * U * normalization * V;
|
||||
}
|
||||
|
||||
|
||||
@@ -213,7 +213,7 @@ static void mlpackMain()
|
||||
rs = CLI::GetParam<RSModel*>("input_model");
|
||||
|
||||
Log::Info << "Using range search model from '"
|
||||
<< CLI::GetPrintableParam<RSModel>("input_model") << "' ("
|
||||
<< CLI::GetPrintableParam<RSModel*>("input_model") << "' ("
|
||||
<< "trained on " << rs->Dataset().n_rows << "x" << rs->Dataset().n_cols
|
||||
<< " dataset)." << endl;
|
||||
|
||||
|
||||
@@ -271,11 +271,11 @@ void MonoSearchVisitor::operator()(RSType* rs) const
|
||||
}
|
||||
|
||||
//! Save parameters for bichromatic range search.
|
||||
BiSearchVisitor::BiSearchVisitor(const arma::mat& querySet,
|
||||
const math::Range& range,
|
||||
std::vector<std::vector<size_t>>& neighbors,
|
||||
std::vector<std::vector<double>>& distances,
|
||||
const size_t leafSize):
|
||||
inline BiSearchVisitor::BiSearchVisitor(const arma::mat& querySet,
|
||||
const math::Range& range,
|
||||
std::vector<std::vector<size_t>>& neighbors,
|
||||
std::vector<std::vector<double>>& distances,
|
||||
const size_t leafSize):
|
||||
querySet(querySet),
|
||||
range(range),
|
||||
neighbors(neighbors),
|
||||
@@ -295,7 +295,7 @@ void BiSearchVisitor::operator()(RSTypeT<TreeType>* rs) const
|
||||
}
|
||||
|
||||
//! Bichromatic range search on the given RSType specialized for KDTrees.
|
||||
void BiSearchVisitor::operator()(RSTypeT<tree::KDTree>* rs) const
|
||||
inline void BiSearchVisitor::operator()(RSTypeT<tree::KDTree>* rs) const
|
||||
{
|
||||
if (rs)
|
||||
return SearchLeaf(rs);
|
||||
@@ -303,7 +303,7 @@ void BiSearchVisitor::operator()(RSTypeT<tree::KDTree>* rs) const
|
||||
}
|
||||
|
||||
//! Bichromatic range search on the given RSType specialized for BallTrees.
|
||||
void BiSearchVisitor::operator()(RSTypeT<tree::BallTree>* rs) const
|
||||
inline void BiSearchVisitor::operator()(RSTypeT<tree::BallTree>* rs) const
|
||||
{
|
||||
if (rs)
|
||||
return SearchLeaf(rs);
|
||||
@@ -311,7 +311,7 @@ void BiSearchVisitor::operator()(RSTypeT<tree::BallTree>* rs) const
|
||||
}
|
||||
|
||||
//! Bichromatic range search specialized for Ocrees.
|
||||
void BiSearchVisitor::operator()(RSTypeT<tree::Octree>* rs) const
|
||||
inline void BiSearchVisitor::operator()(RSTypeT<tree::Octree>* rs) const
|
||||
{
|
||||
if (rs)
|
||||
return SearchLeaf(rs);
|
||||
@@ -351,8 +351,8 @@ void BiSearchVisitor::SearchLeaf(RSType* rs) const
|
||||
}
|
||||
|
||||
//! Save parameters for Train.
|
||||
TrainVisitor::TrainVisitor(arma::mat&& referenceSet,
|
||||
const size_t leafSize) :
|
||||
inline TrainVisitor::TrainVisitor(arma::mat&& referenceSet,
|
||||
const size_t leafSize) :
|
||||
referenceSet(std::move(referenceSet)),
|
||||
leafSize(leafSize)
|
||||
{}
|
||||
@@ -369,7 +369,7 @@ void TrainVisitor::operator()(RSTypeT<TreeType>* rs) const
|
||||
}
|
||||
|
||||
//! Train on the given RSType specialized for KDTrees.
|
||||
void TrainVisitor::operator()(RSTypeT<tree::KDTree>* rs) const
|
||||
inline void TrainVisitor::operator()(RSTypeT<tree::KDTree>* rs) const
|
||||
{
|
||||
if (rs)
|
||||
return TrainLeaf(rs);
|
||||
@@ -377,7 +377,7 @@ void TrainVisitor::operator()(RSTypeT<tree::KDTree>* rs) const
|
||||
}
|
||||
|
||||
//! Train on the given RSType specialized for BallTrees.
|
||||
void TrainVisitor::operator()(RSTypeT<tree::BallTree>* rs) const
|
||||
inline void TrainVisitor::operator()(RSTypeT<tree::BallTree>* rs) const
|
||||
{
|
||||
if (rs)
|
||||
return TrainLeaf(rs);
|
||||
@@ -385,7 +385,7 @@ void TrainVisitor::operator()(RSTypeT<tree::BallTree>* rs) const
|
||||
}
|
||||
|
||||
//! Train specialized for Octrees.
|
||||
void TrainVisitor::operator()(RSTypeT<tree::Octree>* rs) const
|
||||
inline void TrainVisitor::operator()(RSTypeT<tree::Octree>* rs) const
|
||||
{
|
||||
if (rs)
|
||||
return TrainLeaf(rs);
|
||||
|
||||
@@ -4,7 +4,7 @@ set(SOURCES
|
||||
mountain_car.hpp
|
||||
cart_pole.hpp
|
||||
continuous_mountain_car.hpp
|
||||
acrobat.hpp
|
||||
acrobot.hpp
|
||||
pendulum.hpp
|
||||
reward_clipping.hpp
|
||||
)
|
||||
|
||||
+20
-15
@@ -1,8 +1,8 @@
|
||||
/**
|
||||
* @file acrobat.hpp
|
||||
* @file acrobot.hpp
|
||||
* @author Rohan Raj
|
||||
*
|
||||
* This file is an implementation of Acrobat task:
|
||||
* This file is an implementation of Acrobot task:
|
||||
* https://gym.openai.com/envs/Acrobot-v1/
|
||||
*
|
||||
* mlpack is free software; you may redistribute it and/or modify it under the
|
||||
@@ -10,8 +10,8 @@
|
||||
* 3-clause BSD license along with mlpack. If not, see
|
||||
* http://www.opensource.org/licenses/BSD-3-Clause for more information.
|
||||
*/
|
||||
#ifndef MLPACK_METHODS_RL_ENVIRONMENT_ACROBAT_HPP
|
||||
#define MLPACK_METHODS_RL_ENVIRONMENT_ACROBAT_HPP
|
||||
#ifndef MLPACK_METHODS_RL_ENVIRONMENT_ACROBOT_HPP
|
||||
#define MLPACK_METHODS_RL_ENVIRONMENT_ACROBOT_HPP
|
||||
|
||||
#include <mlpack/core.hpp>
|
||||
|
||||
@@ -19,17 +19,17 @@ namespace mlpack{
|
||||
namespace rl{
|
||||
|
||||
/**
|
||||
* Implementation of Acrobat game. Acrobot is a 2-link pendulum with only the
|
||||
* Implementation of Acrobot game. Acrobot is a 2-link pendulum with only the
|
||||
* second joint actuated. Intitially, both links point downwards. The goal is
|
||||
* to swing the end-effector at a height at least the length of one link above
|
||||
* the base. Both links can swing freely and can pass by each other, i.e.,
|
||||
* they don't collide when they have the same angle.
|
||||
*/
|
||||
class Acrobat
|
||||
class Acrobot
|
||||
{
|
||||
public:
|
||||
/*
|
||||
* Implementation of Acrobat State. Each State is a tuple vector
|
||||
* Implementation of Acrobot State. Each State is a tuple vector
|
||||
* (theta1, thetha2, angular velocity 1, angular velocity 2).
|
||||
*/
|
||||
class State
|
||||
@@ -83,7 +83,7 @@ class Acrobat
|
||||
};
|
||||
|
||||
/*
|
||||
* Implementation of action for Acrobat
|
||||
* Implementation of action for Acrobot
|
||||
*/
|
||||
enum Action
|
||||
{
|
||||
@@ -96,7 +96,7 @@ class Acrobat
|
||||
};
|
||||
|
||||
/**
|
||||
* Construct a Acrobat instance using the given constants.
|
||||
* Construct a Acrobot instance using the given constants.
|
||||
*
|
||||
* @param gravity The gravity parameter.
|
||||
* @param linkLength1 The length of link 1.
|
||||
@@ -110,7 +110,7 @@ class Acrobat
|
||||
* @param maxVel2 The max angular velocity of link2.
|
||||
* @param dt The differential value.
|
||||
*/
|
||||
Acrobat(const double gravity = 9.81,
|
||||
Acrobot(const double gravity = 9.81,
|
||||
const double linkLength1 = 1.0,
|
||||
const double linkLength2 = 1.0,
|
||||
const double linkMass1 = 1.0,
|
||||
@@ -137,7 +137,7 @@ class Acrobat
|
||||
{ /* Nothing to do here */ }
|
||||
|
||||
/**
|
||||
* Dynamics of the Acrobat System. To get reward and next state based on
|
||||
* Dynamics of the Acrobot System. To get reward and next state based on
|
||||
* current state and current action. Always return -1 reward.
|
||||
*
|
||||
* @param state The current State.
|
||||
@@ -165,7 +165,7 @@ class Acrobat
|
||||
nextState.AngularVelocity2() = std::min(
|
||||
std::max(currentNextState[3], -maxVel2), maxVel2);
|
||||
/**
|
||||
* If the acrobat reaches a terminal state, it should be given a positive
|
||||
* If the acrobot reaches a terminal state, it should be given a positive
|
||||
* reward. This will ensure that the agent learns the goal of the game.
|
||||
*/
|
||||
bool done = IsTerminal(nextState);
|
||||
@@ -175,7 +175,7 @@ class Acrobat
|
||||
};
|
||||
|
||||
/**
|
||||
* Dynamics of the Acrobat System. To get reward and next state based on
|
||||
* Dynamics of the Acrobot System. To get reward and next state based on
|
||||
* current state and current action. This function calls the Sample function
|
||||
* to estimate the next state return reward for taking a particular action.
|
||||
*
|
||||
@@ -198,7 +198,7 @@ class Acrobat
|
||||
}
|
||||
|
||||
/**
|
||||
* This function checks if the acrobat has reached the terminal state.
|
||||
* This function checks if the acrobot has reached the terminal state.
|
||||
*
|
||||
* @param state The current State.
|
||||
*/
|
||||
@@ -349,7 +349,12 @@ class Acrobat
|
||||
|
||||
//! Locally-stored done reward.
|
||||
double doneReward;
|
||||
}; // class Acrobat
|
||||
}; // class Acrobot
|
||||
|
||||
/**
|
||||
* Add an alias for backward compatibility.
|
||||
*/
|
||||
typedef Acrobot Acrobat;
|
||||
|
||||
} // namespace rl
|
||||
} // namespace mlpack
|
||||
@@ -184,7 +184,7 @@ class ContinuousMountainCar
|
||||
*/
|
||||
bool IsTerminal(const State& state) const
|
||||
{
|
||||
return bool(state.Position() >= positionGoal);
|
||||
return state.Position() >= positionGoal;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@@ -90,16 +90,19 @@ class MountainCar
|
||||
*
|
||||
* @param positionMin Minimum legal position.
|
||||
* @param positionMax Maximum legal position.
|
||||
* @param positionGoal Final target position.
|
||||
* @param velocityMin Minimum legal velocity.
|
||||
* @param velocityMax Maximum legal velocity.
|
||||
*/
|
||||
MountainCar(const double positionMin = -1.2,
|
||||
const double positionMax = 0.5,
|
||||
const double positionMax = 0.6,
|
||||
const double positionGoal = 0.5,
|
||||
const double velocityMin = -0.07,
|
||||
const double velocityMax = 0.07,
|
||||
const double doneReward = 0) :
|
||||
positionMin(positionMin),
|
||||
positionMax(positionMax),
|
||||
positionGoal(positionGoal),
|
||||
velocityMin(velocityMin),
|
||||
velocityMax(velocityMax),
|
||||
doneReward(doneReward)
|
||||
@@ -130,10 +133,9 @@ class MountainCar
|
||||
nextState.Position() = std::min(
|
||||
std::max(nextState.Position(), positionMin), positionMax);
|
||||
|
||||
if (std::abs(nextState.Position() - positionMin) <= 1e-5)
|
||||
{
|
||||
if (nextState.Position() == positionMin && nextState.Velocity() < 0)
|
||||
nextState.Velocity() = 0.0;
|
||||
}
|
||||
|
||||
bool done = IsTerminal(nextState);
|
||||
/**
|
||||
* If done is true , it means that car has reached its goal.
|
||||
@@ -183,7 +185,7 @@ class MountainCar
|
||||
*/
|
||||
bool IsTerminal(const State& state) const
|
||||
{
|
||||
return std::abs(state.Position() - positionMax) <= 1e-5;
|
||||
return state.Position() >= positionGoal;
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -193,6 +195,9 @@ class MountainCar
|
||||
//! Locally-stored maximum legal position.
|
||||
double positionMax;
|
||||
|
||||
//! Locally-stored goal position.
|
||||
double positionGoal;
|
||||
|
||||
//! Locally-stored minimum legal velocity.
|
||||
double velocityMin;
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ add_executable(mlpack_test
|
||||
hyperplane_test.cpp
|
||||
imputation_test.cpp
|
||||
init_rules_test.cpp
|
||||
kde_test.cpp
|
||||
kernel_pca_test.cpp
|
||||
kernel_test.cpp
|
||||
kernel_traits_test.cpp
|
||||
@@ -119,6 +120,7 @@ add_executable(mlpack_test
|
||||
main_tests/det_test.cpp
|
||||
main_tests/decision_tree_test.cpp
|
||||
main_tests/decision_stump_test.cpp
|
||||
main_tests/kde_test.cpp
|
||||
main_tests/linear_regression_test.cpp
|
||||
main_tests/logistic_regression_test.cpp
|
||||
main_tests/lmnn_test.cpp
|
||||
@@ -144,6 +146,7 @@ add_executable(mlpack_test
|
||||
main_tests/radical_test.cpp
|
||||
main_tests/hmm_test_utils.hpp
|
||||
main_tests/kernel_pca_test.cpp
|
||||
main_tests/range_search_test.cpp
|
||||
)
|
||||
|
||||
# Link dependencies of test executable.
|
||||
|
||||
@@ -243,11 +243,12 @@ void CheckELUDerivativeCorrect(const arma::colvec input,
|
||||
ELU<> lrf(1.0);
|
||||
|
||||
// Test the calculation of the derivatives using the entire vector as input.
|
||||
arma::colvec derivatives;
|
||||
arma::colvec derivatives, activations;
|
||||
|
||||
// This error vector will be set to 1 to get the derivatives.
|
||||
arma::colvec error = arma::ones<arma::colvec>(input.n_elem);
|
||||
lrf.Backward(std::move(input), std::move(error), std::move(derivatives));
|
||||
lrf.Forward(std::move(input), std::move(activations));
|
||||
lrf.Backward(std::move(activations), std::move(error), std::move(derivatives));
|
||||
for (size_t i = 0; i < derivatives.n_elem; i++)
|
||||
{
|
||||
BOOST_REQUIRE_CLOSE(derivatives.at(i), target.at(i), 1e-3);
|
||||
@@ -381,21 +382,23 @@ BOOST_AUTO_TEST_CASE(SELUFunctionDerivativeTest)
|
||||
|
||||
arma::mat error = arma::ones<arma::mat>(input.n_elem, 1);
|
||||
|
||||
arma::mat derivatives;
|
||||
arma::mat derivatives, activations;
|
||||
|
||||
SELU selu;
|
||||
|
||||
selu.Backward(std::move(input), std::move(error), std::move(derivatives));
|
||||
selu.Forward(std::move(input), activations);
|
||||
selu.Backward(std::move(activations), std::move(error), std::move(derivatives));
|
||||
|
||||
BOOST_REQUIRE_LE(arma::as_scalar(arma::abs(arma::mean(derivatives) -
|
||||
selu.Lambda())), 10e-4);
|
||||
|
||||
input.fill(-1);
|
||||
|
||||
selu.Backward(std::move(input), std::move(error), std::move(derivatives));
|
||||
selu.Forward(std::move(input), activations);
|
||||
selu.Backward(std::move(activations), std::move(error), std::move(derivatives));
|
||||
|
||||
BOOST_REQUIRE_LE(arma::as_scalar(arma::abs(arma::mean(derivatives) -
|
||||
selu.Lambda() * (selu.Alpha() - 1))), 10e-4);
|
||||
selu.Lambda() * selu.Alpha() - arma::mean(activations))), 10e-4);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -519,7 +522,7 @@ BOOST_AUTO_TEST_CASE(ELUFunctionTest)
|
||||
1 0.36787945 1 1");
|
||||
|
||||
CheckELUActivationCorrect(activationData, desiredActivations);
|
||||
CheckELUDerivativeCorrect(desiredActivations, desiredDerivatives);
|
||||
CheckELUDerivativeCorrect(activationData, desiredDerivatives);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1626,7 +1626,9 @@ BOOST_AUTO_TEST_CASE(GradientAtrousConvolutionLayerTest)
|
||||
arma::mat input, target;
|
||||
} function;
|
||||
|
||||
BOOST_REQUIRE_LE(CheckGradient(function), 1e-3);
|
||||
// TODO: this tolerance seems far higher than necessary. The implementation
|
||||
// should be checked.
|
||||
BOOST_REQUIRE_LE(CheckGradient(function), 0.2);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+115
-77
@@ -111,10 +111,11 @@ BOOST_AUTO_TEST_CASE(GMMTrainEMOneGaussian)
|
||||
arma::mat actualCovar = ccov(data, 1 /* biased estimator */);
|
||||
|
||||
// Check the model to see that it is correct.
|
||||
CheckMatrices(gmm.Component(0).Mean(), actualMean);
|
||||
CheckMatrices(gmm.Component(0).Covariance(), actualCovar);
|
||||
BOOST_REQUIRE_LT(arma::norm(gmm.Component(0).Mean() - actualMean), 1e-5);
|
||||
BOOST_REQUIRE_LT(arma::norm(gmm.Component(0).Covariance() - actualCovar),
|
||||
1e-4);
|
||||
|
||||
BOOST_REQUIRE_CLOSE(gmm.Weights()[0], 1.0, 1e-5);
|
||||
BOOST_REQUIRE_CLOSE(gmm.Weights()[0], 1.0, 1e-4);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,83 +131,120 @@ BOOST_AUTO_TEST_CASE(GMMTrainEMMultipleGaussians)
|
||||
size_t dims = 8;
|
||||
size_t gaussians = 3;
|
||||
|
||||
// Generate dataset.
|
||||
arma::mat data;
|
||||
data.zeros(dims, 500);
|
||||
|
||||
std::vector<arma::vec> means(gaussians);
|
||||
std::vector<arma::mat> covars(gaussians);
|
||||
arma::vec weights(gaussians);
|
||||
arma::Col<size_t> counts(gaussians);
|
||||
|
||||
// Choose weights randomly.
|
||||
weights.zeros();
|
||||
while (weights.min() < 0.02)
|
||||
// We'll run three trials, and it needs to pass during at least one trial.
|
||||
bool success = false;
|
||||
for (size_t trial = 0; trial < 3; ++trial)
|
||||
{
|
||||
weights.randu(gaussians);
|
||||
weights /= accu(weights);
|
||||
// Generate dataset.
|
||||
arma::mat data;
|
||||
data.zeros(dims, 500);
|
||||
|
||||
std::vector<arma::vec> means(gaussians);
|
||||
std::vector<arma::mat> covars(gaussians);
|
||||
arma::vec weights(gaussians);
|
||||
arma::Col<size_t> counts(gaussians);
|
||||
|
||||
// Choose weights randomly. We want each component to have somewhat
|
||||
// significant weight, but we also need to make sure that no weights are too
|
||||
// close.
|
||||
double minDiff = DBL_MAX;
|
||||
do
|
||||
{
|
||||
weights.zeros();
|
||||
weights.randu(gaussians);
|
||||
weights /= accu(weights);
|
||||
weights *= 0.4;
|
||||
weights += (0.6 / double(gaussians));
|
||||
weights /= accu(weights); // Paranoia, just to be sure they sum to 1.
|
||||
|
||||
// Compute minimum element difference.
|
||||
minDiff = DBL_MAX;
|
||||
for (size_t i = 0; i < weights.n_elem; ++i)
|
||||
for (size_t j = (i + 1); j < weights.n_elem; ++j)
|
||||
if (std::abs(weights[i] - weights[j]) < minDiff)
|
||||
minDiff = std::abs(weights[i] - weights[j]);
|
||||
} while (minDiff < 0.02);
|
||||
|
||||
for (size_t i = 0; i < gaussians; i++)
|
||||
counts[i] = round(weights[i] * (data.n_cols - gaussians));
|
||||
// Ensure one point minimum in each.
|
||||
counts += 1;
|
||||
|
||||
// Account for rounding errors (possibly necessary).
|
||||
counts[gaussians - 1] += (data.n_cols - arma::accu(counts));
|
||||
|
||||
// Build each Gaussian individually.
|
||||
size_t point = 0;
|
||||
for (size_t i = 0; i < gaussians; i++)
|
||||
{
|
||||
arma::mat gaussian;
|
||||
gaussian.randn(dims, counts[i]);
|
||||
|
||||
// Randomly generate mean and covariance.
|
||||
means[i].randu(dims);
|
||||
means[i] -= 0.5;
|
||||
means[i] *= 50;
|
||||
|
||||
// We need to make sure the covariance is positive definite. We will take
|
||||
// a random matrix C and then set our covariance to 4 * C * C', which will
|
||||
// be positive semidefinite.
|
||||
covars[i].randu(dims, dims);
|
||||
covars[i] *= 4 * trans(covars[i]);
|
||||
|
||||
data.cols(point, point + counts[i] - 1) = (covars[i] * gaussian + means[i]
|
||||
* arma::ones<arma::rowvec>(counts[i]));
|
||||
|
||||
// Calculate the actual means and covariances because they will probably
|
||||
// be different (this is easier to do before we shuffle the points).
|
||||
means[i] = arma::mean(data.cols(point, point + counts[i] - 1), 1);
|
||||
covars[i] = ccov(data.cols(point, point + counts[i] - 1), 1 /* biased */);
|
||||
|
||||
point += counts[i];
|
||||
}
|
||||
|
||||
// Calculate actual weights.
|
||||
for (size_t i = 0; i < gaussians; i++)
|
||||
weights[i] = (double) counts[i] / data.n_cols;
|
||||
|
||||
// Now train the model.
|
||||
GMM gmm(gaussians, dims);
|
||||
gmm.Train(data, 10);
|
||||
|
||||
arma::uvec sortRef = sort_index(weights);
|
||||
arma::uvec sortTry = sort_index(gmm.Weights());
|
||||
|
||||
// If it's a bad match, try training again with a different seed. We
|
||||
// probably just fell into some bad local minimum or had a bad starting
|
||||
// point.
|
||||
gmm = GMM(gaussians, dims);
|
||||
gmm.Train(data, 10);
|
||||
|
||||
sortTry = sort_index(gmm.Weights());
|
||||
|
||||
if (arma::norm(weights.elem(sortRef) - gmm.Weights().elem(sortTry)) > 0.1)
|
||||
continue;
|
||||
|
||||
// Check the model to see that it is correct.
|
||||
for (size_t i = 0; i < gaussians; i++)
|
||||
{
|
||||
// Check the mean.
|
||||
BOOST_REQUIRE_LT(
|
||||
arma::norm(gmm.Component(sortTry[i]).Mean() - means[sortRef[i]]),
|
||||
0.05);
|
||||
// Check the covariance.
|
||||
BOOST_REQUIRE_LT(
|
||||
arma::norm(gmm.Component(sortTry[i]).Covariance() -
|
||||
covars[sortRef[i]]), 0.2);
|
||||
// Check the weight.
|
||||
BOOST_REQUIRE_CLOSE(gmm.Weights()[sortTry[i]], weights[sortRef[i]],
|
||||
0.005);
|
||||
}
|
||||
|
||||
success = true;
|
||||
break; // No need for multiple iterations.
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < gaussians; i++)
|
||||
counts[i] = round(weights[i] * (data.n_cols - gaussians));
|
||||
// Ensure one point minimum in each.
|
||||
counts += 1;
|
||||
|
||||
// Account for rounding errors (possibly necessary).
|
||||
counts[gaussians - 1] += (data.n_cols - arma::accu(counts));
|
||||
|
||||
// Build each Gaussian individually.
|
||||
size_t point = 0;
|
||||
for (size_t i = 0; i < gaussians; i++)
|
||||
{
|
||||
arma::mat gaussian;
|
||||
gaussian.randn(dims, counts[i]);
|
||||
|
||||
// Randomly generate mean and covariance.
|
||||
means[i].randu(dims);
|
||||
means[i] -= 0.5;
|
||||
means[i] *= 50;
|
||||
|
||||
// We need to make sure the covariance is positive definite. We will take a
|
||||
// random matrix C and then set our covariance to 4 * C * C', which will be
|
||||
// positive semidefinite.
|
||||
covars[i].randu(dims, dims);
|
||||
covars[i] *= 4 * trans(covars[i]);
|
||||
|
||||
data.cols(point, point + counts[i] - 1) = (covars[i] * gaussian + means[i]
|
||||
* arma::ones<arma::rowvec>(counts[i]));
|
||||
|
||||
// Calculate the actual means and covariances because they will probably
|
||||
// be different (this is easier to do before we shuffle the points).
|
||||
means[i] = arma::mean(data.cols(point, point + counts[i] - 1), 1);
|
||||
covars[i] = ccov(data.cols(point, point + counts[i] - 1), 1 /* biased */);
|
||||
|
||||
point += counts[i];
|
||||
}
|
||||
|
||||
// Calculate actual weights.
|
||||
for (size_t i = 0; i < gaussians; i++)
|
||||
weights[i] = (double) counts[i] / data.n_cols;
|
||||
|
||||
// Now train the model.
|
||||
GMM gmm(gaussians, dims);
|
||||
gmm.Train(data, 10);
|
||||
|
||||
arma::uvec sortRef = sort_index(weights);
|
||||
arma::uvec sortTry = sort_index(gmm.Weights());
|
||||
|
||||
// Check the model to see that it is correct.
|
||||
for (size_t i = 0; i < gaussians; i++)
|
||||
{
|
||||
// Check the mean.
|
||||
CheckMatrices(gmm.Component(sortTry[i]).Mean(), means[sortRef[i]], 1e-3);
|
||||
// Check the covariance.
|
||||
CheckMatrices(gmm.Component(sortTry[i]).Covariance(), covars[sortRef[i]],
|
||||
0.15);
|
||||
// Check the weight.
|
||||
BOOST_REQUIRE_CLOSE(gmm.Weights()[sortTry[i]], weights[sortRef[i]],
|
||||
0.005);
|
||||
}
|
||||
BOOST_REQUIRE_EQUAL(success, true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,821 @@
|
||||
/**
|
||||
* @file kde_test.cpp
|
||||
* @author Roberto Hueso
|
||||
*
|
||||
* 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 <mlpack/core.hpp>
|
||||
|
||||
#include <mlpack/methods/kde/kde.hpp>
|
||||
#include <mlpack/core/tree/binary_space_tree.hpp>
|
||||
#include <mlpack/core/tree/octree.hpp>
|
||||
#include <mlpack/core/tree/cover_tree.hpp>
|
||||
#include <mlpack/core/tree/rectangle_tree.hpp>
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include "test_tools.hpp"
|
||||
#include "serialization.hpp"
|
||||
|
||||
using namespace mlpack;
|
||||
using namespace mlpack::kde;
|
||||
using namespace mlpack::metric;
|
||||
using namespace mlpack::tree;
|
||||
using namespace mlpack::kernel;
|
||||
|
||||
using namespace boost::serialization;
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(KDETest);
|
||||
|
||||
// Brute force gaussian KDE.
|
||||
template <typename KernelType>
|
||||
void BruteForceKDE(const arma::mat& reference,
|
||||
const arma::mat& query,
|
||||
arma::vec& densities,
|
||||
KernelType& kernel)
|
||||
{
|
||||
metric::EuclideanDistance metric;
|
||||
for (size_t i = 0; i < query.n_cols; ++i)
|
||||
{
|
||||
for (size_t j = 0; j < reference.n_cols; ++j)
|
||||
{
|
||||
double distance = metric.Evaluate(query.col(i),reference.col(j));
|
||||
densities(i) += kernel.Evaluate(distance);
|
||||
}
|
||||
}
|
||||
densities /= reference.n_cols;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if simple case is correct according to manually calculated results.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(KDESimpleTest)
|
||||
{
|
||||
// Transposed reference and query sets because it's easier to read.
|
||||
arma::mat reference = { {-1.0, -1.0},
|
||||
{-2.0, -1.0},
|
||||
{-3.0, -2.0},
|
||||
{ 1.0, 1.0},
|
||||
{ 2.0, 1.0},
|
||||
{ 3.0, 2.0} };
|
||||
arma::mat query = { { 0.0, 0.5},
|
||||
{ 0.4, -3.0},
|
||||
{ 0.0, 0.0},
|
||||
{-2.1, 1.0} };
|
||||
arma::inplace_trans(reference);
|
||||
arma::inplace_trans(query);
|
||||
arma::vec estimations;
|
||||
// Manually calculated results.
|
||||
arma::vec estimationsResult = {0.08323668699564207296148765,
|
||||
0.00167470061366603324010116,
|
||||
0.07658867126520703394465527,
|
||||
0.01028120384800740999553525};
|
||||
KDE<GaussianKernel,
|
||||
EuclideanDistance,
|
||||
arma::mat,
|
||||
KDTree>
|
||||
kde(0.0, 0.01, GaussianKernel(0.8));
|
||||
kde.Train(reference);
|
||||
kde.Evaluate(query, estimations);
|
||||
for (size_t i = 0; i < query.n_cols; ++i)
|
||||
BOOST_REQUIRE_CLOSE(estimations[i], estimationsResult[i], 0.01);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test Train(Tree...) and Evaluate(Tree...).
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(KDETreeAsArguments)
|
||||
{
|
||||
// Transposed reference and query sets because it's easier to read.
|
||||
arma::mat reference = { {-1.0, -1.0},
|
||||
{-2.0, -1.0},
|
||||
{-3.0, -2.0},
|
||||
{ 1.0, 1.0},
|
||||
{ 2.0, 1.0},
|
||||
{ 3.0, 2.0} };
|
||||
arma::mat query = { { 0.0, 0.5},
|
||||
{ 0.4, -3.0},
|
||||
{ 0.0, 0.0},
|
||||
{-2.1, 1.0} };
|
||||
arma::inplace_trans(reference);
|
||||
arma::inplace_trans(query);
|
||||
arma::vec estimations = arma::vec(query.n_cols, arma::fill::zeros);
|
||||
arma::vec estimationsResult = arma::vec(query.n_cols, arma::fill::zeros);
|
||||
const double kernelBandwidth = 0.8;
|
||||
|
||||
// Get brute force results.
|
||||
GaussianKernel kernel(kernelBandwidth);
|
||||
BruteForceKDE<GaussianKernel>(reference,
|
||||
query,
|
||||
estimationsResult,
|
||||
kernel);
|
||||
|
||||
// Get dual-tree results.
|
||||
typedef KDTree<EuclideanDistance, kde::KDEStat, arma::mat> Tree;
|
||||
std::vector<size_t> oldFromNewQueries, oldFromNewReferences;
|
||||
Tree* queryTree = new Tree(query, oldFromNewQueries, 2);
|
||||
Tree* referenceTree = new Tree(reference, oldFromNewReferences, 2);
|
||||
KDE<GaussianKernel,
|
||||
EuclideanDistance,
|
||||
arma::mat,
|
||||
KDTree>
|
||||
kde(0.0, 1e-6, GaussianKernel(kernelBandwidth));
|
||||
kde.Train(referenceTree, &oldFromNewReferences);
|
||||
kde.Evaluate(queryTree, std::move(oldFromNewQueries), estimations);
|
||||
for (size_t i = 0; i < query.n_cols; ++i)
|
||||
BOOST_REQUIRE_CLOSE(estimations[i], estimationsResult[i], 0.01);
|
||||
delete queryTree;
|
||||
delete referenceTree;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test dual-tree implementation results against brute force results.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(GaussianKDEBruteForceTest)
|
||||
{
|
||||
arma::mat reference = arma::randu(2, 200);
|
||||
arma::mat query = arma::randu(2, 60);
|
||||
arma::vec bfEstimations = arma::vec(query.n_cols, arma::fill::zeros);
|
||||
arma::vec treeEstimations = arma::vec(query.n_cols, arma::fill::zeros);
|
||||
const double kernelBandwidth = 0.3;
|
||||
const double relError = 0.01;
|
||||
|
||||
// Brute force KDE.
|
||||
GaussianKernel kernel(kernelBandwidth);
|
||||
BruteForceKDE<GaussianKernel>(reference,
|
||||
query,
|
||||
bfEstimations,
|
||||
kernel);
|
||||
|
||||
// Optimized KDE.
|
||||
metric::EuclideanDistance metric;
|
||||
KDE<GaussianKernel,
|
||||
metric::EuclideanDistance,
|
||||
arma::mat,
|
||||
tree::KDTree>
|
||||
kde(relError, 0.0, kernel, KDEMode::DUAL_TREE_MODE, metric);
|
||||
kde.Train(reference);
|
||||
kde.Evaluate(query, treeEstimations);
|
||||
|
||||
// Check whether results are equal.
|
||||
for (size_t i = 0; i < query.n_cols; ++i)
|
||||
BOOST_REQUIRE_CLOSE(bfEstimations[i], treeEstimations[i], relError*100);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test single-tree implementation results against brute force results.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(GaussianSingleKDEBruteForceTest)
|
||||
{
|
||||
arma::mat reference = arma::randu(2, 300);
|
||||
arma::mat query = arma::randu(2, 100);
|
||||
arma::vec bfEstimations = arma::vec(query.n_cols, arma::fill::zeros);
|
||||
arma::vec treeEstimations = arma::vec(query.n_cols, arma::fill::zeros);
|
||||
const double kernelBandwidth = 0.3;
|
||||
const double relError = 0.01;
|
||||
|
||||
// Brute force KDE.
|
||||
GaussianKernel kernel(kernelBandwidth);
|
||||
BruteForceKDE<GaussianKernel>(reference,
|
||||
query,
|
||||
bfEstimations,
|
||||
kernel);
|
||||
|
||||
// Optimized KDE.
|
||||
metric::EuclideanDistance metric;
|
||||
KDE<GaussianKernel,
|
||||
metric::EuclideanDistance,
|
||||
arma::mat,
|
||||
tree::KDTree>
|
||||
kde(relError, 0.0, kernel, KDEMode::SINGLE_TREE_MODE, metric);
|
||||
kde.Train(reference);
|
||||
kde.Evaluate(query, treeEstimations);
|
||||
|
||||
// Check whether results are equal.
|
||||
for (size_t i = 0; i < query.n_cols; ++i)
|
||||
BOOST_REQUIRE_CLOSE(bfEstimations[i], treeEstimations[i], relError*100);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test single-tree implementation results against brute force results using
|
||||
* a cover-tree and Epanechnikov kernel.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(EpanechnikovCoverSingleKDETest)
|
||||
{
|
||||
arma::mat reference = arma::randu(2, 300);
|
||||
arma::mat query = arma::randu(2, 100);
|
||||
arma::vec bfEstimations = arma::vec(query.n_cols, arma::fill::zeros);
|
||||
arma::vec treeEstimations = arma::vec(query.n_cols, arma::fill::zeros);
|
||||
const double kernelBandwidth = 1.1;
|
||||
const double relError = 0.08;
|
||||
|
||||
// Brute force KDE.
|
||||
EpanechnikovKernel kernel(kernelBandwidth);
|
||||
BruteForceKDE<EpanechnikovKernel>(reference,
|
||||
query,
|
||||
bfEstimations,
|
||||
kernel);
|
||||
|
||||
// Optimized KDE.
|
||||
metric::EuclideanDistance metric;
|
||||
KDE<EpanechnikovKernel,
|
||||
metric::EuclideanDistance,
|
||||
arma::mat,
|
||||
tree::StandardCoverTree>
|
||||
kde(relError, 0.0, kernel, KDEMode::SINGLE_TREE_MODE, metric);
|
||||
kde.Train(reference);
|
||||
kde.Evaluate(query, treeEstimations);
|
||||
|
||||
// Check whether results are equal.
|
||||
for (size_t i = 0; i < query.n_cols; ++i)
|
||||
BOOST_REQUIRE_CLOSE(bfEstimations[i], treeEstimations[i], relError*100);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test single-tree implementation results against brute force results using
|
||||
* an octree and Epanechnikov kernel.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(EpanechnikovOctreeSingleKDETest)
|
||||
{
|
||||
arma::mat reference = arma::randu(2, 300);
|
||||
arma::mat query = arma::randu(2, 100);
|
||||
arma::vec bfEstimations = arma::vec(query.n_cols, arma::fill::zeros);
|
||||
arma::vec treeEstimations = arma::vec(query.n_cols, arma::fill::zeros);
|
||||
const double kernelBandwidth = 1.0;
|
||||
const double relError = 0.05;
|
||||
|
||||
// Brute force KDE.
|
||||
EpanechnikovKernel kernel(kernelBandwidth);
|
||||
BruteForceKDE<EpanechnikovKernel>(reference,
|
||||
query,
|
||||
bfEstimations,
|
||||
kernel);
|
||||
|
||||
// Optimized KDE.
|
||||
metric::EuclideanDistance metric;
|
||||
KDE<EpanechnikovKernel,
|
||||
metric::EuclideanDistance,
|
||||
arma::mat,
|
||||
tree::Octree>
|
||||
kde(relError, 0.0, kernel, KDEMode::SINGLE_TREE_MODE, metric);
|
||||
kde.Train(reference);
|
||||
kde.Evaluate(query, treeEstimations);
|
||||
|
||||
// Check whether results are equal.
|
||||
for (size_t i = 0; i < query.n_cols; ++i)
|
||||
BOOST_REQUIRE_CLOSE(bfEstimations[i], treeEstimations[i], relError*100);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test BallTree dual-tree implementation results against brute force results.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(BallTreeGaussianKDETest)
|
||||
{
|
||||
arma::mat reference = arma::randu(2, 200);
|
||||
arma::mat query = arma::randu(2, 60);
|
||||
arma::vec bfEstimations = arma::vec(query.n_cols, arma::fill::zeros);
|
||||
arma::vec treeEstimations = arma::vec(query.n_cols, arma::fill::zeros);
|
||||
const double kernelBandwidth = 0.4;
|
||||
const double relError = 0.05;
|
||||
|
||||
// Brute force KDE.
|
||||
GaussianKernel kernel(kernelBandwidth);
|
||||
BruteForceKDE<GaussianKernel>(reference,
|
||||
query,
|
||||
bfEstimations,
|
||||
kernel);
|
||||
|
||||
// BallTree KDE.
|
||||
typedef BallTree<EuclideanDistance, kde::KDEStat, arma::mat> Tree;
|
||||
std::vector<size_t> oldFromNewQueries, oldFromNewReferences;
|
||||
Tree* queryTree = new Tree(query, oldFromNewQueries, 2);
|
||||
Tree* referenceTree = new Tree(reference, oldFromNewReferences, 2);
|
||||
KDE<GaussianKernel,
|
||||
EuclideanDistance,
|
||||
arma::mat,
|
||||
BallTree>
|
||||
kde(relError, 0.0, GaussianKernel(kernelBandwidth));
|
||||
kde.Train(referenceTree, &oldFromNewReferences);
|
||||
kde.Evaluate(queryTree, std::move(oldFromNewQueries), treeEstimations);
|
||||
|
||||
// Check whether results are equal.
|
||||
for (size_t i = 0; i < query.n_cols; ++i)
|
||||
BOOST_REQUIRE_CLOSE(bfEstimations[i], treeEstimations[i], relError*100);
|
||||
|
||||
delete queryTree;
|
||||
delete referenceTree;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test Octree dual-tree implementation results against brute force results.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(OctreeGaussianKDETest)
|
||||
{
|
||||
arma::mat reference = arma::randu(2, 500);
|
||||
arma::mat query = arma::randu(2, 200);
|
||||
arma::vec bfEstimations = arma::vec(query.n_cols, arma::fill::zeros);
|
||||
arma::vec treeEstimations = arma::vec(query.n_cols, arma::fill::zeros);
|
||||
const double kernelBandwidth = 0.3;
|
||||
const double relError = 0.01;
|
||||
|
||||
// Brute force KDE.
|
||||
GaussianKernel kernel(kernelBandwidth);
|
||||
BruteForceKDE<GaussianKernel>(reference,
|
||||
query,
|
||||
bfEstimations,
|
||||
kernel);
|
||||
|
||||
// Optimized KDE.
|
||||
metric::EuclideanDistance metric;
|
||||
KDE<GaussianKernel,
|
||||
metric::EuclideanDistance,
|
||||
arma::mat,
|
||||
tree::Octree>
|
||||
kde(relError, 0.0, kernel, KDEMode::DUAL_TREE_MODE, metric);
|
||||
kde.Train(reference);
|
||||
kde.Evaluate(query, treeEstimations);
|
||||
|
||||
// Check whether results are equal.
|
||||
for (size_t i = 0; i < query.n_cols; ++i)
|
||||
BOOST_REQUIRE_CLOSE(bfEstimations[i], treeEstimations[i], relError*100);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test RTree dual-tree implementation results against brute force results.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(RTreeGaussianKDETest)
|
||||
{
|
||||
arma::mat reference = arma::randu(2, 500);
|
||||
arma::mat query = arma::randu(2, 200);
|
||||
arma::vec bfEstimations = arma::vec(query.n_cols, arma::fill::zeros);
|
||||
arma::vec treeEstimations = arma::vec(query.n_cols, arma::fill::zeros);
|
||||
const double kernelBandwidth = 0.3;
|
||||
const double relError = 0.01;
|
||||
|
||||
// Brute force KDE.
|
||||
GaussianKernel kernel(kernelBandwidth);
|
||||
BruteForceKDE<GaussianKernel>(reference,
|
||||
query,
|
||||
bfEstimations,
|
||||
kernel);
|
||||
|
||||
// Optimized KDE.
|
||||
metric::EuclideanDistance metric;
|
||||
KDE<GaussianKernel,
|
||||
metric::EuclideanDistance,
|
||||
arma::mat,
|
||||
tree::RTree>
|
||||
kde(relError, 0.0, kernel, KDEMode::DUAL_TREE_MODE, metric);
|
||||
kde.Train(reference);
|
||||
kde.Evaluate(query, treeEstimations);
|
||||
|
||||
// Check whether results are equal.
|
||||
for (size_t i = 0; i < query.n_cols; ++i)
|
||||
BOOST_REQUIRE_CLOSE(bfEstimations[i], treeEstimations[i], relError*100);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test Standard Cover Tree dual-tree implementation results against brute
|
||||
* force results.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(StandardCoverTreeGaussianKDETest)
|
||||
{
|
||||
arma::mat reference = arma::randu(2, 500);
|
||||
arma::mat query = arma::randu(2, 200);
|
||||
arma::vec bfEstimations = arma::vec(query.n_cols, arma::fill::zeros);
|
||||
arma::vec treeEstimations = arma::vec(query.n_cols, arma::fill::zeros);
|
||||
const double kernelBandwidth = 0.3;
|
||||
const double relError = 0.01;
|
||||
|
||||
// Brute force KDE.
|
||||
GaussianKernel kernel(kernelBandwidth);
|
||||
BruteForceKDE<GaussianKernel>(reference,
|
||||
query,
|
||||
bfEstimations,
|
||||
kernel);
|
||||
|
||||
// Optimized KDE.
|
||||
metric::EuclideanDistance metric;
|
||||
KDE<GaussianKernel,
|
||||
metric::EuclideanDistance,
|
||||
arma::mat,
|
||||
tree::StandardCoverTree>
|
||||
kde(relError, 0.0, kernel, KDEMode::DUAL_TREE_MODE, metric);
|
||||
kde.Train(reference);
|
||||
kde.Evaluate(query, treeEstimations);
|
||||
|
||||
// Check whether results are equal.
|
||||
for (size_t i = 0; i < query.n_cols; ++i)
|
||||
BOOST_REQUIRE_CLOSE(bfEstimations[i], treeEstimations[i], relError*100);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test duplicated value in reference matrix.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(DuplicatedReferenceSampleKDETest)
|
||||
{
|
||||
arma::mat reference = arma::randu(2, 30);
|
||||
arma::mat query = arma::randu(2, 10);
|
||||
arma::vec bfEstimations = arma::vec(query.n_cols, arma::fill::zeros);
|
||||
arma::vec treeEstimations = arma::vec(query.n_cols, arma::fill::zeros);
|
||||
const double kernelBandwidth = 0.4;
|
||||
const double relError = 0.05;
|
||||
|
||||
// Duplicate value.
|
||||
reference.col(2) = reference.col(3);
|
||||
|
||||
// Brute force KDE.
|
||||
GaussianKernel kernel(kernelBandwidth);
|
||||
BruteForceKDE<GaussianKernel>(reference,
|
||||
query,
|
||||
bfEstimations,
|
||||
kernel);
|
||||
|
||||
// Dual-tree KDE.
|
||||
typedef KDTree<EuclideanDistance, kde::KDEStat, arma::mat> Tree;
|
||||
std::vector<size_t> oldFromNewQueries, oldFromNewReferences;
|
||||
Tree* queryTree = new Tree(query, oldFromNewQueries, 2);
|
||||
Tree* referenceTree = new Tree(reference, oldFromNewReferences, 2);
|
||||
KDE<GaussianKernel,
|
||||
EuclideanDistance,
|
||||
arma::mat,
|
||||
KDTree>
|
||||
kde(relError, 0.0, GaussianKernel(kernelBandwidth));
|
||||
kde.Train(referenceTree, &oldFromNewReferences);
|
||||
kde.Evaluate(queryTree, oldFromNewQueries, treeEstimations);
|
||||
|
||||
// Check whether results are equal.
|
||||
for (size_t i = 0; i < query.n_cols; ++i)
|
||||
BOOST_REQUIRE_CLOSE(bfEstimations[i], treeEstimations[i], relError*100);
|
||||
|
||||
delete queryTree;
|
||||
delete referenceTree;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test duplicated value in query matrix.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(DuplicatedQuerySampleKDETest)
|
||||
{
|
||||
arma::mat reference = arma::randu(2, 30);
|
||||
arma::mat query = arma::randu(2, 10);
|
||||
arma::vec estimations = arma::vec(query.n_cols, arma::fill::zeros);
|
||||
const double kernelBandwidth = 0.4;
|
||||
const double relError = 0.05;
|
||||
|
||||
// Duplicate value.
|
||||
query.col(2) = query.col(3);
|
||||
|
||||
// Dual-tree KDE.
|
||||
typedef KDTree<EuclideanDistance, kde::KDEStat, arma::mat> Tree;
|
||||
std::vector<size_t> oldFromNewQueries, oldFromNewReferences;
|
||||
Tree* queryTree = new Tree(query, oldFromNewQueries, 2);
|
||||
Tree* referenceTree = new Tree(reference, oldFromNewReferences, 2);
|
||||
KDE<GaussianKernel,
|
||||
EuclideanDistance,
|
||||
arma::mat,
|
||||
KDTree>
|
||||
kde(relError, 0.0, GaussianKernel(kernelBandwidth));
|
||||
kde.Train(referenceTree, &oldFromNewReferences);
|
||||
kde.Evaluate(queryTree, oldFromNewQueries, estimations);
|
||||
|
||||
// Check whether results are equal.
|
||||
BOOST_REQUIRE_CLOSE(estimations[2], estimations[3], relError*100);
|
||||
|
||||
delete queryTree;
|
||||
delete referenceTree;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test dual-tree breadth-first implementation results against brute force
|
||||
* results.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(BreadthFirstKDETest)
|
||||
{
|
||||
arma::mat reference = arma::randu(2, 200);
|
||||
arma::mat query = arma::randu(2, 60);
|
||||
arma::vec bfEstimations = arma::vec(query.n_cols, arma::fill::zeros);
|
||||
arma::vec treeEstimations = arma::vec(query.n_cols, arma::fill::zeros);
|
||||
const double kernelBandwidth = 0.8;
|
||||
const double relError = 0.01;
|
||||
|
||||
// Brute force KDE.
|
||||
GaussianKernel kernel(kernelBandwidth);
|
||||
BruteForceKDE<GaussianKernel>(reference,
|
||||
query,
|
||||
bfEstimations,
|
||||
kernel);
|
||||
|
||||
// Breadth-First KDE.
|
||||
metric::EuclideanDistance metric;
|
||||
KDE<GaussianKernel,
|
||||
metric::EuclideanDistance,
|
||||
arma::mat,
|
||||
tree::KDTree,
|
||||
tree::KDTree<metric::EuclideanDistance,
|
||||
kde::KDEStat,
|
||||
arma::mat>::template BreadthFirstDualTreeTraverser>
|
||||
kde(relError, 0.0, kernel, KDEMode::DUAL_TREE_MODE, metric);
|
||||
kde.Train(reference);
|
||||
kde.Evaluate(query, treeEstimations);
|
||||
|
||||
// Check whether results are equal.
|
||||
for (size_t i = 0; i < query.n_cols; ++i)
|
||||
BOOST_REQUIRE_CLOSE(bfEstimations[i], treeEstimations[i], relError*100);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test 1-dimensional implementation results against brute force results.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(OneDimensionalTest)
|
||||
{
|
||||
arma::mat reference = arma::randu(1, 200);
|
||||
arma::mat query = arma::randu(1, 60);
|
||||
arma::vec bfEstimations = arma::vec(query.n_cols, arma::fill::zeros);
|
||||
arma::vec treeEstimations = arma::vec(query.n_cols, arma::fill::zeros);
|
||||
const double kernelBandwidth = 0.7;
|
||||
const double relError = 0.01;
|
||||
|
||||
// Brute force KDE.
|
||||
GaussianKernel kernel(kernelBandwidth);
|
||||
BruteForceKDE<GaussianKernel>(reference,
|
||||
query,
|
||||
bfEstimations,
|
||||
kernel);
|
||||
|
||||
// Optimized KDE.
|
||||
metric::EuclideanDistance metric;
|
||||
KDE<GaussianKernel,
|
||||
metric::EuclideanDistance,
|
||||
arma::mat,
|
||||
tree::KDTree>
|
||||
kde(relError, 0.0, kernel, KDEMode::DUAL_TREE_MODE, metric);
|
||||
kde.Train(reference);
|
||||
kde.Evaluate(query, treeEstimations);
|
||||
|
||||
// Check whether results are equal.
|
||||
for (size_t i = 0; i < query.n_cols; ++i)
|
||||
BOOST_REQUIRE_CLOSE(bfEstimations[i], treeEstimations[i], relError*100);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a case where an empty reference set is given to train the model.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(EmptyReferenceTest)
|
||||
{
|
||||
arma::mat reference;
|
||||
arma::mat query = arma::randu(1, 10);
|
||||
arma::vec estimations = arma::vec(query.n_cols, arma::fill::zeros);
|
||||
const double kernelBandwidth = 0.7;
|
||||
const double relError = 0.01;
|
||||
|
||||
// KDE.
|
||||
metric::EuclideanDistance metric;
|
||||
GaussianKernel kernel(kernelBandwidth);
|
||||
KDE<GaussianKernel,
|
||||
metric::EuclideanDistance,
|
||||
arma::mat,
|
||||
tree::KDTree>
|
||||
kde(relError, 0.0, kernel, KDEMode::DUAL_TREE_MODE, metric);
|
||||
|
||||
// When training using the dataset matrix.
|
||||
BOOST_REQUIRE_THROW(kde.Train(reference), std::invalid_argument);
|
||||
|
||||
// When training using a tree.
|
||||
std::vector<size_t> oldFromNewReferences;
|
||||
typedef KDTree<EuclideanDistance, kde::KDEStat, arma::mat> Tree;
|
||||
Tree* referenceTree = new Tree(reference, oldFromNewReferences, 2);
|
||||
BOOST_REQUIRE_THROW(
|
||||
kde.Train(referenceTree, &oldFromNewReferences), std::invalid_argument);
|
||||
|
||||
delete referenceTree;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests when reference set values and query set values dimensions don't match.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(EvaluationMatchDimensionsTest)
|
||||
{
|
||||
arma::mat reference = arma::randu(3, 10);
|
||||
arma::mat query = arma::randu(1, 10);
|
||||
arma::vec estimations = arma::vec(query.n_cols, arma::fill::zeros);
|
||||
const double kernelBandwidth = 0.7;
|
||||
const double relError = 0.01;
|
||||
|
||||
// KDE.
|
||||
metric::EuclideanDistance metric;
|
||||
GaussianKernel kernel(kernelBandwidth);
|
||||
KDE<GaussianKernel,
|
||||
metric::EuclideanDistance,
|
||||
arma::mat,
|
||||
tree::KDTree>
|
||||
kde(relError, 0.0, kernel, KDEMode::DUAL_TREE_MODE, metric);
|
||||
kde.Train(reference);
|
||||
|
||||
// When evaluating using the query dataset matrix.
|
||||
BOOST_REQUIRE_THROW(kde.Evaluate(query, estimations),
|
||||
std::invalid_argument);
|
||||
|
||||
// When evaluating using a query tree.
|
||||
typedef KDTree<EuclideanDistance, kde::KDEStat, arma::mat> Tree;
|
||||
std::vector<size_t> oldFromNewQueries;
|
||||
Tree* queryTree = new Tree(query, oldFromNewQueries, 3);
|
||||
BOOST_REQUIRE_THROW(kde.Evaluate(queryTree, oldFromNewQueries, estimations),
|
||||
std::invalid_argument);
|
||||
delete queryTree;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests when an empty query set is given to be evaluated.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(EmptyQuerySetTest)
|
||||
{
|
||||
arma::mat reference = arma::randu(1, 10);
|
||||
arma::mat query;
|
||||
// Set estimations to the wrong size.
|
||||
arma::vec estimations(33, arma::fill::zeros);
|
||||
const double kernelBandwidth = 0.7;
|
||||
const double relError = 0.01;
|
||||
|
||||
// KDE.
|
||||
metric::EuclideanDistance metric;
|
||||
GaussianKernel kernel(kernelBandwidth);
|
||||
KDE<GaussianKernel,
|
||||
metric::EuclideanDistance,
|
||||
arma::mat,
|
||||
tree::KDTree>
|
||||
kde(relError, 0.0, kernel, KDEMode::DUAL_TREE_MODE, metric);
|
||||
kde.Train(reference);
|
||||
|
||||
// The query set must be empty.
|
||||
BOOST_REQUIRE_EQUAL(query.n_cols, 0);
|
||||
// When evaluating using the query dataset matrix.
|
||||
BOOST_REQUIRE_NO_THROW(kde.Evaluate(query, estimations));
|
||||
|
||||
// When evaluating using a query tree.
|
||||
typedef KDTree<EuclideanDistance, kde::KDEStat, arma::mat> Tree;
|
||||
std::vector<size_t> oldFromNewQueries;
|
||||
Tree* queryTree = new Tree(query, oldFromNewQueries, 3);
|
||||
BOOST_REQUIRE_NO_THROW(
|
||||
kde.Evaluate(queryTree, oldFromNewQueries, estimations));
|
||||
delete queryTree;
|
||||
|
||||
// Estimations must be empty.
|
||||
BOOST_REQUIRE_EQUAL(estimations.size(), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests serialiation of KDE models.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(SerializationTest)
|
||||
{
|
||||
// Initial KDE model to me serialized.
|
||||
const double relError = 0.25;
|
||||
const double absError = 0.0;
|
||||
arma::mat reference = arma::randu(4, 800);
|
||||
KDE<GaussianKernel,
|
||||
metric::EuclideanDistance,
|
||||
arma::mat,
|
||||
tree::KDTree>
|
||||
kde(relError, absError, GaussianKernel(0.25));
|
||||
kde.Train(reference);
|
||||
|
||||
// Get estimations to compare.
|
||||
arma::mat query = arma::randu(4, 100);;
|
||||
arma::vec estimations = arma::vec(query.n_cols, arma::fill::zeros);
|
||||
kde.Evaluate(query, estimations);
|
||||
|
||||
// Initialize serialized objects.
|
||||
KDE<GaussianKernel,
|
||||
metric::EuclideanDistance,
|
||||
arma::mat,
|
||||
tree::KDTree> kdeXml, kdeText, kdeBinary;
|
||||
SerializeObjectAll(kde, kdeXml, kdeText, kdeBinary);
|
||||
|
||||
// Check everything is correct.
|
||||
BOOST_REQUIRE_CLOSE(kde.RelativeError(), relError, 1e-8);
|
||||
BOOST_REQUIRE_CLOSE(kdeXml.RelativeError(), relError, 1e-8);
|
||||
BOOST_REQUIRE_CLOSE(kdeText.RelativeError(), relError, 1e-8);
|
||||
BOOST_REQUIRE_CLOSE(kdeBinary.RelativeError(), relError, 1e-8);
|
||||
|
||||
BOOST_REQUIRE_CLOSE(kde.AbsoluteError(), absError, 1e-8);
|
||||
BOOST_REQUIRE_CLOSE(kdeXml.AbsoluteError(), absError, 1e-8);
|
||||
BOOST_REQUIRE_CLOSE(kdeText.AbsoluteError(), absError, 1e-8);
|
||||
BOOST_REQUIRE_CLOSE(kdeBinary.AbsoluteError(), absError, 1e-8);
|
||||
|
||||
BOOST_REQUIRE_EQUAL(kde.IsTrained(), true);
|
||||
BOOST_REQUIRE_EQUAL(kdeXml.IsTrained(), true);
|
||||
BOOST_REQUIRE_EQUAL(kdeText.IsTrained(), true);
|
||||
BOOST_REQUIRE_EQUAL(kdeBinary.IsTrained(), true);
|
||||
|
||||
const KDEMode mode = KDEMode::DUAL_TREE_MODE;
|
||||
BOOST_REQUIRE_EQUAL(kde.Mode(), mode);
|
||||
BOOST_REQUIRE_EQUAL(kdeXml.Mode(), mode);
|
||||
BOOST_REQUIRE_EQUAL(kdeText.Mode(), mode);
|
||||
BOOST_REQUIRE_EQUAL(kdeBinary.Mode(), mode);
|
||||
|
||||
// Test if execution gives the same result.
|
||||
arma::vec xmlEstimations = arma::vec(query.n_cols, arma::fill::zeros);
|
||||
arma::vec textEstimations = arma::vec(query.n_cols, arma::fill::zeros);
|
||||
arma::vec binEstimations = arma::vec(query.n_cols, arma::fill::zeros);
|
||||
|
||||
kdeXml.Evaluate(query, xmlEstimations);
|
||||
kdeText.Evaluate(query, textEstimations);
|
||||
kdeBinary.Evaluate(query, binEstimations);
|
||||
|
||||
for (size_t i = 0; i < query.n_cols; ++i)
|
||||
{
|
||||
BOOST_REQUIRE_CLOSE(estimations[i], xmlEstimations[i], relError*100);
|
||||
BOOST_REQUIRE_CLOSE(estimations[i], textEstimations[i], relError*100);
|
||||
BOOST_REQUIRE_CLOSE(estimations[i], binEstimations[i], relError*100);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the copy constructor and copy operator works properly.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(CopyConstructor)
|
||||
{
|
||||
arma::mat reference = arma::randu(2, 300);
|
||||
arma::mat query = arma::randu(2, 100);
|
||||
arma::vec estimations1, estimations2, estimations3;
|
||||
const double kernelBandwidth = 1.5;
|
||||
const double relError = 0.05;
|
||||
|
||||
typedef KDE<GaussianKernel, metric::EuclideanDistance, arma::mat>
|
||||
KDEType;
|
||||
|
||||
// KDE.
|
||||
KDEType kde(relError, 0, kernel::GaussianKernel(kernelBandwidth));
|
||||
kde.Train(std::move(reference));
|
||||
|
||||
// Copy constructor KDE.
|
||||
KDEType constructor(kde);
|
||||
|
||||
// Copy operator KDE.
|
||||
KDEType oper = kde;
|
||||
|
||||
// Evaluations.
|
||||
kde.Evaluate(query, estimations1);
|
||||
constructor.Evaluate(query, estimations2);
|
||||
oper.Evaluate(query, estimations3);
|
||||
|
||||
// Check results.
|
||||
for (size_t i = 0; i < query.n_cols; ++i)
|
||||
{
|
||||
BOOST_REQUIRE_CLOSE(estimations1[i], estimations2[i], 1e-10);
|
||||
BOOST_REQUIRE_CLOSE(estimations2[i], estimations3[i], 1e-10);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the move constructor works properly.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(MoveConstructor)
|
||||
{
|
||||
arma::mat reference = arma::randu(2, 300);
|
||||
arma::mat query = arma::randu(2, 100);
|
||||
arma::vec estimations1, estimations2, estimations3;
|
||||
const double kernelBandwidth = 1.2;
|
||||
const double relError = 0.05;
|
||||
|
||||
typedef KDE<EpanechnikovKernel, metric::EuclideanDistance, arma::mat>
|
||||
KDEType;
|
||||
|
||||
// KDE.
|
||||
KDEType kde(relError, 0, kernel::EpanechnikovKernel(kernelBandwidth));
|
||||
kde.Train(std::move(reference));
|
||||
kde.Evaluate(query, estimations1);
|
||||
|
||||
// Move constructor KDE.
|
||||
KDEType constructor(std::move(kde));
|
||||
constructor.Evaluate(query, estimations2);
|
||||
|
||||
// Check results.
|
||||
BOOST_REQUIRE_THROW(kde.Evaluate(query, estimations3), std::runtime_error);
|
||||
for (size_t i = 0; i < query.n_cols; ++i)
|
||||
BOOST_REQUIRE_CLOSE(estimations1[i], estimations2[i], 1e-10);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if an untrained KDE works properly.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(NotTrained)
|
||||
{
|
||||
arma::mat query = arma::randu(1, 10);
|
||||
std::vector<size_t> oldFromNew;
|
||||
arma::vec estimations;
|
||||
|
||||
KDE<> kde;
|
||||
KDE<>::Tree queryTree(query, oldFromNew);
|
||||
|
||||
// Check results.
|
||||
BOOST_REQUIRE_THROW(kde.Evaluate(query, estimations), std::runtime_error);
|
||||
BOOST_REQUIRE_THROW(kde.Evaluate(&queryTree, oldFromNew, estimations),
|
||||
std::runtime_error);
|
||||
BOOST_REQUIRE_THROW(kde.Evaluate(estimations), std::runtime_error);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END();
|
||||
@@ -0,0 +1,408 @@
|
||||
/**
|
||||
* @file kde_test.cpp
|
||||
* @author Roberto Hueso
|
||||
*
|
||||
* Test mlpackMain() of kde_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 <string>
|
||||
|
||||
#define BINDING_TYPE BINDING_TYPE_TEST
|
||||
|
||||
static const std::string testName = "KDE";
|
||||
|
||||
#include <mlpack/core.hpp>
|
||||
#include <mlpack/core/util/mlpack_main.hpp>
|
||||
#include "test_helper.hpp"
|
||||
#include <mlpack/methods/kde/kde_main.cpp>
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include "../test_tools.hpp"
|
||||
|
||||
using namespace mlpack;
|
||||
|
||||
struct KDETestFixture
|
||||
{
|
||||
public:
|
||||
KDETestFixture()
|
||||
{
|
||||
// Cache in the options for this program.
|
||||
CLI::RestoreSettings(testName);
|
||||
}
|
||||
|
||||
~KDETestFixture()
|
||||
{
|
||||
// Clear the settings.
|
||||
CLI::ClearSettings();
|
||||
}
|
||||
};
|
||||
|
||||
void ResetKDESettings()
|
||||
{
|
||||
CLI::ClearSettings();
|
||||
CLI::RestoreSettings(testName);
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_SUITE(KDEMainTest, KDETestFixture);
|
||||
|
||||
/**
|
||||
* Ensure that the estimations we get for KDEMain, are the same as the ones we
|
||||
* get from the KDE class without any wrappers. Requires normalization.
|
||||
**/
|
||||
BOOST_AUTO_TEST_CASE(KDEGaussianRTreeResultsMain)
|
||||
{
|
||||
// Datasets.
|
||||
arma::mat reference = arma::randu(3, 500);
|
||||
arma::mat query = arma::randu(3, 100);
|
||||
arma::vec kdeEstimations, mainEstimations;
|
||||
double kernelBandwidth = 1.5;
|
||||
double relError = 0.05;
|
||||
|
||||
kernel::GaussianKernel kernel(kernelBandwidth);
|
||||
metric::EuclideanDistance metric;
|
||||
KDE<kernel::GaussianKernel,
|
||||
metric::EuclideanDistance,
|
||||
arma::mat,
|
||||
tree::RTree>
|
||||
kde(relError, 0.0, kernel, KDEMode::DUAL_TREE_MODE, metric);
|
||||
kde.Train(reference);
|
||||
kde.Evaluate(query, kdeEstimations);
|
||||
// Normalize estimations.
|
||||
kdeEstimations /= kernel.Normalizer(reference.n_rows);
|
||||
|
||||
// Main estimations.
|
||||
SetInputParam("reference", reference);
|
||||
SetInputParam("query", query);
|
||||
SetInputParam("kernel", std::string("gaussian"));
|
||||
SetInputParam("tree", std::string("r-tree"));
|
||||
SetInputParam("rel_error", relError);
|
||||
SetInputParam("bandwidth", kernelBandwidth);
|
||||
|
||||
mlpackMain();
|
||||
|
||||
mainEstimations = std::move(CLI::GetParam<arma::vec>("predictions"));
|
||||
|
||||
// Check whether results are equal.
|
||||
for (size_t i = 0; i < query.n_cols; ++i)
|
||||
BOOST_REQUIRE_CLOSE(kdeEstimations[i], mainEstimations[i], relError);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that the estimations we get for KDEMain, are the same as the ones we
|
||||
* get from the KDE class without any wrappers. Doesn't require normalization.
|
||||
**/
|
||||
BOOST_AUTO_TEST_CASE(KDETriangularBallTreeResultsMain)
|
||||
{
|
||||
// Datasets.
|
||||
arma::mat reference = arma::randu(3, 300);
|
||||
arma::mat query = arma::randu(3, 100);
|
||||
arma::vec kdeEstimations, mainEstimations;
|
||||
double kernelBandwidth = 3.0;
|
||||
double relError = 0.06;
|
||||
|
||||
kernel::TriangularKernel kernel(kernelBandwidth);
|
||||
metric::EuclideanDistance metric;
|
||||
KDE<kernel::TriangularKernel,
|
||||
metric::EuclideanDistance,
|
||||
arma::mat,
|
||||
tree::BallTree>
|
||||
kde(relError, 0.0, kernel, KDEMode::DUAL_TREE_MODE, metric);
|
||||
kde.Train(reference);
|
||||
kde.Evaluate(query, kdeEstimations);
|
||||
|
||||
// Main estimations.
|
||||
SetInputParam("reference", reference);
|
||||
SetInputParam("query", query);
|
||||
SetInputParam("kernel", std::string("triangular"));
|
||||
SetInputParam("tree", std::string("ball-tree"));
|
||||
SetInputParam("rel_error", relError);
|
||||
SetInputParam("bandwidth", kernelBandwidth);
|
||||
|
||||
mlpackMain();
|
||||
|
||||
mainEstimations = std::move(CLI::GetParam<arma::vec>("predictions"));
|
||||
|
||||
// Check whether results are equal.
|
||||
for (size_t i = 0; i < query.n_cols; ++i)
|
||||
BOOST_REQUIRE_CLOSE(kdeEstimations[i], mainEstimations[i], relError);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that the estimations we get for KDEMain, are the same as the ones we
|
||||
* get from the KDE class without any wrappers in the monochromatic case.
|
||||
**/
|
||||
BOOST_AUTO_TEST_CASE(KDEMonoResultsMain)
|
||||
{
|
||||
// Datasets.
|
||||
arma::mat reference = arma::randu(2, 300);
|
||||
arma::vec kdeEstimations, mainEstimations;
|
||||
double kernelBandwidth = 2.3;
|
||||
double relError = 0.05;
|
||||
|
||||
kernel::EpanechnikovKernel kernel(kernelBandwidth);
|
||||
metric::EuclideanDistance metric;
|
||||
KDE<kernel::EpanechnikovKernel,
|
||||
metric::EuclideanDistance,
|
||||
arma::mat,
|
||||
tree::StandardCoverTree>
|
||||
kde(relError, 0.0, kernel, KDEMode::DUAL_TREE_MODE, metric);
|
||||
kde.Train(reference);
|
||||
// Perform monochromatic KDE.
|
||||
kde.Evaluate(kdeEstimations);
|
||||
// Normalize.
|
||||
kdeEstimations /= kernel.Normalizer(reference.n_rows);
|
||||
|
||||
// Main estimations.
|
||||
SetInputParam("reference", reference);
|
||||
SetInputParam("kernel", std::string("epanechnikov"));
|
||||
SetInputParam("tree", std::string("cover-tree"));
|
||||
SetInputParam("rel_error", relError);
|
||||
SetInputParam("bandwidth", kernelBandwidth);
|
||||
|
||||
mlpackMain();
|
||||
|
||||
mainEstimations = std::move(CLI::GetParam<arma::vec>("predictions"));
|
||||
|
||||
// Check whether results are equal.
|
||||
for (size_t i = 0; i < reference.n_cols; ++i)
|
||||
BOOST_REQUIRE_CLOSE(kdeEstimations[i], mainEstimations[i], relError);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensuring that absence of input data is checked.
|
||||
**/
|
||||
BOOST_AUTO_TEST_CASE(KDENoInputData)
|
||||
{
|
||||
// No input data is not provided. Should throw a runtime error.
|
||||
Log::Fatal.ignoreInput = true;
|
||||
BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error);
|
||||
Log::Fatal.ignoreInput = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check result has as many densities as query points.
|
||||
**/
|
||||
BOOST_AUTO_TEST_CASE(KDEOutputSize)
|
||||
{
|
||||
const size_t dim = 3;
|
||||
const size_t samples = 110;
|
||||
arma::mat reference = arma::randu<arma::mat>(dim, 325);
|
||||
arma::mat query = arma::randu<arma::mat>(dim, samples);
|
||||
|
||||
// Main params.
|
||||
SetInputParam("reference", reference);
|
||||
SetInputParam("query", query);
|
||||
|
||||
mlpackMain();
|
||||
// Check number of output elements.
|
||||
BOOST_REQUIRE_EQUAL(CLI::GetParam<arma::vec>("predictions").size(), samples);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that saved model can be reused.
|
||||
**/
|
||||
BOOST_AUTO_TEST_CASE(KDEModelReuse)
|
||||
{
|
||||
const size_t dim = 3;
|
||||
const size_t samples = 100;
|
||||
const double relError = 0.05;
|
||||
arma::mat reference = arma::randu<arma::mat>(dim, 300);
|
||||
arma::mat query = arma::randu<arma::mat>(dim, samples);
|
||||
|
||||
// Main params.
|
||||
SetInputParam("reference", reference);
|
||||
SetInputParam("query", query);
|
||||
SetInputParam("bandwidth", 2.4);
|
||||
SetInputParam("rel_error", 0.05);
|
||||
|
||||
mlpackMain();
|
||||
|
||||
arma::vec oldEstimations = std::move(CLI::GetParam<arma::vec>("predictions"));
|
||||
|
||||
// Change parameters and load model.
|
||||
CLI::GetSingleton().Parameters()["reference"].wasPassed = false;
|
||||
SetInputParam("bandwidth", 0.5);
|
||||
SetInputParam("query", query);
|
||||
SetInputParam("input_model",
|
||||
std::move(CLI::GetParam<KDEModel*>("output_model")));
|
||||
|
||||
mlpackMain();
|
||||
|
||||
arma::vec newEstimations = std::move(CLI::GetParam<arma::vec>("predictions"));
|
||||
|
||||
// Check estimations are the same.
|
||||
for (size_t i = 0; i < samples; ++i)
|
||||
BOOST_REQUIRE_CLOSE(oldEstimations[i], newEstimations[i], relError);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that the estimations we get for KDEMain, are the same as the ones we
|
||||
* get from the KDE class without any wrappers using single-tree mode.
|
||||
**/
|
||||
BOOST_AUTO_TEST_CASE(KDEGaussianSingleKDTreeResultsMain)
|
||||
{
|
||||
// Datasets.
|
||||
arma::mat reference = arma::randu(3, 400);
|
||||
arma::mat query = arma::randu(3, 400);
|
||||
arma::vec kdeEstimations, mainEstimations;
|
||||
double kernelBandwidth = 3.0;
|
||||
double relError = 0.06;
|
||||
|
||||
kernel::GaussianKernel kernel(kernelBandwidth);
|
||||
metric::EuclideanDistance metric;
|
||||
KDE<kernel::GaussianKernel,
|
||||
metric::EuclideanDistance,
|
||||
arma::mat,
|
||||
tree::BallTree>
|
||||
kde(relError, 0.0, kernel, KDEMode::SINGLE_TREE_MODE, metric);
|
||||
kde.Train(reference);
|
||||
kde.Evaluate(query, kdeEstimations);
|
||||
kdeEstimations /= kernel.Normalizer(reference.n_rows);
|
||||
|
||||
// Main estimations.
|
||||
SetInputParam("reference", reference);
|
||||
SetInputParam("query", query);
|
||||
SetInputParam("kernel", std::string("gaussian"));
|
||||
SetInputParam("tree", std::string("kd-tree"));
|
||||
SetInputParam("algorithm", std::string("single-tree"));
|
||||
SetInputParam("rel_error", relError);
|
||||
SetInputParam("bandwidth", kernelBandwidth);
|
||||
|
||||
mlpackMain();
|
||||
|
||||
mainEstimations = std::move(CLI::GetParam<arma::vec>("predictions"));
|
||||
|
||||
// Check whether results are equal.
|
||||
for (size_t i = 0; i < query.n_cols; ++i)
|
||||
BOOST_REQUIRE_CLOSE(kdeEstimations[i], mainEstimations[i], relError);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure we get an exception when an invalid kernel is specified.
|
||||
**/
|
||||
BOOST_AUTO_TEST_CASE(KDEMainInvalidKernel)
|
||||
{
|
||||
arma::mat reference = arma::randu<arma::mat>(2, 10);
|
||||
arma::mat query = arma::randu<arma::mat>(2, 5);
|
||||
|
||||
// Main params.
|
||||
SetInputParam("reference", reference);
|
||||
SetInputParam("query", query);
|
||||
SetInputParam("kernel", std::string("linux"));
|
||||
|
||||
Log::Fatal.ignoreInput = true;
|
||||
BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error);
|
||||
Log::Fatal.ignoreInput = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure we get an exception when an invalid tree is specified.
|
||||
**/
|
||||
BOOST_AUTO_TEST_CASE(KDEMainInvalidTree)
|
||||
{
|
||||
arma::mat reference = arma::randu<arma::mat>(2, 10);
|
||||
arma::mat query = arma::randu<arma::mat>(2, 5);
|
||||
|
||||
// Main params.
|
||||
SetInputParam("reference", reference);
|
||||
SetInputParam("query", query);
|
||||
SetInputParam("tree", std::string("olive"));
|
||||
|
||||
Log::Fatal.ignoreInput = true;
|
||||
BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error);
|
||||
Log::Fatal.ignoreInput = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure we get an exception when an invalid algorithm is specified.
|
||||
**/
|
||||
BOOST_AUTO_TEST_CASE(KDEMainInvalidAlgorithm)
|
||||
{
|
||||
arma::mat reference = arma::randu<arma::mat>(2, 10);
|
||||
arma::mat query = arma::randu<arma::mat>(2, 5);
|
||||
|
||||
// Main params.
|
||||
SetInputParam("reference", reference);
|
||||
SetInputParam("query", query);
|
||||
SetInputParam("algorithm", std::string("bogosort"));
|
||||
|
||||
Log::Fatal.ignoreInput = true;
|
||||
BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error);
|
||||
Log::Fatal.ignoreInput = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure we get an exception when both reference and input_model are
|
||||
* specified.
|
||||
**/
|
||||
BOOST_AUTO_TEST_CASE(KDEMainReferenceAndModel)
|
||||
{
|
||||
arma::mat reference = arma::randu<arma::mat>(2, 10);
|
||||
arma::mat query = arma::randu<arma::mat>(2, 5);
|
||||
KDEModel* model = new KDEModel();
|
||||
|
||||
// Main params.
|
||||
SetInputParam("reference", reference);
|
||||
SetInputParam("query", query);
|
||||
SetInputParam("input_model", model);
|
||||
|
||||
Log::Fatal.ignoreInput = true;
|
||||
BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error);
|
||||
Log::Fatal.ignoreInput = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure we get an exception when an invalid absolute error is specified.
|
||||
**/
|
||||
BOOST_AUTO_TEST_CASE(KDEMainInvalidAbsoluteError)
|
||||
{
|
||||
arma::mat reference = arma::randu<arma::mat>(1, 10);
|
||||
arma::mat query = arma::randu<arma::mat>(1, 5);
|
||||
|
||||
// Main params.
|
||||
SetInputParam("reference", reference);
|
||||
SetInputParam("query", query);
|
||||
|
||||
Log::Fatal.ignoreInput = true;
|
||||
// Invalid value.
|
||||
SetInputParam("abs_error", -0.1);
|
||||
BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error);
|
||||
|
||||
// Valid value.
|
||||
SetInputParam("abs_error", 5.8);
|
||||
BOOST_REQUIRE_NO_THROW(mlpackMain());
|
||||
Log::Fatal.ignoreInput = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure we get an exception when an invalid relative error is specified.
|
||||
**/
|
||||
BOOST_AUTO_TEST_CASE(KDEMainInvalidRelativeError)
|
||||
{
|
||||
arma::mat reference = arma::randu<arma::mat>(1, 10);
|
||||
arma::mat query = arma::randu<arma::mat>(1, 5);
|
||||
|
||||
// Main params.
|
||||
SetInputParam("reference", reference);
|
||||
SetInputParam("query", query);
|
||||
|
||||
Log::Fatal.ignoreInput = true;
|
||||
// Invalid under 0.
|
||||
SetInputParam("rel_error", -0.1);
|
||||
BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error);
|
||||
|
||||
// Invalid over 1.
|
||||
SetInputParam("rel_error", 1.1);
|
||||
BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error);
|
||||
|
||||
// Valid value.
|
||||
SetInputParam("rel_error", 0.3);
|
||||
BOOST_REQUIRE_NO_THROW(mlpackMain());
|
||||
Log::Fatal.ignoreInput = false;
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END();
|
||||
@@ -58,7 +58,7 @@ BOOST_FIXTURE_TEST_SUITE(NMFMainTest, NMFTestFixture);
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(NMFMultdistShapeTest)
|
||||
{
|
||||
mat v = randu<mat>(10, 10);
|
||||
mat v = randu<mat>(8, 10);
|
||||
int r = 5;
|
||||
|
||||
SetInputParam("update_rules", std::string("multdist"));
|
||||
@@ -73,7 +73,7 @@ BOOST_AUTO_TEST_CASE(NMFMultdistShapeTest)
|
||||
const mat& h = CLI::GetParam<mat>("h");
|
||||
|
||||
// Check the shapes of W and H.
|
||||
BOOST_REQUIRE_EQUAL(w.n_rows, 10);
|
||||
BOOST_REQUIRE_EQUAL(w.n_rows, 8);
|
||||
BOOST_REQUIRE_EQUAL(w.n_cols, 5);
|
||||
BOOST_REQUIRE_EQUAL(h.n_rows, 5);
|
||||
BOOST_REQUIRE_EQUAL(h.n_cols, 10);
|
||||
@@ -85,7 +85,7 @@ BOOST_AUTO_TEST_CASE(NMFMultdistShapeTest)
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(NMFMultdivShapeTest)
|
||||
{
|
||||
mat v = randu<mat>(10, 10);
|
||||
mat v = randu<mat>(8, 10);
|
||||
int r = 5;
|
||||
|
||||
SetInputParam("update_rules", std::string("multdiv"));
|
||||
@@ -100,7 +100,7 @@ BOOST_AUTO_TEST_CASE(NMFMultdivShapeTest)
|
||||
const mat& h = CLI::GetParam<mat>("h");
|
||||
|
||||
// Check the shapes of W and H.
|
||||
BOOST_REQUIRE_EQUAL(w.n_rows, 10);
|
||||
BOOST_REQUIRE_EQUAL(w.n_rows, 8);
|
||||
BOOST_REQUIRE_EQUAL(w.n_cols, 5);
|
||||
BOOST_REQUIRE_EQUAL(h.n_rows, 5);
|
||||
BOOST_REQUIRE_EQUAL(h.n_cols, 10);
|
||||
@@ -112,7 +112,7 @@ BOOST_AUTO_TEST_CASE(NMFMultdivShapeTest)
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(NMFAlsShapeTest)
|
||||
{
|
||||
mat v = randu<mat>(10, 10);
|
||||
mat v = randu<mat>(8, 10);
|
||||
int r = 5;
|
||||
|
||||
SetInputParam("update_rules", std::string("als"));
|
||||
@@ -127,7 +127,7 @@ BOOST_AUTO_TEST_CASE(NMFAlsShapeTest)
|
||||
const mat& h = CLI::GetParam<mat>("h");
|
||||
|
||||
// Check the shapes of W and H.
|
||||
BOOST_REQUIRE_EQUAL(w.n_rows, 10);
|
||||
BOOST_REQUIRE_EQUAL(w.n_rows, 8);
|
||||
BOOST_REQUIRE_EQUAL(w.n_cols, 5);
|
||||
BOOST_REQUIRE_EQUAL(h.n_rows, 5);
|
||||
BOOST_REQUIRE_EQUAL(h.n_cols, 10);
|
||||
|
||||
@@ -0,0 +1,574 @@
|
||||
/**
|
||||
* @file range_search_test.cpp
|
||||
* @author Niteya Shah
|
||||
*
|
||||
* Test mlpackMain() of range_search_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.
|
||||
*/
|
||||
#define BINDING_TYPE BINDING_TYPE_TEST
|
||||
static const std::string testName = "RangeSearchMain";
|
||||
|
||||
#include <mlpack/core.hpp>
|
||||
#include <mlpack/core/util/mlpack_main.hpp>
|
||||
#include "test_helper.hpp"
|
||||
#include <mlpack/methods/range_search/range_search_main.cpp>
|
||||
#include "range_search_utils.hpp"
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
using namespace mlpack;
|
||||
|
||||
struct RangeSearchTestFixture
|
||||
{
|
||||
public:
|
||||
|
||||
RangeSearchTestFixture()
|
||||
{
|
||||
// Cache in the options for this program.
|
||||
CLI::RestoreSettings(testName);
|
||||
}
|
||||
~RangeSearchTestFixture()
|
||||
{
|
||||
// Clear the settings.
|
||||
bindings::tests::CleanMemory();
|
||||
CLI::ClearSettings();
|
||||
}
|
||||
};
|
||||
|
||||
BOOST_FIXTURE_TEST_SUITE(RangeSearchMainTest, RangeSearchTestFixture);
|
||||
|
||||
/**
|
||||
* Check that we have to specify a reference set or input model.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(RangeSearchNoReference)
|
||||
{
|
||||
Log::Fatal.ignoreInput = true;
|
||||
BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error);
|
||||
Log::Fatal.ignoreInput = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that we cannot pass an incorrect parameter.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(RangeSearchWrongParameter)
|
||||
{
|
||||
string wrongString = "abc";
|
||||
|
||||
Log::Fatal.ignoreInput = true;
|
||||
BOOST_REQUIRE_THROW(SetInputParam("RST", wrongString), std::runtime_error);
|
||||
Log::Fatal.ignoreInput = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that we have to specify a query if an input model is specified.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(RangeSearchInputModelNoQuery)
|
||||
{
|
||||
arma::mat inputData;
|
||||
double minVal = 0, maxVal = 3;
|
||||
string distanceFile = "distances.csv";
|
||||
string neighborsFile = "neighbors.csv";
|
||||
|
||||
if (!data::Load("iris.csv", inputData))
|
||||
BOOST_FAIL("Unable to load dataset iris.csv!");
|
||||
|
||||
SetInputParam("reference", move(inputData));
|
||||
SetInputParam("min", minVal);
|
||||
SetInputParam("max", maxVal);
|
||||
SetInputParam("distances_file", distanceFile);
|
||||
SetInputParam("neighbors_file", neighborsFile);
|
||||
|
||||
mlpackMain();
|
||||
|
||||
CLI::GetSingleton().Parameters()["reference"].wasPassed = false;
|
||||
SetInputParam("input_model", move(CLI::GetParam<RSModel*>("output_model")));
|
||||
|
||||
Log::Fatal.ignoreInput = true;
|
||||
BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error);
|
||||
Log::Fatal.ignoreInput = false;
|
||||
|
||||
remove(neighborsFile.c_str());
|
||||
remove(distanceFile.c_str());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that we cannot specify a tree type which is not available or wrong.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(RangeSearchDifferentTree)
|
||||
{
|
||||
arma::mat inputData;
|
||||
double minVal = 0, maxVal = 3;
|
||||
string distanceFile = "distances.csv";
|
||||
string neighborsFile = "neighbors.csv";
|
||||
string wrongTreeType = "RST";
|
||||
if (!data::Load("iris.csv", inputData))
|
||||
BOOST_FAIL("Unable to load dataset iris.csv!");
|
||||
|
||||
SetInputParam("reference", move(inputData));
|
||||
SetInputParam("min", minVal);
|
||||
SetInputParam("max", maxVal);
|
||||
SetInputParam("distances_file", distanceFile);
|
||||
SetInputParam("neighbors_file", neighborsFile);
|
||||
SetInputParam("tree_type", wrongTreeType);
|
||||
|
||||
Log::Fatal.ignoreInput = true;
|
||||
BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error);
|
||||
Log::Fatal.ignoreInput = false;
|
||||
|
||||
remove(neighborsFile.c_str());
|
||||
remove(distanceFile.c_str());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that we cannot specify both a reference set and input model.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(RangeSearchBothReferenceAndModel)
|
||||
{
|
||||
arma::mat inputData, queryData;
|
||||
double minVal = 0, maxVal = 3;
|
||||
string distanceFile = "distances.csv";
|
||||
string neighborsFile = "neighbors.csv";
|
||||
|
||||
if (!data::Load("iris.csv", inputData))
|
||||
BOOST_FAIL("Unable to load dataset iris.csv!");
|
||||
if (!data::Load("iris_test.csv", queryData))
|
||||
BOOST_FAIL("Unable to load dataset iris_test.csv!");
|
||||
|
||||
SetInputParam("reference", move(inputData));
|
||||
SetInputParam("min", minVal);
|
||||
SetInputParam("max", maxVal);
|
||||
SetInputParam("distances_file", distanceFile);
|
||||
SetInputParam("neighbors_file", neighborsFile);
|
||||
SetInputParam("query", queryData);
|
||||
|
||||
mlpackMain();
|
||||
|
||||
SetInputParam("input_model", move(CLI::GetParam<RSModel*>("output_model")));
|
||||
SetInputParam("query", move(queryData));
|
||||
|
||||
Log::Fatal.ignoreInput = true;
|
||||
BOOST_REQUIRE_THROW(mlpackMain(), std::runtime_error);
|
||||
Log::Fatal.ignoreInput = false;
|
||||
|
||||
remove(neighborsFile.c_str());
|
||||
remove(distanceFile.c_str());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the correct output is returned for a small synthetic input case,
|
||||
* by comparing with pre-calculated neighbor and distance values, when no query
|
||||
* set is specified.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(RangeSearchTest)
|
||||
{
|
||||
arma::mat x = {{0, 3, 3, 4, 3, 1},
|
||||
{4, 4, 4, 5, 5, 2},
|
||||
{0, 1, 2, 2, 3, 3}};
|
||||
|
||||
string distanceFile = "distances.csv";
|
||||
string neighborsFile = "neighbors.csv";
|
||||
double minVal = 0, maxVal = 3;
|
||||
vector<vector<size_t>> neighborVal = {{},
|
||||
{2, 3, 4},
|
||||
{1, 3, 4, 5},
|
||||
{1, 2, 4},
|
||||
{1, 2, 3},
|
||||
{2}};
|
||||
vector<vector<double>> distanceVal = {{},
|
||||
{1, 1.73205, 2.23607},
|
||||
{1, 1.41421, 1.41421, 3},
|
||||
{1.73205, 1.41421, 1.41421},
|
||||
{2.23607, 1.41421, 1.41421},
|
||||
{3}};
|
||||
|
||||
vector<vector<size_t>> neighbors;
|
||||
vector<vector<double>> distances;
|
||||
|
||||
SetInputParam("reference", move(x));
|
||||
SetInputParam("min", minVal);
|
||||
SetInputParam("max", maxVal);
|
||||
SetInputParam("distances_file", distanceFile);
|
||||
SetInputParam("neighbors_file", neighborsFile);
|
||||
|
||||
mlpackMain();
|
||||
|
||||
neighbors = ReadData<size_t>(neighborsFile);
|
||||
distances = ReadData<double>(distanceFile);
|
||||
|
||||
CheckMatrices(neighbors, neighborVal);
|
||||
CheckMatrices(distances, distanceVal);
|
||||
|
||||
remove(neighborsFile.c_str());
|
||||
remove(distanceFile.c_str());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the correct output is returned for a small synthetic input case,
|
||||
* when a query set is provided.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(RangeSeachTestwithQuery)
|
||||
{
|
||||
arma::mat queryData = {{5, 3, 1}, {4, 2, 4}, {3, 1, 7}};
|
||||
arma::mat x = {{0, 3, 3, 4, 3, 1},
|
||||
{4, 4, 4, 5, 5, 2},
|
||||
{0, 1, 2, 2, 3, 3}};
|
||||
|
||||
vector<vector<double>> distanceVal = {
|
||||
{2.82843, 2.23607, 1.73205, 2.23607, 4.47214},
|
||||
{3.74166, 2, 2.23607, 3.31662, 3.60555, 2.82843},
|
||||
{4.58258, 4.47214}};
|
||||
vector<vector<size_t>> neighborVal = {{1, 2, 3, 4, 5},
|
||||
{0, 1, 2, 3, 4, 5},
|
||||
{4, 5}};
|
||||
|
||||
vector<vector<size_t>> neighbors;
|
||||
vector<vector<double>> distances;
|
||||
string distanceFile = "distances.csv";
|
||||
string neighborsFile = "neighbors.csv";
|
||||
double minVal = 0, maxVal = 5;
|
||||
|
||||
SetInputParam("query", queryData);
|
||||
SetInputParam("reference", move(x));
|
||||
SetInputParam("min", minVal);
|
||||
SetInputParam("max", maxVal);
|
||||
SetInputParam("distances_file", distanceFile);
|
||||
SetInputParam("neighbors_file", neighborsFile);
|
||||
|
||||
mlpackMain();
|
||||
|
||||
neighbors = ReadData<size_t>(neighborsFile);
|
||||
distances = ReadData<double>(distanceFile);
|
||||
|
||||
CheckMatrices(neighbors, neighborVal);
|
||||
CheckMatrices(distances, distanceVal);
|
||||
|
||||
remove(neighborsFile.c_str());
|
||||
remove(distanceFile.c_str());
|
||||
}
|
||||
|
||||
/**
|
||||
* Train a model using a synthetic dataset and then output the model, and ensure
|
||||
* it can be used again.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(ModelCheck)
|
||||
{
|
||||
arma::mat inputData, queryData;
|
||||
double minVal = 0, maxVal = 3;
|
||||
string distanceFile = "distances.csv";
|
||||
string neighborsFile = "neighbors.csv";
|
||||
vector<vector<size_t>> neighbors, neighborsTemp;
|
||||
vector<vector<double>> distances, distancetemp;
|
||||
|
||||
if (!data::Load("iris.csv", inputData))
|
||||
BOOST_FAIL("Unable to load dataset iris.csv!");
|
||||
if (!data::Load("iris_test.csv", queryData))
|
||||
BOOST_FAIL("Unable to load dataset iris_test.csv!");
|
||||
|
||||
SetInputParam("reference", move(inputData));
|
||||
SetInputParam("min", minVal);
|
||||
SetInputParam("max", maxVal);
|
||||
SetInputParam("distances_file", distanceFile);
|
||||
SetInputParam("neighbors_file", neighborsFile);
|
||||
SetInputParam("query", queryData);
|
||||
|
||||
mlpackMain();
|
||||
|
||||
neighbors = ReadData<size_t>(neighborsFile);
|
||||
distances = ReadData<double>(distanceFile);
|
||||
|
||||
RSModel* outputModel = move(CLI::GetParam<RSModel*>("output_model"));
|
||||
CLI::GetSingleton().Parameters()["reference"].wasPassed = false;
|
||||
|
||||
SetInputParam("input_model", outputModel);
|
||||
SetInputParam("query", move(queryData));
|
||||
|
||||
mlpackMain();
|
||||
|
||||
neighborsTemp = ReadData<size_t>(neighborsFile);
|
||||
distancetemp = ReadData<double>(distanceFile);
|
||||
|
||||
CheckMatrices(neighbors, neighborsTemp);
|
||||
CheckMatrices(distances, distancetemp);
|
||||
|
||||
BOOST_REQUIRE_EQUAL(ModelToString(outputModel),
|
||||
ModelToString(CLI::GetParam<RSModel*>("output_model")));
|
||||
|
||||
remove(neighborsFile.c_str());
|
||||
remove(distanceFile.c_str());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the models are different but the results are the same for three
|
||||
* different leaf size parameters.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(LeafValueTesting)
|
||||
{
|
||||
arma::mat inputData;
|
||||
if (!data::Load("iris.csv", inputData))
|
||||
BOOST_FAIL("Unable to load dataset iris.csv!");
|
||||
|
||||
string distanceFile = "distances.csv";
|
||||
string neighborsFile = "neighbors.csv";
|
||||
double minVal = 0, maxVal = 3;
|
||||
|
||||
vector<vector<size_t>> neighbors, neighborsTemp;
|
||||
vector<vector<double>> distances, distancestemp;
|
||||
|
||||
vector<int> leafSizes {20, 15, 25};
|
||||
|
||||
SetInputParam("reference", inputData);
|
||||
SetInputParam("min", minVal);
|
||||
SetInputParam("max", maxVal);
|
||||
SetInputParam("distances_file", distanceFile);
|
||||
SetInputParam("neighbors_file", neighborsFile);
|
||||
SetInputParam("leaf_size", leafSizes[0]);
|
||||
// The default leaf size is 20.
|
||||
|
||||
mlpackMain();
|
||||
|
||||
RSModel* outputModel1 = CLI::GetParam<RSModel*>("output_model");
|
||||
neighbors = ReadData<size_t>(neighborsFile);
|
||||
distances = ReadData<double>(distanceFile);
|
||||
|
||||
for (size_t i = 1; i < leafSizes.size(); i++)
|
||||
{
|
||||
SetInputParam("leaf_size", leafSizes[i]);
|
||||
SetInputParam("reference", inputData);
|
||||
SetInputParam("min", minVal);
|
||||
SetInputParam("max", maxVal);
|
||||
SetInputParam("distances_file", distanceFile);
|
||||
SetInputParam("neighbors_file", neighborsFile);
|
||||
|
||||
mlpackMain();
|
||||
|
||||
neighborsTemp = ReadData<size_t>(neighborsFile);
|
||||
distancestemp = ReadData<double>(distanceFile);
|
||||
|
||||
CheckMatrices(neighbors, neighborsTemp);
|
||||
CheckMatrices(distances, distancestemp);
|
||||
|
||||
BOOST_REQUIRE_NE(ModelToString(outputModel1),
|
||||
ModelToString(CLI::GetParam<RSModel*>("output_model")));
|
||||
}
|
||||
|
||||
remove(neighborsFile.c_str());
|
||||
remove(distanceFile.c_str());
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure that the models are different but the results are the same for
|
||||
* different tree types. We use the default kd-tree as the base model to
|
||||
* compare against.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(TreeTypeTesting)
|
||||
{
|
||||
string distanceFile = "distances.csv";
|
||||
string neighborsFile = "neighbors.csv";
|
||||
|
||||
double minVal = 0, maxVal = 3;
|
||||
arma::mat queryData, inputData;
|
||||
vector<vector<size_t>> neighbors, neighborsTemp;
|
||||
vector<vector<double>> distances, distancestemp;
|
||||
vector<string> trees = {"kd", "cover", "r", "r-star", "ball", "x",
|
||||
"hilbert-r", "r-plus", "r-plus-plus", "vp","rp",
|
||||
"max-rp", "ub", "oct"};
|
||||
|
||||
if (!data::Load("iris.csv", inputData))
|
||||
BOOST_FAIL("Unable to load dataset iris.csv!");
|
||||
if (!data::Load("iris_test.csv", queryData))
|
||||
BOOST_FAIL("Unable to load dataset iris_test.csv!");
|
||||
|
||||
// Define base parameters with the kd-tree.
|
||||
SetInputParam("tree_type", trees[0]);
|
||||
SetInputParam("min", minVal);
|
||||
SetInputParam("max", maxVal);
|
||||
SetInputParam("distances_file", distanceFile);
|
||||
SetInputParam("neighbors_file", neighborsFile);
|
||||
SetInputParam("reference", inputData);
|
||||
SetInputParam("query", queryData);
|
||||
|
||||
mlpackMain();
|
||||
|
||||
neighbors = ReadData<size_t>(neighborsFile);
|
||||
distances = ReadData<double>(distanceFile);
|
||||
RSModel* outputModel1=CLI::GetParam<RSModel*>("output_model");
|
||||
|
||||
for (size_t i = 1;i < trees.size(); i++)
|
||||
{
|
||||
if (!data::Load("iris.csv", inputData))
|
||||
BOOST_FAIL("Unable to load dataset iris.csv!");
|
||||
if (!data::Load("iris_test.csv", queryData))
|
||||
BOOST_FAIL("Unable to load dataset iris_test.csv!");
|
||||
|
||||
SetInputParam("min", minVal);
|
||||
SetInputParam("max", maxVal);
|
||||
SetInputParam("distances_file", distanceFile);
|
||||
SetInputParam("neighbors_file", neighborsFile);
|
||||
SetInputParam("query", queryData);
|
||||
SetInputParam("reference", inputData);
|
||||
SetInputParam("tree_type", trees[i]);
|
||||
|
||||
mlpackMain();
|
||||
|
||||
neighborsTemp = ReadData<size_t>(neighborsFile);
|
||||
distancestemp = ReadData<double>(distanceFile);
|
||||
|
||||
CheckMatrices(neighbors, neighborsTemp);
|
||||
CheckMatrices(distances, distancestemp);
|
||||
BOOST_REQUIRE_NE(ModelToString(outputModel1),
|
||||
ModelToString(CLI::GetParam<RSModel*>("output_model")));
|
||||
}
|
||||
|
||||
remove(neighborsFile.c_str());
|
||||
remove(distanceFile.c_str());
|
||||
}
|
||||
|
||||
/**
|
||||
* Project the data onto a random basis and ensure that this gives identical
|
||||
* results to non-projected data but different models.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(RandomBasisTesting)
|
||||
{
|
||||
string distanceFile = "distances.csv";
|
||||
string neighborsFile = "neighbors.csv";
|
||||
double minVal = 0, maxVal = 3;
|
||||
|
||||
arma::mat queryData, inputData;
|
||||
if (!data::Load("iris.csv", inputData))
|
||||
BOOST_FAIL("Unable to load dataset iris.csv!");
|
||||
if (!data::Load("iris_test.csv", queryData))
|
||||
BOOST_FAIL("Unable to load dataset iris_test.csv!");
|
||||
|
||||
SetInputParam("min", minVal);
|
||||
SetInputParam("max", maxVal);
|
||||
SetInputParam("distances_file", distanceFile);
|
||||
SetInputParam("neighbors_file", neighborsFile);
|
||||
SetInputParam("reference", inputData);
|
||||
|
||||
mlpackMain();
|
||||
|
||||
RSModel* outputModel = move(CLI::GetParam<RSModel*>("output_model"));
|
||||
|
||||
SetInputParam("min", minVal);
|
||||
SetInputParam("max", maxVal);
|
||||
SetInputParam("distances_file", distanceFile);
|
||||
SetInputParam("neighbors_file", neighborsFile);
|
||||
SetInputParam("reference", inputData);
|
||||
SetInputParam("random_basis",true);
|
||||
|
||||
mlpackMain();
|
||||
|
||||
BOOST_REQUIRE_NE(ModelToString(outputModel),
|
||||
ModelToString(CLI::GetParam<RSModel*>("output_model")));
|
||||
|
||||
remove(neighborsFile.c_str());
|
||||
remove(distanceFile.c_str());
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that naive mode gives the same result, but different models.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(NaiveModeTest)
|
||||
{
|
||||
string distanceFile = "distances.csv";
|
||||
string neighborsFile = "neighbors.csv";
|
||||
double minVal = 0, maxVal = 3;
|
||||
|
||||
arma::mat queryData, inputData;
|
||||
vector<vector<size_t>> neighbors, neighborsTemp;
|
||||
vector<vector<double>> distances, distancestemp;
|
||||
|
||||
if (!data::Load("iris.csv", inputData))
|
||||
BOOST_FAIL("Unable to load dataset iris.csv!");
|
||||
if (!data::Load("iris_test.csv", queryData))
|
||||
BOOST_FAIL("Unable to load dataset iris_test.csv!");
|
||||
|
||||
SetInputParam("min", minVal);
|
||||
SetInputParam("max", maxVal);
|
||||
SetInputParam("distances_file", distanceFile);
|
||||
SetInputParam("neighbors_file", neighborsFile);
|
||||
SetInputParam("reference", inputData);
|
||||
|
||||
mlpackMain();
|
||||
|
||||
neighbors = ReadData<size_t>(neighborsFile);
|
||||
distances = ReadData<double>(distanceFile);
|
||||
RSModel* outputModel = move(CLI::GetParam<RSModel*>("output_model"));
|
||||
|
||||
SetInputParam("min", minVal);
|
||||
SetInputParam("max", maxVal);
|
||||
SetInputParam("distances_file", distanceFile);
|
||||
SetInputParam("neighbors_file", neighborsFile);
|
||||
SetInputParam("reference", inputData);
|
||||
SetInputParam("naive", true);
|
||||
|
||||
mlpackMain();
|
||||
|
||||
neighborsTemp = ReadData<size_t>(neighborsFile);
|
||||
distancestemp = ReadData<double>(distanceFile);
|
||||
|
||||
CheckMatrices(neighbors, neighborsTemp);
|
||||
CheckMatrices(distances, distancestemp);
|
||||
|
||||
BOOST_REQUIRE_NE(ModelToString(outputModel),
|
||||
ModelToString(CLI::GetParam<RSModel*>("output_model")));
|
||||
|
||||
remove(neighborsFile.c_str());
|
||||
remove(distanceFile.c_str());
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that single-tree mode gives the same result but different models.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(SingleModeTest)
|
||||
{
|
||||
string distanceFile = "distances.csv";
|
||||
string neighborsFile = "neighbors.csv";
|
||||
double minVal = 0, maxVal = 3;
|
||||
|
||||
arma::mat queryData, inputData;
|
||||
vector<vector<size_t>> neighbors, neighborsTemp;
|
||||
vector<vector<double>> distances, distancestemp;
|
||||
|
||||
if (!data::Load("iris.csv", inputData))
|
||||
BOOST_FAIL("Unable to load dataset iris.csv!");
|
||||
if (!data::Load("iris_test.csv", queryData))
|
||||
BOOST_FAIL("Unable to load dataset iris_test.csv!");
|
||||
|
||||
SetInputParam("min", minVal);
|
||||
SetInputParam("max", maxVal);
|
||||
SetInputParam("distances_file", distanceFile);
|
||||
SetInputParam("neighbors_file", neighborsFile);
|
||||
SetInputParam("reference", inputData);
|
||||
|
||||
mlpackMain();
|
||||
|
||||
neighbors = ReadData<size_t>(neighborsFile);
|
||||
distances = ReadData<double>(distanceFile);
|
||||
RSModel* outputModel = move(CLI::GetParam<RSModel*>("output_model"));
|
||||
|
||||
SetInputParam("min", minVal);
|
||||
SetInputParam("max", maxVal);
|
||||
SetInputParam("distances_file", distanceFile);
|
||||
SetInputParam("neighbors_file", neighborsFile);
|
||||
SetInputParam("reference", inputData);
|
||||
SetInputParam("single_mode", true);
|
||||
|
||||
mlpackMain();
|
||||
|
||||
neighborsTemp = ReadData<size_t>(neighborsFile);
|
||||
distancestemp = ReadData<double>(distanceFile);
|
||||
|
||||
CheckMatrices(neighbors, neighborsTemp);
|
||||
CheckMatrices(distances, distancestemp);
|
||||
BOOST_REQUIRE_NE(ModelToString(outputModel),
|
||||
ModelToString(CLI::GetParam<RSModel*>("output_model")));
|
||||
|
||||
remove(neighborsFile.c_str());
|
||||
remove(distanceFile.c_str());
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END();
|
||||
@@ -0,0 +1,107 @@
|
||||
/**
|
||||
* @file range_search_utils.hpp
|
||||
* @author Niteya Shah
|
||||
*
|
||||
* Helper functions used in the execution of the Range Search test.
|
||||
*
|
||||
* 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_TESTS_MAIN_TESTS_RANGE_SEARCH_TEST_UTILS_HPP
|
||||
#define MLPACK_TESTS_MAIN_TESTS_RANGE_SEARCH_TEST_UTILS_HPP
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include <mlpack/methods/range_search/rs_model.hpp>
|
||||
#include <mlpack/core.hpp>
|
||||
#include <mlpack/core/util/mlpack_main.hpp>
|
||||
|
||||
/**
|
||||
* Convert a model to a string using the text_oarchive of boost::serialization.
|
||||
*
|
||||
* @param model RSModel to be converted to string.
|
||||
*/
|
||||
inline std::string ModelToString(RSModel* model)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
boost::archive::text_oarchive oa(oss);
|
||||
oa << model;
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that 2 matrices of type vector<vector<double>> are close to equal,
|
||||
* using the given tolerance.
|
||||
*
|
||||
* @param vec1 First vector to compare.
|
||||
* @param vec2 Second vector to compare.
|
||||
* @param tolerance Allowed tolerance for values.
|
||||
*/
|
||||
inline void CheckMatrices(std::vector<std::vector<double>>& vec1,
|
||||
std::vector<std::vector<double>>& vec2,
|
||||
const double tolerance = 1e-3)
|
||||
{
|
||||
BOOST_REQUIRE_EQUAL(vec1.size() , vec2.size());
|
||||
for (size_t i = 0; i < vec1.size(); i++)
|
||||
{
|
||||
BOOST_REQUIRE_EQUAL(vec1[i].size(), vec2[i].size());
|
||||
std::sort(vec1[i].begin(), vec1[i].end());
|
||||
std::sort(vec2[i].begin(), vec2[i].end());
|
||||
for (size_t j = 0 ; j < vec1[i].size(); j++)
|
||||
{
|
||||
BOOST_REQUIRE_CLOSE(vec1[i][j], vec2[i][j], tolerance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that 2 matrices of type vector<vector<size_t>> are equal.
|
||||
*
|
||||
* @param vec1 First vector to compare.
|
||||
* @param vec2 Second vector to compare.
|
||||
*/
|
||||
inline void CheckMatrices(std::vector<std::vector<size_t>>& vec1,
|
||||
std::vector<std::vector<size_t>>& vec2)
|
||||
{
|
||||
BOOST_REQUIRE_EQUAL(vec1.size() , vec2.size());
|
||||
for (size_t i = 0; i < vec1.size(); i++)
|
||||
{
|
||||
BOOST_REQUIRE_EQUAL(vec1[i].size(), vec2[i].size());
|
||||
std::sort(vec1[i].begin(), vec1[i].end());
|
||||
std::sort(vec2[i].begin(), vec2[i].end());
|
||||
for (size_t j = 0; j < vec1[i].size(); j++)
|
||||
{
|
||||
BOOST_REQUIRE_EQUAL(vec1[i][j], vec2[i][j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a CSV file into a vector of vector with a templated datatype. Any ','
|
||||
* characters are stripped from the input; lines are split on '\n' and elements
|
||||
* of each line are split on spaces.
|
||||
*
|
||||
* @param filename Name of the file to load.
|
||||
*/
|
||||
template<typename T>
|
||||
std::vector<std::vector<T>> ReadData(const std::string& filename)
|
||||
{
|
||||
std::ifstream ifs(filename);
|
||||
std::vector<std::vector<T>> table;
|
||||
std::string line;
|
||||
while (std::getline(ifs, line))
|
||||
{
|
||||
std::vector<T> numbers;
|
||||
T n;
|
||||
std::replace(line.begin(), line.end(), ',', ' ');
|
||||
std::istringstream stm(line);
|
||||
while (stm >> n)
|
||||
numbers.push_back(n);
|
||||
table.push_back(numbers);
|
||||
}
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -19,7 +19,7 @@
|
||||
#include <mlpack/methods/ann/loss_functions/mean_squared_error.hpp>
|
||||
#include <mlpack/methods/reinforcement_learning/q_learning.hpp>
|
||||
#include <mlpack/methods/reinforcement_learning/environment/mountain_car.hpp>
|
||||
#include <mlpack/methods/reinforcement_learning/environment/acrobat.hpp>
|
||||
#include <mlpack/methods/reinforcement_learning/environment/acrobot.hpp>
|
||||
#include <mlpack/methods/reinforcement_learning/environment/cart_pole.hpp>
|
||||
#include <mlpack/methods/reinforcement_learning/policy/greedy_policy.hpp>
|
||||
#include <mlpack/methods/reinforcement_learning/training_config.hpp>
|
||||
@@ -173,8 +173,8 @@ BOOST_AUTO_TEST_CASE(CartPoleWithDoubleDQN)
|
||||
BOOST_REQUIRE(converged);
|
||||
}
|
||||
|
||||
//! Test DQN in Acrobat task.
|
||||
BOOST_AUTO_TEST_CASE(AcrobatWithDQN)
|
||||
//! Test DQN in Acrobot task.
|
||||
BOOST_AUTO_TEST_CASE(AcrobotWithDQN)
|
||||
{
|
||||
// We will allow three trials, although it would be very uncommon for the test
|
||||
// to use more than one.
|
||||
@@ -191,8 +191,8 @@ BOOST_AUTO_TEST_CASE(AcrobatWithDQN)
|
||||
model.Add<Linear<>>(32, 3);
|
||||
|
||||
// Set up the policy and replay method.
|
||||
GreedyPolicy<Acrobat> policy(1.0, 1000, 0.1);
|
||||
RandomReplay<Acrobat> replayMethod(20, 10000);
|
||||
GreedyPolicy<Acrobot> policy(1.0, 1000, 0.1);
|
||||
RandomReplay<Acrobot> replayMethod(20, 10000);
|
||||
|
||||
TrainingConfig config;
|
||||
config.StepSize() = 0.01;
|
||||
@@ -203,7 +203,7 @@ BOOST_AUTO_TEST_CASE(AcrobatWithDQN)
|
||||
config.StepLimit() = 400;
|
||||
|
||||
// Set up DQN agent.
|
||||
QLearning<Acrobat, decltype(model), AdamUpdate, decltype(policy)>
|
||||
QLearning<Acrobot, decltype(model), AdamUpdate, decltype(policy)>
|
||||
agent(std::move(config), std::move(model), std::move(policy),
|
||||
std::move(replayMethod));
|
||||
|
||||
@@ -218,13 +218,13 @@ BOOST_AUTO_TEST_CASE(AcrobatWithDQN)
|
||||
|
||||
if (episodes > 1000)
|
||||
{
|
||||
Log::Debug << "Acrobat with DQN failed." << std::endl;
|
||||
Log::Debug << "Acrobot with DQN failed." << std::endl;
|
||||
converged = false;
|
||||
break;
|
||||
}
|
||||
|
||||
/**
|
||||
* I am using a thresold of -380 to check convegence.
|
||||
* I am using a threshold of -380 to check convergence.
|
||||
*/
|
||||
Log::Debug << "Average return: " << averageReturn.mean()
|
||||
<< " Episode return: " << episodeReturn << std::endl;
|
||||
@@ -251,4 +251,69 @@ BOOST_AUTO_TEST_CASE(AcrobatWithDQN)
|
||||
BOOST_REQUIRE_EQUAL(success, true);
|
||||
}
|
||||
|
||||
//! Test DQN in Mountain Car task.
|
||||
BOOST_AUTO_TEST_CASE(MountainCarWithDQN)
|
||||
{
|
||||
// Set up the network.
|
||||
FFN<MeanSquaredError<>, GaussianInitialization> model(MeanSquaredError<>(),
|
||||
GaussianInitialization(0, 0.001));
|
||||
model.Add<Linear<>>(2, 64);
|
||||
model.Add<ReLULayer<>>();
|
||||
model.Add<Linear<>>(64, 32);
|
||||
model.Add<ReLULayer<>>();
|
||||
model.Add<Linear<>>(32, 3);
|
||||
|
||||
// Set up the policy and replay method.
|
||||
GreedyPolicy<MountainCar> policy(1.0, 1000, 0.1);
|
||||
RandomReplay<MountainCar> replayMethod(20, 10000);
|
||||
|
||||
TrainingConfig config;
|
||||
config.StepSize() = 0.0001;
|
||||
config.Discount() = 0.9;
|
||||
config.TargetNetworkSyncInterval() = 100;
|
||||
config.ExplorationSteps() = 100;
|
||||
config.DoubleQLearning() = false;
|
||||
config.StepLimit() = 400;
|
||||
|
||||
// Set up DQN agent.
|
||||
QLearning<MountainCar, decltype(model), AdamUpdate, decltype(policy)>
|
||||
agent(std::move(config), std::move(model), std::move(policy),
|
||||
std::move(replayMethod));
|
||||
|
||||
arma::running_stat<double> averageReturn;
|
||||
size_t episodes = 0;
|
||||
bool converged = true;
|
||||
while (true)
|
||||
{
|
||||
double episodeReturn = agent.Episode();
|
||||
averageReturn(episodeReturn);
|
||||
episodes += 1;
|
||||
|
||||
if (episodes > 1000)
|
||||
{
|
||||
Log::Debug << "Mountain Car with DQN failed." << std::endl;
|
||||
converged = false;
|
||||
break;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a threshold of -370 to check convergence.
|
||||
*/
|
||||
Log::Debug << "Average return: " << averageReturn.mean()
|
||||
<< " Episode return: " << episodeReturn << std::endl;
|
||||
if (averageReturn.mean() > -370)
|
||||
{
|
||||
agent.Deterministic() = true;
|
||||
arma::running_stat<double> testReturn;
|
||||
for (size_t i = 0; i < 10; ++i)
|
||||
testReturn(agent.Episode());
|
||||
|
||||
Log::Debug << "Average return in deterministic test: "
|
||||
<< testReturn.mean() << std::endl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
BOOST_REQUIRE(converged);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END();
|
||||
|
||||
@@ -238,7 +238,7 @@ BOOST_AUTO_TEST_CASE(UnweightedCategoricalLearningTest)
|
||||
size_t rfCorrect = arma::accu(rfPredictions == testLabels);
|
||||
size_t dtCorrect = arma::accu(dtPredictions == testLabels);
|
||||
|
||||
BOOST_REQUIRE_GE(rfCorrect, dtCorrect - 30);
|
||||
BOOST_REQUIRE_GE(rfCorrect, dtCorrect - 50);
|
||||
BOOST_REQUIRE_GE(rfCorrect, size_t(0.7 * testData.n_cols));
|
||||
}
|
||||
|
||||
@@ -295,7 +295,7 @@ BOOST_AUTO_TEST_CASE(WeightedCategoricalLearningTest)
|
||||
size_t rfCorrect = arma::accu(rfPredictions == testLabels);
|
||||
size_t dtCorrect = arma::accu(dtPredictions == testLabels);
|
||||
|
||||
BOOST_REQUIRE_GE(rfCorrect, dtCorrect - 30);
|
||||
BOOST_REQUIRE_GE(rfCorrect, dtCorrect - 50);
|
||||
BOOST_REQUIRE_GE(rfCorrect, size_t(0.7 * testData.n_cols));
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
#include <mlpack/methods/reinforcement_learning/environment/mountain_car.hpp>
|
||||
#include <mlpack/methods/reinforcement_learning/environment/continuous_mountain_car.hpp>
|
||||
#include <mlpack/methods/reinforcement_learning/environment/cart_pole.hpp>
|
||||
#include <mlpack/methods/reinforcement_learning/environment/acrobat.hpp>
|
||||
#include <mlpack/methods/reinforcement_learning/environment/acrobot.hpp>
|
||||
#include <mlpack/methods/reinforcement_learning/environment/pendulum.hpp>
|
||||
#include <mlpack/methods/reinforcement_learning/environment/reward_clipping.hpp>
|
||||
|
||||
@@ -54,8 +54,8 @@ BOOST_AUTO_TEST_CASE(ClippedRewardTest)
|
||||
BOOST_REQUIRE(reward >= -2.0);
|
||||
}
|
||||
|
||||
//! Test DQN in Acrobat task.
|
||||
BOOST_AUTO_TEST_CASE(RewardClippedAcrobatWithDQN)
|
||||
//! Test DQN in Acrobot task.
|
||||
BOOST_AUTO_TEST_CASE(RewardClippedAcrobotWithDQN)
|
||||
{
|
||||
// Set up the network.
|
||||
FFN<MeanSquaredError<>, GaussianInitialization> model(MeanSquaredError<>(),
|
||||
@@ -67,12 +67,12 @@ BOOST_AUTO_TEST_CASE(RewardClippedAcrobatWithDQN)
|
||||
model.Add<Linear<>>(32, 3);
|
||||
|
||||
// Set up the policy and replay method.
|
||||
GreedyPolicy<RewardClipping<Acrobat>> policy(1.0, 1000, 0.1);
|
||||
RandomReplay<RewardClipping<Acrobat>> replayMethod(20, 10000);
|
||||
GreedyPolicy<RewardClipping<Acrobot>> policy(1.0, 1000, 0.1);
|
||||
RandomReplay<RewardClipping<Acrobot>> replayMethod(20, 10000);
|
||||
|
||||
// Set up Acrobat task and reward clipping wrapper
|
||||
Acrobat task;
|
||||
RewardClipping<Acrobat> rewardClipping(task, -2.0, +2.0);
|
||||
// Set up Acrobot task and reward clipping wrapper
|
||||
Acrobot task;
|
||||
RewardClipping<Acrobot> rewardClipping(task, -2.0, +2.0);
|
||||
|
||||
// Set up update rule
|
||||
AdamUpdate update;
|
||||
@@ -102,7 +102,7 @@ BOOST_AUTO_TEST_CASE(RewardClippedAcrobatWithDQN)
|
||||
|
||||
if (episodes > 1000)
|
||||
{
|
||||
Log::Debug << "Acrobat with DQN failed." << std::endl;
|
||||
Log::Debug << "Acrobot with DQN failed." << std::endl;
|
||||
converged = false;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
#include <mlpack/methods/reinforcement_learning/environment/mountain_car.hpp>
|
||||
#include <mlpack/methods/reinforcement_learning/environment/continuous_mountain_car.hpp>
|
||||
#include <mlpack/methods/reinforcement_learning/environment/cart_pole.hpp>
|
||||
#include <mlpack/methods/reinforcement_learning/environment/acrobat.hpp>
|
||||
#include <mlpack/methods/reinforcement_learning/environment/acrobot.hpp>
|
||||
#include <mlpack/methods/reinforcement_learning/environment/pendulum.hpp>
|
||||
#include <mlpack/methods/reinforcement_learning/replay/random_replay.hpp>
|
||||
#include <mlpack/methods/reinforcement_learning/policy/greedy_policy.hpp>
|
||||
@@ -68,20 +68,20 @@ BOOST_AUTO_TEST_CASE(SimpleContinuousMountainCarTest)
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a Acrobat instance and check if the main rountine works as
|
||||
* Constructs a Acrobot instance and check if the main rountine works as
|
||||
* it should be.
|
||||
*/
|
||||
BOOST_AUTO_TEST_CASE(SimpleAcrobatTest)
|
||||
BOOST_AUTO_TEST_CASE(SimpleAcrobotTest)
|
||||
{
|
||||
const Acrobat task = Acrobat();
|
||||
const Acrobot task = Acrobot();
|
||||
|
||||
Acrobat::State state = task.InitialSample();
|
||||
Acrobat::Action action = Acrobat::Action::negativeTorque;
|
||||
Acrobot::State state = task.InitialSample();
|
||||
Acrobot::Action action = Acrobot::Action::negativeTorque;
|
||||
double reward = task.Sample(state, action);
|
||||
|
||||
BOOST_REQUIRE_EQUAL(reward, -1.0);
|
||||
BOOST_REQUIRE(!task.IsTerminal(state));
|
||||
BOOST_REQUIRE_EQUAL(3, Acrobat::Action::size);
|
||||
BOOST_REQUIRE_EQUAL(3, Acrobot::Action::size);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user