diff --git a/.ci/ci.yaml b/.ci/ci.yaml
index 7a1f41de0b..ab57443861 100644
--- a/.ci/ci.yaml
+++ b/.ci/ci.yaml
@@ -19,16 +19,15 @@ jobs:
# RAM usage.
CMakeArgs: '-DDEBUG=ON -DPROFILE=OFF -DBUILD_TESTS=ON -DBUILD_PYTHON_BINDINGS=OFF -DBUILD_JULIA_BINDINGS=OFF -DBUILD_GO_BINDINGS=OFF -DBUILD_R_BINDINGS=OFF -DUSE_PRECOMPILED_HEADERS=OFF'
Python:
+ CMakeArgs: '-DDEBUG=OFF -DPROFILE=OFF -DBUILD_TESTS=ON -DBUILD_PYTHON_BINDINGS=ON -DPYTHON_EXECUTABLE=/usr/bin/python3 -DBUILD_GO_BINDINGS=OFF -DBUILD_JULIA_BINDINGS=OFF -DBUILD_R_BINDINGS=OFF'
binding: 'python'
python.version: '3.7'
- CMakeArgs: '-DDEBUG=OFF -DPROFILE=OFF -DBUILD_TESTS=ON -DBUILD_PYTHON_BINDINGS=ON -DPYTHON_EXECUTABLE=/usr/bin/python3 -DBUILD_GO_BINDINGS=OFF -DBUILD_JULIA_BINDINGS=OFF -DBUILD_R_BINDINGS=OFF'
Julia:
- julia.version: '1.3.0'
- CMakeArgs: '-DDEBUG=OFF -DPROFILE=OFF -DBUILD_TESTS=ON -DBUILD_PYTHON_BINDINGS=OFF -DBUILD_JULIA_BINDINGS=ON -DBUILD_GO_BINDINGS=OFF -DJULIA_EXECUTABLE=/opt/julia-1.6.3/bin/julia -DBUILD_R_BINDINGS=OFF'
+ CMakeArgs: '-DDEBUG=OFF -DPROFILE=OFF -DBUILD_TESTS=ON -DBUILD_PYTHON_BINDINGS=OFF -DBUILD_JULIA_BINDINGS=ON -DBUILD_GO_BINDINGS=OFF -DJULIA_EXECUTABLE=/opt/julia-1.10.4/bin/julia -DBUILD_R_BINDINGS=OFF'
+ binding: 'julia'
Go:
- binding: 'go'
- go.version: '1.11.0'
CMakeArgs: '-DDEBUG=OFF -DPROFILE=OFF -DBUILD_TESTS=ON -DBUILD_PYTHON_BINDINGS=OFF -DBUILD_JULIA_BINDINGS=OFF -DBUILD_GO_BINDINGS=ON -DBUILD_R_BINDINGS=OFF'
+ binding: 'go'
Markdown:
CMakeArgs: '-DDEBUG=OFF -DPROFILE=OFF -DBUILD_TESTS=ON -DBUILD_MARKDOWN_BINDINGS=ON -DBUILD_PYTHON_BINDINGS=OFF -DBUILD_GO_BINDINGS=OFF -DBUILD_JULIA_BINDINGS=OFF -DBUILD_R_BINDINGS=OFF'
@@ -44,20 +43,19 @@ jobs:
# clang on OS X segfaults when using precompiled headers, so we disable
# them.
Plain:
+ python.version: '3.8'
CMakeArgs: '-DDEBUG=ON -DPROFILE=OFF -DBUILD_TESTS=ON -DBUILD_PYTHON_BINDINGS=OFF -DBUILD_JULIA_BINDINGS=OFF -DBUILD_GO_BINDINGS=OFF -DBUILD_R_BINDINGS=OFF -DUSE_PRECOMPILED_HEADERS=OFF'
- python.version: '3.8'
Python:
- binding: 'python'
python.version: '3.8'
+ binding: 'python'
CMakeArgs: '-DDEBUG=ON -DPROFILE=OFF -DBUILD_TESTS=ON -DBUILD_PYTHON_BINDINGS=ON -DBUILD_JULIA_BINDINGS=OFF -DBUILD_GO_BINDINGS=OFF -DBUILD_R_BINDINGS=OFF -DUSE_PRECOMPILED_HEADERS=OFF'
Julia:
python.version: '3.8'
- julia.version: '1.6.3'
+ binding: 'julia'
CMakeArgs: '-DDEBUG=ON -DPROFILE=OFF -DBUILD_TESTS=ON -DBUILD_JULIA_BINDINGS=ON -DBUILD_PYTHON_BINDINGS=OFF -DBUILD_GO_BINDINGS=OFF -DBUILD_R_BINDINGS=OFF -DUSE_PRECOMPILED_HEADERS=OFF'
Go:
- binding: 'go'
python.version: '3.8'
- go.version: '1.11.0'
+ binding: 'go'
CMakeArgs: '-DDEBUG=ON -DPROFILE=OFF -DBUILD_TESTS=ON -DBUILD_PYTHON_BINDINGS=OFF -DBUILD_JULIA_BINDINGS=OFF -DBUILD_GO_BINDINGS=ON -DBUILD_R_BINDINGS=OFF -DUSE_PRECOMPILED_HEADERS=OFF'
steps:
diff --git a/.ci/linux-steps.yaml b/.ci/linux-steps.yaml
index ebaa342552..cb669ba27e 100644
--- a/.ci/linux-steps.yaml
+++ b/.ci/linux-steps.yaml
@@ -7,7 +7,8 @@ steps:
# Set python version
- task: UsePythonVersion@0
inputs:
- versionSpec: '3.7'
+ versionSpec: '$(python.version)'
+ condition: ne(variables['python.version'], '')
# Install build dependencies.
- script: |
@@ -24,19 +25,18 @@ steps:
sudo apt-get install -y --allow-unauthenticated libopenblas-dev g++ xz-utils
- if [ "$(binding)" == "python" ]; then
- export PYBIN=$(which python)
- $PYBIN -m pip install --upgrade pip
- $PYBIN -m pip install --upgrade --ignore-installed setuptools cython pandas wheel
+ if [ "$BINDING" = "python" ]; then
+ python -m pip install --upgrade pip
+ python -m pip install --upgrade --ignore-installed setuptools cython pandas wheel
fi
- if [ "a$(julia.version)" != "a" ]; then
- wget https://julialang-s3.julialang.org/bin/linux/x64/1.6/julia-1.6.3-linux-x86_64.tar.gz
- sudo tar -C /opt/ -xvpf julia-1.6.3-linux-x86_64.tar.gz
+ if [ "$BINDING" = "julia" ]; then
+ wget https://julialang-s3.julialang.org/bin/linux/x64/1.10/julia-1.10.4-linux-x86_64.tar.gz
+ sudo tar -C /opt/ -xvpf julia-1.10.4-linux-x86_64.tar.gz
fi
# Install armadillo.
- curl -k -L https://sourceforge.net/projects/arma/files/armadillo-9.800.6.tar.xz | tar -xvJ && \
+ curl -k -L https://sourceforge.net/projects/arma/files/armadillo-10.8.2.tar.xz | tar -xvJ && \
cd armadillo* && \
cmake . && \
make && \
@@ -68,14 +68,13 @@ steps:
# Configure mlpack (CMake)
- script: |
- unset BOOST_ROOT
mkdir build && cd build
- if [ "$(binding)" == "go" ]; then
+ if [ "$BINDING" = "go" ]; then
export GOPATH=$PWD/src/mlpack/bindings/go
export GO111MODULE=off
go get -u -t gonum.org/v1/gonum/...
fi
- cmake $(CMakeArgs) -DPYTHON_EXECUTABLE=`which python` -DCEREAL_INCLUDE_DIR=/usr/include/ ..
+ cmake $CMAKEARGS -DPYTHON_EXECUTABLE=`which python` -DCEREAL_INCLUDE_DIR=/usr/include/ ..
displayName: 'CMake'
# Build mlpack
diff --git a/.ci/macos-steps.yaml b/.ci/macos-steps.yaml
index 0607b6d431..d6f177f28a 100644
--- a/.ci/macos-steps.yaml
+++ b/.ci/macos-steps.yaml
@@ -15,12 +15,12 @@ steps:
sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer
brew install libomp openblas armadillo cereal ensmallen
- if [ "$(binding)" == "python" ]; then
+ if [ "$BINDING" = "python" ]; then
pip install --upgrade pip
pip install cython numpy pandas zipp configparser wheel
fi
- if [ "a$(julia.version)" != "a" ]; then
+ if [ "$BINDING" = "julia" ]; then
brew install --cask julia
fi
@@ -29,16 +29,15 @@ steps:
# Configure mlpack (CMake)
- script: |
mkdir build && cd build
- if [ "$(binding)" == "go" ]; then
+ if [ "$BINDING" = "go" ]; then
export GOPATH=$PWD/src/mlpack/bindings/go
export GO111MODULE=off
go get -u -t gonum.org/v1/gonum/...
fi
- if [ "$(binding)" == "python" ]; then
- export PYPATH=$(which python)
- cmake $(CMakeArgs) -DPYTHON_EXECUTABLE=$PYPATH ..
+ if [ "$BINDING" = "python" ]; then
+ cmake $CMAKEARGS -DPYTHON_EXECUTABLE=$(which python) ..
else
- cmake $(CMakeArgs) ..
+ cmake $CMAKEARGS ..
fi
displayName: 'CMake'
diff --git a/.ci/windows-steps.yaml b/.ci/windows-steps.yaml
index 9d3a91baa2..4c615a428f 100644
--- a/.ci/windows-steps.yaml
+++ b/.ci/windows-steps.yaml
@@ -27,10 +27,10 @@ steps:
- bash: |
git clone --depth 1 https://github.com/mlpack/jenkins-conf.git conf
- curl -O -L https://sourceforge.net/projects/arma/files/armadillo-9.800.6.tar.xz -o armadillo-9.800.6.tar.xz
- tar -xvf armadillo-9.800.6.tar.xz
+ curl -O -L https://sourceforge.net/projects/arma/files/armadillo-10.8.2.tar.xz -o armadillo-10.8.2.tar.xz
+ tar -xvf armadillo-10.8.2.tar.xz
- cd armadillo-9.800.6/ && cmake $(CMakeGenerator) \
+ cd armadillo-10.8.2/ && cmake $(CMakeGenerator) \
-DBLAS_LIBRARY:FILEPATH=$(Agent.ToolsDirectory)/OpenBLAS.0.2.14.1/lib/native/lib/x64/libopenblas.dll.a \
-DLAPACK_LIBRARY:FILEPATH=$(Agent.ToolsDirectory)/OpenBLAS.0.2.14.1/lib/native/lib/x64/libopenblas.dll.a \
-DCMAKE_PREFIX:FILEPATH=../../armadillo \
@@ -41,7 +41,7 @@ steps:
# Build armadillo
- task: MSBuild@1
inputs:
- solution: 'armadillo-9.800.6/*.sln'
+ solution: 'armadillo-10.8.2/*.sln'
msbuildLocationMethod: 'location'
msbuildVersion: $(MSBuildVersion)
configuration: 'Release'
@@ -60,8 +60,8 @@ steps:
$(CMakeArgs) `
-DBLAS_LIBRARIES:FILEPATH=$(Agent.ToolsDirectory)\OpenBLAS.0.2.14.1\lib\native\lib\x64\libopenblas.dll.a `
-DLAPACK_LIBRARIES:FILEPATH=$(Agent.ToolsDirectory)\OpenBLAS.0.2.14.1\lib\native\lib\x64\libopenblas.dll.a `
- -DARMADILLO_INCLUDE_DIR="..\armadillo-9.800.6\tmp\include" `
- -DARMADILLO_LIBRARY="..\armadillo-9.800.6\Release\armadillo.lib" `
+ -DARMADILLO_INCLUDE_DIR="..\armadillo-10.8.2\tmp\include" `
+ -DARMADILLO_LIBRARY="..\armadillo-10.8.2\Release\armadillo.lib" `
-DCEREAL_INCLUDE_DIR="..\cereal-1.3.2\include" `
-DENSMALLEN_INCLUDE_DIR=$(Agent.ToolsDirectory)\ensmallen.2.17.0\installed\x64-linux\include `
-DBUILD_JULIA_BINDINGS=OFF `
diff --git a/.github/workflows/auto-approve.yml b/.github/workflows/auto-approve.yml
new file mode 100644
index 0000000000..1270e838d1
--- /dev/null
+++ b/.github/workflows/auto-approve.yml
@@ -0,0 +1,22 @@
+# Once a PR has been approved by one member of the mlpack organization, a second
+# approving review will automatically be added 24 hours later. This allows time
+# for other maintainers to take a look.
+name: Auto-approve pull requests
+on:
+ schedule:
+ # Run roughly every four hours.
+ - cron: "15 0,4,8,12,16,20 * * *"
+
+jobs:
+ auto-approve:
+ runs-on: ubuntu-latest
+ permissions:
+ pull-requests: write
+
+ steps:
+ - name: Auto-approve pull requests
+ uses: rcurtin/actions/auto-approve@v1
+ with:
+ repo-token: ${{ secrets.GITHUB_TOKEN }}
+ approval-message:
+ 'Second approval provided automatically after 24 hours. :+1:'
diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml
new file mode 100644
index 0000000000..8f59d84fbf
--- /dev/null
+++ b/.github/workflows/stale.yml
@@ -0,0 +1,24 @@
+name: Close inactive issues
+on:
+ schedule:
+ - cron: "30 1 * * *"
+
+jobs:
+ close-issues:
+ runs-on: ubuntu-latest
+ permissions:
+ issues: write
+ pull-requests: write
+ steps:
+ - uses: actions/stale@v9
+ with:
+ days-before-issues-stale: 30
+ days-before-issue-close: 7
+ stale-issue-label: "s: stale"
+ stale-pr-label: "s: stale"
+ stale-issue-message: "This issue has been automatically marked as stale because it has not had any recent activity. It will be closed in 7 days if no further activity occurs. Thank you for your contributions! :+1:"
+ days-before-pr-stale: 30,
+ days-before-pr-close: 14
+ repo-token: ${{ secrets.GITHUB_TOKEN }}
+ exempt-issue-labels: "s: keep open"
+ exempt-pr-labels: "s: keep open"
diff --git a/.github/workflows/stickers.yaml b/.github/workflows/stickers.yaml
new file mode 100644
index 0000000000..aafb7d390b
--- /dev/null
+++ b/.github/workflows/stickers.yaml
@@ -0,0 +1,17 @@
+# Post a message to new contributors that they can get some stickers mailed to
+# them.
+name: 'Stickers for new contributors'
+on:
+ pull_request:
+ types: [closed]
+
+jobs:
+ sticker_comment:
+ runs-on: ubuntu-latest
+ if: github.event.pull_request.merged == true
+ steps:
+ # Forked version of first-interaction that runs only on first merged PR.
+ - uses: rcurtin/actions/stickers@v1
+ with:
+ repo-token: ${{ secrets.GITHUB_TOKEN }}
+ pr-message: "Hello there! Thanks for your contribution. Congratulations on your first contribution to mlpack! If you'd like to add your name to the list of contributors in `COPYRIGHT.txt` and you haven't already, please feel free to push a change to this PR---or, if it gets merged before you can, feel free to open another PR.\n\nIn addition, if you'd like some stickers to put on your laptop, we can get them in the mail for you. Just send an email with your physical mailing address to stickers@mlpack.org, and then one of the mlpack maintainers will put some stickers in an envelope for you. It may take a few weeks to get them, depending on your location. :+1:"
diff --git a/.github/workflows/welcome-pr.yaml b/.github/workflows/welcome-pr.yaml
new file mode 100644
index 0000000000..00341842fb
--- /dev/null
+++ b/.github/workflows/welcome-pr.yaml
@@ -0,0 +1,16 @@
+# Post a message to new contributors that they can get some stickers mailed to
+# them.
+name: 'Welcome message for new contributors'
+on:
+ pull_request:
+ types: [open]
+
+jobs:
+ sticker_comment:
+ runs-on: ubuntu-latest
+ if: github.event.pull_request.merged == true
+ steps:
+ - uses: actions/first-interaction@v1.3.0
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
+ pr-message: "Thanks for opening your first pull request in this repository! Someone will review it when they have a chance. In the mean time, please be sure that you've handled the following things, to make the review process quicker and easier:\n\n - All code should follow the [style guide](https://github.com/mlpack/mlpack/wiki/DesignGuidelines#style-guidelines)\n - Documentation added for any new functionality\n - Tests added for any new functionality\n - Tests that are added follow the [testing guide](https://github.com/mlpack/mlpack/wiki/Testing-Guidelines)\n - Headers and license information added to the top of any new code files\n - HISTORY.md updated if the changes are big or user-facing\n - All CI checks should be passing\n\nThank you again for your contributions! :+1:"
diff --git a/CMake/ConfigureCrossCompile.cmake b/CMake/ConfigureCrossCompile.cmake
index a320d32932..0a39b829ed 100644
--- a/CMake/ConfigureCrossCompile.cmake
+++ b/CMake/ConfigureCrossCompile.cmake
@@ -28,6 +28,7 @@ macro(search_openblas version)
get_deps(https://github.com/xianyi/OpenBLAS/releases/download/v${version}/OpenBLAS-${version}.tar.gz OpenBLAS OpenBLAS-${version}.tar.gz)
if (NOT MSVC)
if (NOT EXISTS "${CMAKE_BINARY_DIR}/deps/OpenBLAS-${version}/libopenblas.a")
+ set(ENV{COMMON_OPT} "${CMAKE_OPENBLAS_FLAGS}") # Pass our flags to OpenBLAS
execute_process(COMMAND make TARGET=${OPENBLAS_TARGET} BINARY=${OPENBLAS_BINARY} HOSTCC=gcc CC=${CMAKE_C_COMPILER} FC=${CMAKE_FORTRAN_COMPILER} NO_SHARED=1
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/deps/OpenBLAS-${version})
endif()
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 75f6fe1b9d..ced015958b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -23,7 +23,7 @@ option(USE_PRECOMPILED_HEADERS "Use precompiled headers for mlpack_test build."
# For Armadillo, try to keep the minimum required version less than or equal to
# what's available on the current Ubuntu LTS or most recent stable RHEL release.
# See https://github.com/mlpack/mlpack/issues/3033 for some more discussion.
-set(ARMADILLO_VERSION "9.800")
+set(ARMADILLO_VERSION "10.8")
set(ENSMALLEN_VERSION "2.10.0")
set(CEREAL_VERSION "1.1.2")
@@ -249,8 +249,12 @@ if (DEBUG)
else()
add_definitions(-DNDEBUG)
if (NOT MSVC)
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3")
- set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -O3")
+ if (NOT CMAKE_CROSSCOMPILING)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3")
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -O3")
+ else()
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99")
+ endif()
else ()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /O3")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /O3")
@@ -283,7 +287,7 @@ endif()
# Download and compile OpenBLAS if we are cross compiling mlpack for a specific
# architecture. The function takes the version of OpenBLAS as variable.
if (CMAKE_CROSSCOMPILING)
- search_openblas(0.3.13)
+ search_openblas(0.3.26)
endif()
if (NOT DOWNLOAD_DEPENDENCIES)
diff --git a/HISTORY.md b/HISTORY.md
index 68f1bf4d91..3f6ef2d629 100644
--- a/HISTORY.md
+++ b/HISTORY.md
@@ -4,9 +4,19 @@
_????-??-??_
- * Distribute STB headers as part of R package (#3724, #3726).
- * Added OpenMP support for fast approximation (#3685).
+ * Distribute STB headers as part of R package (#3724, #3726).
+ * Added OpenMP parallelization to Hamerly, Naive, and Elkan k-means (#3761, #3762, #3764).
+
+ * Added OpenMP support for fast approximation (#3685).
+
+ * Implemented the Find and Fill algorithm into the Dropout Layer and added OpenMP support (#3684).
+
+ * Update Python bindings to support NumPy 2.x (#3752).
+
+ * Bump minimum Armadillo version to 10.8 (#3760).
+
+ * Adapt `NearestInterpolation` ANN layer to new Layer Inteface (#3768).
## mlpack 4.4.0
diff --git a/README.md b/README.md
index 7bf1bf301a..70c3c2ab01 100644
--- a/README.md
+++ b/README.md
@@ -108,7 +108,7 @@ Citations are beneficial for the growth and improvement of mlpack.
**mlpack** requires the following additional dependencies:
- C++17 compiler
- - [Armadillo](https://arma.sourceforge.net) >= 9.800
+ - [Armadillo](https://arma.sourceforge.net) >= 10.8
- [ensmallen](https://ensmallen.org) >= 2.10.0
- [cereal](http://uscilab.github.io/cereal/) >= 1.1.2
@@ -333,7 +333,7 @@ dependencies are installed:
- R >= 4.0
- Rcpp >= 0.12.12
- - RcppArmadillo >= 0.9.800.0
+ - RcppArmadillo >= 0.10.8.0
- RcppEnsmallen >= 0.2.10.0
- roxygen2
- testthat
diff --git a/board/flags-config.cmake b/board/flags-config.cmake
index 94b2060b44..5999ab86ac 100644
--- a/board/flags-config.cmake
+++ b/board/flags-config.cmake
@@ -4,63 +4,86 @@
# footprints.
# Set generic minimization flags for all platforms.
-# These flags are the same for all cross-compilation cases.
-set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Os -fdata-sections -ffunction-sections")
+# These flags are the same for all cross-compilation cases and they are
+# mainly to reduce the binary footprint.
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Os -s -fdata-sections -ffunction-sections")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fomit-frame-pointer -fno-unwind-tables")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-asynchronous-unwind-tables -fvisibility=hidden")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fshort-enums -finline-small-functions")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -findirect-inlining -fno-common")
-#-flto -fuse-ld=gold # There is an issue with gold link when compiling on
-# Ubuntu 16. At that point gcc linker did not integrate the flto support
-# inside and it was a separate plugin that need to be added. Therefore,
-# this can be added when mlpack Azure CI moves toward Ubuntu 20.
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fmerge-all-constants -fno-ident")
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-unroll-loops -fno-math-errno")
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-stack-protector")
+set(CMAKE_OPENBLAS_FLAGS "${CMAKE_CXX_FLAGS}") # OpenBLAS does not supoport flto
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -flto")
+set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--hash-style=gnu -Wl,--build-id=none")
+set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-z,norelro")
+set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections")
+## Keep the following flag in comment, they might be relevant in the case of MCU's
+#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wl,-nmagic,-Bsymbolic -nostartfiles")
set(BOARD_NAME "" CACHE STRING "Specify Board name to optimize for.")
string(TOUPPER ${BOARD_NAME} BOARD)
# Set specific platforms CMAKE CXX flags.
-if(BOARD MATCHES "RPI0" OR BOARD MATCHES "RPI1")
+if(BOARD MATCHES "RPI0" OR BOARD MATCHES "RPI1" OR BOARD MATCHES "ARM11")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mtune=arm1176jzf-s")
- set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcpu=arm1176jzf-s -mfloat-abi=hard -mfpu=vfp")
set(OPENBLAS_TARGET "ARMV6")
set(OPENBLAS_BINARY "32")
-elseif(BOARD MATCHES "RPI2")
+elseif(BOARD MATCHES "RPI2" OR BOARD MATCHES "CORTEXA7")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mtune=cortex-a7")
- set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfloat-abi=hard -mfpu=neon-vfpv4")
set(OPENBLAS_TARGET "ARMV7")
set(OPENBLAS_BINARY "32")
-elseif(BOARD MATCHES "RPI3")
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mtune=cortex-a53")
- set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections")
+elseif(BOARD MATCHES "CORTEXA8")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mtune=cortex-a8")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfloat-abi=hard -mfpu=neon")
+ set(OPENBLAS_TARGET "ARMV7")
+ set(OPENBLAS_BINARY "32")
+elseif(BOARD MATCHES "CORTEXA9")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mtune=cortex-a9")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfloat-abi=hard -mfpu=neon")
+ set(OPENBLAS_TARGET "CORTEXA9")
+ set(OPENBLAS_BINARY "32")
+elseif(BOARD MATCHES "CORTEXA15")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mtune=cortex-a15")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfloat-abi=hard -mfpu=neon")
+ set(OPENBLAS_TARGET "CORTEXA15")
+ set(OPENBLAS_BINARY "32")
+elseif(BOARD MATCHES "RPI3" OR BOARD MATCHES "CORTEXA53")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mtune=cortex-a53 -ftree-vectorize")
set(OPENBLAS_TARGET "CORTEXA53")
set(OPENBLAS_BINARY "64")
-elseif(BOARD MATCHES "RPI4")
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mtune=cortex-a72")
- set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections")
+elseif(BOARD MATCHES "RPI4" OR BOARD MATCHES "CORTEXA72")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mtune=cortex-a72 -ftree-vectorize")
set(OPENBLAS_TARGET "CORTEXA72")
set(OPENBLAS_BINARY "64")
+elseif(BOARD MATCHES "JETSONAGX" OR BOARD MATCHES "CORTEXA76")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mtune=cortex-a76 -ftree-vectorize")
+ set(OPENBLAS_TARGET "CORTEXA76")
+ set(OPENBLAS_BINARY "64")
elseif(BOARD MATCHES "BV")
- set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections")
set(OPENBLAS_TARGET "RISCV64_GENERIC")
set(OPENBLAS_BINARY "64")
-elseif(BOARD MATCHES "JETSONAGX")
- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -matune=cortex-a76")
- set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections")
- set(OPENBLAS_TARGET "ARM8")
+elseif(BOARD MATCHES "C906")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mtune=thead-c906")
+ set(OPENBLAS_TARGET "RISCV64_GENERIC")
+ set(OPENBLAS_BINARY "64")
+elseif(BOARD MATCHES "x280")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mtune=sifive-x280")
+ set(OPENBLAS_TARGET "x280")
set(OPENBLAS_BINARY "64")
elseif(BOARD MATCHES "KATAMI")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=pentium3")
- set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections")
set(OPENBLAS_TARGET "KATAMI")
set(OPENBLAS_BINARY "32")
elseif(BOARD MATCHES "COPPERMINE")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=pentium3")
- set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections")
set(OPENBLAS_TARGET "COPPERMINE")
set(OPENBLAS_BINARY "32")
elseif(BOARD MATCHES "NORTHWOOD")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=pentium4")
- set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections")
set(OPENBLAS_TARGET "NORTHWOOD")
set(OPENBLAS_BINARY "32")
elseif(BOARD)
diff --git a/doc/index.md b/doc/index.md
index 607a2ae214..34aa7fea2f 100644
--- a/doc/index.md
+++ b/doc/index.md
@@ -100,29 +100,54 @@ Predict continuous values.
### Clustering algorithms
+***NOTE:*** this documentation is still under construction and so some
+algorithms that mlpack implements are not yet listed here. For now, see
+[the mlpack/methods directory](https://github.com/mlpack/mlpack/tree/master/src/mlpack/methods)
+for a full list of algorithms.
+
Group points into clusters.
-
+ * [`MeanShift`](user/methods/mean_shift.md): clustering with the density-based
+ mean shift algorithm
### Geometric algorithms
+***NOTE:*** this documentation is still under construction and so no geometric
+algorithms in mlpack are documented yet. For now, see
+[the mlpack/methods directory](https://github.com/mlpack/mlpack/tree/master/src/mlpack/methods)
+for a full list of algorithms.
+
Computations based on distance metrics.
### Preprocessing utilities
+***NOTE:*** this documentation is still under construction and so no
+preprocessing utilities in mlpack are documented yet. For now, see
+[the mlpack/methods/preprocess directory](https://github.com/mlpack/mlpack/tree/master/src/mlpack/methods)
+for a full list of algorithms.
+
Prepare data for machine learning algorithms.
### Transformations
+***NOTE:*** this documentation is still under construction and so some
+algorithms that mlpack implements are not yet listed here. For now, see
+[the mlpack/methods directory](https://github.com/mlpack/mlpack/tree/master/src/mlpack/methods)
+for a full list of algorithms.
+
Transform data from one space to another.
* [`AMF`](user/methods/amf.md): alternating matrix factorization
* [`LocalCoordinateCoding`](user/methods/local_coordinate_coding.md): local
coordinate coding with dictionary learning
+ * [`LMNN`](user/methods/lmnn.md): large margin nearest neighbor (distance
+ metric learning)
+ * [`NCA`](user/methods/nca.md): neighborhood components analysis (distance
+ metric learning)
* [`NMF`](user/methods/nmf.md): non-negative matrix factorization
* [`PCA`](user/methods/pca.md): principal components analysis
* [`SparseCoding`](user/methods/sparse_coding.md): sparse coding with
diff --git a/doc/quickstart/cpp.md b/doc/quickstart/cpp.md
index 18c3781b79..da1fc7d133 100644
--- a/doc/quickstart/cpp.md
+++ b/doc/quickstart/cpp.md
@@ -24,7 +24,7 @@ sudo apt-get install libmlpack-dev
and on Fedora or Red Hat:
```sh
-sudo dnf install mlpack
+sudo dnf install mlpack-devel
```
You can also use a Docker image from Dockerhub,
diff --git a/doc/sidebar.html b/doc/sidebar.html
index 568496310e..a5ec834955 100644
--- a/doc/sidebar.html
+++ b/doc/sidebar.html
@@ -132,9 +132,20 @@ when the sidebar is built for each page.
-
- Clustering
-
+
+
+
+ Clustering
+
+
+
+
@@ -170,6 +181,16 @@ when the sidebar is built for each page.
LocalCoordinateCoding
+
+
+ LMNN
+
+
+
+
+ NCA
+
+
NMF
diff --git a/doc/user/bindings/cli.md b/doc/user/bindings/cli.md
index bc02653831..adfd721f22 100644
--- a/doc/user/bindings/cli.md
+++ b/doc/user/bindings/cli.md
@@ -1677,9 +1677,9 @@ $ mlpack_linear_svm --input_model_file lsvm_model.bin --test_file test.csv
$ mlpack_lmnn [--batch_size 50] [--center] [--distance_file ]
[--help] [--info ] --input_file [--k 1] [--labels_file
] [--linear_scan] [--max_iterations 100000] [--normalize]
- [--optimizer 'amsgrad'] [--passes 50] [--print_accuracy] [--range 1]
- [--rank 0] [--regularization 0.5] [--seed 0] [--step_size 0.01]
- [--tolerance 1e-07] [--verbose] [--version] [--centered_data_file
+ [--optimizer 'amsgrad'] [--passes 50] [--print_accuracy] [--rank 0]
+ [--regularization 0.5] [--seed 0] [--step_size 0.01] [--tolerance 1e-07]
+ [--update_interval 1] [--verbose] [--version] [--centered_data_file
] [--output_file ] [--transformed_data_file ]
```
@@ -1706,12 +1706,12 @@ An implementation of Large Margin Nearest Neighbors (LMNN), a distance learning
| `--optimizer (-O)` | [`string`](#doc_string) | Optimizer to use; 'amsgrad', 'bbsgd', 'sgd', or 'lbfgs'. | `'amsgrad'` |
| `--passes (-p)` | [`int`](#doc_int) | Maximum number of full passes over dataset for AMSGrad, BB_SGD and SGD. | `50` |
| `--print_accuracy (-P)` | [`flag`](#doc_flag) | Print accuracies on initial and transformed dataset | |
-| `--range (-R)` | [`int`](#doc_int) | Number of iterations after which impostors needs to be recalculated | `1` |
| `--rank (-A)` | [`int`](#doc_int) | Rank of distance matrix to be optimized. | `0` |
| `--regularization (-r)` | [`double`](#doc_double) | Regularization for LMNN objective function | `0.5` |
| `--seed (-s)` | [`int`](#doc_int) | Random seed. If 0, 'std::time(NULL)' is used. | `0` |
| `--step_size (-a)` | [`double`](#doc_double) | Step size for AMSGrad, BB_SGD and SGD (alpha). | `0.01` |
| `--tolerance (-t)` | [`double`](#doc_double) | Maximum tolerance for termination of AMSGrad, BB_SGD, SGD or L-BFGS. | `1e-07` |
+| `--update_interval (-R)` | [`int`](#doc_int) | Number of iterations after which impostors need to be recalculated. | `1` |
| `--verbose (-v)` | [`flag`](#doc_flag) | Display informational messages and the full list of parameters and timers at the end of execution. | |
| `--version (-V)` | [`flag`](#doc_flag) | Display the version of mlpack. Only exists in CLI binding. | |
@@ -1731,7 +1731,7 @@ This program implements Large Margin Nearest Neighbors, a distance learning tech
To work, this algorithm needs labeled data. It can be given as the last row of the input dataset (specified with `--input_file (-i)`), or alternatively as a separate matrix (specified with `--labels_file (-l)`). Additionally, a starting point for optimization (specified with `--distance_file (-d)`can be given, having (r x d) dimensionality. Here r should satisfy 1 <= r <= d, Consequently a Low-Rank matrix will be optimized. Alternatively, Low-Rank distance can be learned by specifying the `--rank (-A)`parameter (A Low-Rank matrix with uniformly distributed values will be used as initial learning point).
-The program also requires number of targets neighbors to work with ( specified with `--k (-k)`), A regularization parameter can also be passed, It acts as a trade of between the pulling and pushing terms (specified with `--regularization (-r)`), In addition, this implementation of LMNN includes a parameter to decide the interval after which impostors must be re-calculated (specified with `--range (-R)`).
+The program also requires number of targets neighbors to work with ( specified with `--k (-k)`), A regularization parameter can also be passed, It acts as a trade of between the pulling and pushing terms (specified with `--regularization (-r)`), In addition, this implementation of LMNN includes a parameter to decide the interval after which impostors must be re-calculated (specified with `--update_interval (-R)`).
Output can either be the learned distance matrix (specified with `--output_file (-o)`), or the transformed dataset (specified with `--transformed_data_file (-D)`), or both. Additionally mean-centered dataset (specified with `--centered_data_file (-c)`) can be accessed given mean-centering (specified with `--center (-C)`) is performed on the dataset. Accuracy on initial dataset and final transformed dataset can be printed by specifying the `--print_accuracy (-P)`parameter.
@@ -1755,10 +1755,10 @@ $ mlpack_lmnn --input_file iris.csv --labels_file iris_labels.csv --k 3
--optimizer bbsgd --output_file output.csv
```
-An another program call making use of range & regularization parameter with dataset having labels as last column can be made as:
+Another program call making use of update interval & regularization parameter with dataset having labels as last column can be made as:
```bash
-$ mlpack_lmnn --input_file letter_recognition.csv --k 5 --range 10
+$ mlpack_lmnn --input_file letter_recognition.csv --k 5 --update_interval 10
--regularization 0.4 --output_file output.csv
```
diff --git a/doc/user/bindings/go.md b/doc/user/bindings/go.md
index c9baae764f..71878e2430 100644
--- a/doc/user/bindings/go.md
+++ b/doc/user/bindings/go.md
@@ -2069,12 +2069,12 @@ param.Normalize = false
param.Optimizer = "amsgrad"
param.Passes = 50
param.PrintAccuracy = false
-param.Range = 1
param.Rank = 0
param.Regularization = 0.5
param.Seed = 0
param.StepSize = 0.01
param.Tolerance = 1e-07
+param.UpdateInterval = 1
param.Verbose = false
centered_data, output, transformed_data := mlpack.Lmnn(input, param)
@@ -2102,12 +2102,12 @@ There are two types of input options: required options, which are passed directl
| `Optimizer` | [`string`](#doc_string) | Optimizer to use; 'amsgrad', 'bbsgd', 'sgd', or 'lbfgs'. | `"amsgrad"` |
| `Passes` | [`int`](#doc_int) | Maximum number of full passes over dataset for AMSGrad, BB_SGD and SGD. | `50` |
| `PrintAccuracy` | [`bool`](#doc_bool) | Print accuracies on initial and transformed dataset | `false` |
-| `Range` | [`int`](#doc_int) | Number of iterations after which impostors needs to be recalculated | `1` |
| `Rank` | [`int`](#doc_int) | Rank of distance matrix to be optimized. | `0` |
| `Regularization` | [`float64`](#doc_float64) | Regularization for LMNN objective function | `0.5` |
| `Seed` | [`int`](#doc_int) | Random seed. If 0, 'std::time(NULL)' is used. | `0` |
| `StepSize` | [`float64`](#doc_float64) | Step size for AMSGrad, BB_SGD and SGD (alpha). | `0.01` |
| `Tolerance` | [`float64`](#doc_float64) | Maximum tolerance for termination of AMSGrad, BB_SGD, SGD or L-BFGS. | `1e-07` |
+| `UpdateInterval` | [`int`](#doc_int) | Number of iterations after which impostors need to be recalculated. | `1` |
| `Verbose` | [`bool`](#doc_bool) | Display informational messages and the full list of parameters and timers at the end of execution. | `false` |
### Output options
@@ -2127,7 +2127,7 @@ This program implements Large Margin Nearest Neighbors, a distance learning tech
To work, this algorithm needs labeled data. It can be given as the last row of the input dataset (specified with `Input`), or alternatively as a separate matrix (specified with `Labels`). Additionally, a starting point for optimization (specified with `Distance`can be given, having (r x d) dimensionality. Here r should satisfy 1 <= r <= d, Consequently a Low-Rank matrix will be optimized. Alternatively, Low-Rank distance can be learned by specifying the `Rank`parameter (A Low-Rank matrix with uniformly distributed values will be used as initial learning point).
-The program also requires number of targets neighbors to work with ( specified with `K`), A regularization parameter can also be passed, It acts as a trade of between the pulling and pushing terms (specified with `Regularization`), In addition, this implementation of LMNN includes a parameter to decide the interval after which impostors must be re-calculated (specified with `Range`).
+The program also requires number of targets neighbors to work with ( specified with `K`), A regularization parameter can also be passed, It acts as a trade of between the pulling and pushing terms (specified with `Regularization`), In addition, this implementation of LMNN includes a parameter to decide the interval after which impostors must be re-calculated (specified with `UpdateInterval`).
Output can either be the learned distance matrix (specified with `Output`), or the transformed dataset (specified with `TransformedData`), or both. Additionally mean-centered dataset (specified with `CenteredData`) can be accessed given mean-centering (specified with `Center`) is performed on the dataset. Accuracy on initial dataset and final transformed dataset can be printed by specifying the `PrintAccuracy`parameter.
@@ -2156,13 +2156,13 @@ param.Optimizer = "bbsgd"
_, output, _ := mlpack.Lmnn(iris, param)
```
-An another program call making use of range & regularization parameter with dataset having labels as last column can be made as:
+Another program call making use of update interval & regularization parameter with dataset having labels as last column can be made as:
```go
// Initialize optional parameters for Lmnn().
param := mlpack.LmnnOptions()
param.K = 5
-param.Range = 10
+param.UpdateInterval = 10
param.Regularization = 0.4
_, output, _ := mlpack.Lmnn(letter_recognition, param)
diff --git a/doc/user/bindings/julia.md b/doc/user/bindings/julia.md
index 5d9e699354..6b81648246 100644
--- a/doc/user/bindings/julia.md
+++ b/doc/user/bindings/julia.md
@@ -1696,9 +1696,9 @@ julia> using mlpack: lmnn
julia> centered_data, output, transformed_data = lmnn(input;
batch_size=50, center=false, distance=zeros(0, 0), k=1, labels=Int[],
linear_scan=false, max_iterations=100000, normalize=false,
- optimizer="amsgrad", passes=50, print_accuracy=false, range=1, rank=0,
+ optimizer="amsgrad", passes=50, print_accuracy=false, rank=0,
regularization=0.5, seed=0, step_size=0.01, tolerance=1e-07,
- verbose=false)
+ update_interval=1, verbose=false)
```
An implementation of Large Margin Nearest Neighbors (LMNN), a distance learning technique. Given a labeled dataset, this learns a transformation of the data that improves k-nearest-neighbor performance; this can be useful as a preprocessing step. [Detailed documentation](#lmnn_detailed-documentation).
@@ -1722,12 +1722,12 @@ An implementation of Large Margin Nearest Neighbors (LMNN), a distance learning
| `optimizer` | [`String`](#doc_String) | Optimizer to use; 'amsgrad', 'bbsgd', 'sgd', or 'lbfgs'. | `"amsgrad"` |
| `passes` | [`Int`](#doc_Int) | Maximum number of full passes over dataset for AMSGrad, BB_SGD and SGD. | `50` |
| `print_accuracy` | [`Bool`](#doc_Bool) | Print accuracies on initial and transformed dataset | `false` |
-| `range` | [`Int`](#doc_Int) | Number of iterations after which impostors needs to be recalculated | `1` |
| `rank` | [`Int`](#doc_Int) | Rank of distance matrix to be optimized. | `0` |
| `regularization` | [`Float64`](#doc_Float64) | Regularization for LMNN objective function | `0.5` |
| `seed` | [`Int`](#doc_Int) | Random seed. If 0, 'std::time(NULL)' is used. | `0` |
| `step_size` | [`Float64`](#doc_Float64) | Step size for AMSGrad, BB_SGD and SGD (alpha). | `0.01` |
| `tolerance` | [`Float64`](#doc_Float64) | Maximum tolerance for termination of AMSGrad, BB_SGD, SGD or L-BFGS. | `1e-07` |
+| `update_interval` | [`Int`](#doc_Int) | Number of iterations after which impostors need to be recalculated. | `1` |
| `verbose` | [`Bool`](#doc_Bool) | Display informational messages and the full list of parameters and timers at the end of execution. | `false` |
### Output options
@@ -1747,7 +1747,7 @@ This program implements Large Margin Nearest Neighbors, a distance learning tech
To work, this algorithm needs labeled data. It can be given as the last row of the input dataset (specified with `input`), or alternatively as a separate matrix (specified with `labels`). Additionally, a starting point for optimization (specified with `distance`can be given, having (r x d) dimensionality. Here r should satisfy 1 <= r <= d, Consequently a Low-Rank matrix will be optimized. Alternatively, Low-Rank distance can be learned by specifying the `rank`parameter (A Low-Rank matrix with uniformly distributed values will be used as initial learning point).
-The program also requires number of targets neighbors to work with ( specified with `k`), A regularization parameter can also be passed, It acts as a trade of between the pulling and pushing terms (specified with `regularization`), In addition, this implementation of LMNN includes a parameter to decide the interval after which impostors must be re-calculated (specified with `range`).
+The program also requires number of targets neighbors to work with ( specified with `k`), A regularization parameter can also be passed, It acts as a trade of between the pulling and pushing terms (specified with `regularization`), In addition, this implementation of LMNN includes a parameter to decide the interval after which impostors must be re-calculated (specified with `update_interval`).
Output can either be the learned distance matrix (specified with `output`), or the transformed dataset (specified with `transformed_data`), or both. Additionally mean-centered dataset (specified with `centered_data`) can be accessed given mean-centering (specified with `center`) is performed on the dataset. Accuracy on initial dataset and final transformed dataset can be printed by specifying the `print_accuracy`parameter.
@@ -1774,13 +1774,13 @@ julia> _, output, _ = lmnn(iris; k=3, labels=iris_labels,
optimizer="bbsgd")
```
-An another program call making use of range & regularization parameter with dataset having labels as last column can be made as:
+Another program call making use of update interval & regularization parameter with dataset having labels as last column can be made as:
```julia
julia> using CSV
julia> letter_recognition = CSV.read("letter_recognition.csv")
-julia> _, output, _ = lmnn(letter_recognition; k=5, range=10,
- regularization=0.4)
+julia> _, output, _ = lmnn(letter_recognition; k=5,
+ regularization=0.4, update_interval=10)
```
### See also
diff --git a/doc/user/bindings/python.md b/doc/user/bindings/python.md
index a0a777fc72..8399b13531 100644
--- a/doc/user/bindings/python.md
+++ b/doc/user/bindings/python.md
@@ -1715,8 +1715,8 @@ Then, to use that model to predict classes for the dataset '`'test'`', storing t
copy_all_inputs=False, distance=np.empty([0, 0]), input_=np.empty([0,
0]), k=1, labels=np.empty([0], dtype=np.uint64), linear_scan=False,
max_iterations=100000, normalize=False, optimizer='amsgrad', passes=50,
- print_accuracy=False, range=1, rank=0, regularization=0.5, seed=0,
- step_size=0.01, tolerance=1e-07, verbose=False)
+ print_accuracy=False, rank=0, regularization=0.5, seed=0,
+ step_size=0.01, tolerance=1e-07, update_interval=1, verbose=False)
>>> centered_data = d['centered_data']
>>> output = d['output']
>>> transformed_data = d['transformed_data']
@@ -1744,12 +1744,12 @@ An implementation of Large Margin Nearest Neighbors (LMNN), a distance learning
| `optimizer` | [`str`](#doc_str) | Optimizer to use; 'amsgrad', 'bbsgd', 'sgd', or 'lbfgs'. | `'amsgrad'` |
| `passes` | [`int`](#doc_int) | Maximum number of full passes over dataset for AMSGrad, BB_SGD and SGD. | `50` |
| `print_accuracy` | [`bool`](#doc_bool) | Print accuracies on initial and transformed dataset | `False` |
-| `range` | [`int`](#doc_int) | Number of iterations after which impostors needs to be recalculated | `1` |
| `rank` | [`int`](#doc_int) | Rank of distance matrix to be optimized. | `0` |
| `regularization` | [`float`](#doc_float) | Regularization for LMNN objective function | `0.5` |
| `seed` | [`int`](#doc_int) | Random seed. If 0, 'std::time(NULL)' is used. | `0` |
| `step_size` | [`float`](#doc_float) | Step size for AMSGrad, BB_SGD and SGD (alpha). | `0.01` |
| `tolerance` | [`float`](#doc_float) | Maximum tolerance for termination of AMSGrad, BB_SGD, SGD or L-BFGS. | `1e-07` |
+| `update_interval` | [`int`](#doc_int) | Number of iterations after which impostors need to be recalculated. | `1` |
| `verbose` | [`bool`](#doc_bool) | Display informational messages and the full list of parameters and timers at the end of execution. | `False` |
### Output options
@@ -1769,7 +1769,7 @@ This program implements Large Margin Nearest Neighbors, a distance learning tech
To work, this algorithm needs labeled data. It can be given as the last row of the input dataset (specified with `input_`), or alternatively as a separate matrix (specified with `labels`). Additionally, a starting point for optimization (specified with `distance`can be given, having (r x d) dimensionality. Here r should satisfy 1 <= r <= d, Consequently a Low-Rank matrix will be optimized. Alternatively, Low-Rank distance can be learned by specifying the `rank`parameter (A Low-Rank matrix with uniformly distributed values will be used as initial learning point).
-The program also requires number of targets neighbors to work with ( specified with `k`), A regularization parameter can also be passed, It acts as a trade of between the pulling and pushing terms (specified with `regularization`), In addition, this implementation of LMNN includes a parameter to decide the interval after which impostors must be re-calculated (specified with `range`).
+The program also requires number of targets neighbors to work with ( specified with `k`), A regularization parameter can also be passed, It acts as a trade of between the pulling and pushing terms (specified with `regularization`), In addition, this implementation of LMNN includes a parameter to decide the interval after which impostors must be re-calculated (specified with `update_interval`).
Output can either be the learned distance matrix (specified with `output`), or the transformed dataset (specified with `transformed_data`), or both. Additionally mean-centered dataset (specified with `centered_data`) can be accessed given mean-centering (specified with `center`) is performed on the dataset. Accuracy on initial dataset and final transformed dataset can be printed by specifying the `print_accuracy`parameter.
@@ -1793,10 +1793,10 @@ Example - Let's say we want to learn distance on iris dataset with number of tar
>>> output = output['output']
```
-An another program call making use of range & regularization parameter with dataset having labels as last column can be made as:
+Another program call making use of update interval & regularization parameter with dataset having labels as last column can be made as:
```python
->>> output = lmnn(input_=letter_recognition, k=5, range=10,
+>>> output = lmnn(input_=letter_recognition, k=5, update_interval=10,
regularization=0.4)
>>> output = output['output']
```
diff --git a/doc/user/bindings/r.md b/doc/user/bindings/r.md
index 2aa0037959..b6a242b521 100644
--- a/doc/user/bindings/r.md
+++ b/doc/user/bindings/r.md
@@ -1689,9 +1689,9 @@ R> library(mlpack)
R> d <- lmnn(batch_size=50, center=FALSE, distance=matrix(numeric(), 0,
0), input=matrix(numeric(), 0, 0), k=1, labels=matrix(integer(), 0, 0),
linear_scan=FALSE, max_iterations=100000, normalize=FALSE,
- optimizer="amsgrad", passes=50, print_accuracy=FALSE, range=1, rank=0,
+ optimizer="amsgrad", passes=50, print_accuracy=FALSE, rank=0,
regularization=0.5, seed=0, step_size=0.01, tolerance=1e-07,
- verbose=getOption("mlpack.verbose", FALSE))
+ update_interval=1, verbose=getOption("mlpack.verbose", FALSE))
R> centered_data <- d$centered_data
R> output <- d$output
R> transformed_data <- d$transformed_data
@@ -1718,12 +1718,12 @@ An implementation of Large Margin Nearest Neighbors (LMNN), a distance learning
| `optimizer` | [`character`](#doc_character) | Optimizer to use; 'amsgrad', 'bbsgd', 'sgd', or 'lbfgs'. | `"amsgrad"` |
| `passes` | [`integer`](#doc_integer) | Maximum number of full passes over dataset for AMSGrad, BB_SGD and SGD. | `50` |
| `print_accuracy` | [`logical`](#doc_logical) | Print accuracies on initial and transformed dataset | `FALSE` |
-| `range` | [`integer`](#doc_integer) | Number of iterations after which impostors needs to be recalculated | `1` |
| `rank` | [`integer`](#doc_integer) | Rank of distance matrix to be optimized. | `0` |
| `regularization` | [`numeric`](#doc_numeric) | Regularization for LMNN objective function | `0.5` |
| `seed` | [`integer`](#doc_integer) | Random seed. If 0, 'std::time(NULL)' is used. | `0` |
| `step_size` | [`numeric`](#doc_numeric) | Step size for AMSGrad, BB_SGD and SGD (alpha). | `0.01` |
| `tolerance` | [`numeric`](#doc_numeric) | Maximum tolerance for termination of AMSGrad, BB_SGD, SGD or L-BFGS. | `1e-07` |
+| `update_interval` | [`integer`](#doc_integer) | Number of iterations after which impostors need to be recalculated. | `1` |
| `verbose` | [`logical`](#doc_logical) | Display informational messages and the full list of parameters and timers at the end of execution. | `getOption("mlpack.verbose", FALSE)` |
### Output options
@@ -1743,7 +1743,7 @@ This program implements Large Margin Nearest Neighbors, a distance learning tech
To work, this algorithm needs labeled data. It can be given as the last row of the input dataset (specified with `input`), or alternatively as a separate matrix (specified with `labels`). Additionally, a starting point for optimization (specified with `distance`can be given, having (r x d) dimensionality. Here r should satisfy 1 <= r <= d, Consequently a Low-Rank matrix will be optimized. Alternatively, Low-Rank distance can be learned by specifying the `rank`parameter (A Low-Rank matrix with uniformly distributed values will be used as initial learning point).
-The program also requires number of targets neighbors to work with ( specified with `k`), A regularization parameter can also be passed, It acts as a trade of between the pulling and pushing terms (specified with `regularization`), In addition, this implementation of LMNN includes a parameter to decide the interval after which impostors must be re-calculated (specified with `range`).
+The program also requires number of targets neighbors to work with ( specified with `k`), A regularization parameter can also be passed, It acts as a trade of between the pulling and pushing terms (specified with `regularization`), In addition, this implementation of LMNN includes a parameter to decide the interval after which impostors must be re-calculated (specified with `update_interval`).
Output can either be the learned distance matrix (specified with `output`), or the transformed dataset (specified with `transformed_data`), or both. Additionally mean-centered dataset (specified with `centered_data`) can be accessed given mean-centering (specified with `center`) is performed on the dataset. Accuracy on initial dataset and final transformed dataset can be printed by specifying the `print_accuracy`parameter.
@@ -1767,10 +1767,10 @@ R> output <- lmnn(input=iris, labels=iris_labels, k=3, optimizer="bbsgd")
R> output <- output$output
```
-An another program call making use of range & regularization parameter with dataset having labels as last column can be made as:
+Another program call making use of update interval & regularization parameter with dataset having labels as last column can be made as:
```R
-R> output <- lmnn(input=letter_recognition, k=5, range=10,
+R> output <- lmnn(input=letter_recognition, k=5, update_interval=10,
regularization=0.4)
R> output <- output$output
```
diff --git a/doc/user/core.md b/doc/user/core.md
index 0caaea7459..0062191185 100644
--- a/doc/user/core.md
+++ b/doc/user/core.md
@@ -862,9 +862,9 @@ including:
* [`NeighborSearch`](/src/mlpack/methods/neighbor_search/neighbor_search.hpp)
* [`RangeSearch`](/src/mlpack/methods/range_search/range_search.hpp)
- * [`LMNN`](/src/mlpack/methods/lmnn/lmnn.hpp)
+ * [`LMNN`](methods/lmnn.md)
* [`EMST`](/src/mlpack/methods/emst/emst.hpp)
- * [`NCA`](/src/mlpack/methods/nca/nca.hpp)
+ * [`NCA`](methods/nca.md)
* [`RANN`](/src/mlpack/methods/rann/rann.hpp)
* [`KMeans`](/src/mlpack/methods/kmeans/kmeans.hpp)
@@ -1281,6 +1281,11 @@ std::cout << "Squared Mahalanobis distance on 32-bit floating point data:"
<< std::endl;
std::cout << " - Points 3 and 5: " << d1 << "." << std::endl;
std::cout << " - Points 11 and 31: " << d2 << "." << std::endl;
+
+// Note that an equivalent transformation matrix can be recovered from Q with
+// an upper Cholesky decomposition (Q -> R.t() * R).
+arma::mat recoveredW = arma::chol(md.Q(), "lower");
+// A transformed dataset can be created with `(recoveredW * dataset)`.
```
---
@@ -1641,7 +1646,8 @@ where `bw` is the bandwidth parameter of the kernel.
* `g.Gradient(distance)`
- Compute the (one-dimensional) gradient of the kernel function with respect
- to the distance between two points, evaluated at `distance`.
+ to the distance between two points, evaluated at `distance`. This is used
+ by [`MeanShift`](methods/mean_shift.md).
* `g.Normalizer(dimensionality)`
- Return the
@@ -1876,7 +1882,8 @@ distance between `x1` and `x2`) is greater than or equal to `bw`.
* `e.Gradient(distance)`
- Compute the (one-dimensional) gradient of the kernel function with respect
- to the distance between two points, evaluated at `distance`.
+ to the distance between two points, evaluated at `distance`. This is used
+ by [`MeanShift`](methods/mean_shift.md).
* `e.Normalizer(dimensionality)`
- Return the
@@ -1933,7 +1940,7 @@ std::cout << "Kernel values between two floating-point vectors: " << k5
### `HyperbolicTangentKernel`
The `HyperbolicTangentKernel` implements the
-[hyperbolic tangent kernel](https://en.wikipedia.org/wiki/Support_vector_machine#Nonlinear_Kernels),
+[hyperbolic tangent kernel](https://en.wikipedia.org/wiki/Support_vector_machine#Nonlinear_kernels),
which is defined by the following equation:
`f(x1, x2) = tanh(s * (x1^T x2) + t)`
where `s` is the scale parameter and `t` is the offset parameter.
@@ -2046,7 +2053,8 @@ where `bw` is the bandwidth parameter.
* `l.Gradient(distance)`
- Compute the (one-dimensional) gradient of the kernel function with respect
- to the distance between two points, evaluated at `distance`.
+ to the distance between two points, evaluated at `distance`. This is used
+ by [`MeanShift`](methods/mean_shift.md).
---
@@ -2365,7 +2373,8 @@ as the uniform kernel, or rectangular window kernel. The value of the
* `s.Gradient(distance)`
- Compute the (one-dimensional) gradient of the kernel function with respect
- to the distance between two points, evaluated at `distance`.
+ to the distance between two points, evaluated at `distance`. This is used
+ by [`MeanShift`](methods/mean_shift.md).
* `s.Normalizer(dimensionality)`
- Return the
@@ -2459,7 +2468,8 @@ where `bw` is the bandwidth of the kernel.
* `t.Gradient(distance)`
- Compute the (one-dimensional) gradient of the kernel function with respect
- to the distance between two points, evaluated at `distance`.
+ to the distance between two points, evaluated at `distance`. This is used
+ by [`MeanShift`](methods/mean_shift.md).
---
diff --git a/doc/user/methods/decision_tree.md b/doc/user/methods/decision_tree.md
index f4b4253b9e..cec361d871 100644
--- a/doc/user/methods/decision_tree.md
+++ b/doc/user/methods/decision_tree.md
@@ -458,11 +458,9 @@ class CustomNumericSplit
categorical feature.
* The `AllCategoricalSplit` _(default)_ and `BestBinaryCategoricalSplit` are~
available for drop-in usage.
-
* `AllCategoricalSplit`, the default ID3 split
algorithm, splits all categories into their own node. This variant is simple,
and has complexity `O(n)`, where `n` is the number of samples.
-
* `BestBinaryCategoricalSplit` is the preferred algorithm of
[the CART system](https://www.taylorfrancis.com/books/mono/10.1201/9781315139470/classification-regression-trees-leo-breiman-jerome-friedman-olshen-charles-stone).
It will find the the best (entropy-minimizing) binary partition of the
@@ -471,6 +469,9 @@ class CustomNumericSplit
more than two _classes_.~
- ***Note***: `BestBinaryCategoricalSplit` should not be chosen when there
are multiple classes and many categories.
+ * A custom class must take a [`FitnessFunction`](#fitnessfunction) as a
+ template parameter, implement three functions, and have an internal
+ structure `AuxiliarySplitInfo` that is used at classification time:
```c++
template
diff --git a/doc/user/methods/decision_tree_regressor.md b/doc/user/methods/decision_tree_regressor.md
index f484b30ce1..b465a5f4f6 100644
--- a/doc/user/methods/decision_tree_regressor.md
+++ b/doc/user/methods/decision_tree_regressor.md
@@ -462,11 +462,9 @@ class CustomNumericSplit
categorical feature.
* The `AllCategoricalSplit` _(default)_ and `BestBinaryCategoricalSplit` are~
available for drop-in usage.
-
* `AllCategoricalSplit`, the default ID3 split
algorithm, splits all categories into their own node. This variant is simple,
and has complexity `O(n)`, where `n` is the number of samples.
-
* `BestBinaryCategoricalSplit` is the preferred algorithm of
[the CART system](https://www.taylorfrancis.com/books/mono/10.1201/9781315139470/classification-regression-trees-leo-breiman-jerome-friedman-olshen-charles-stone).
It will find the the best (entropy-minimizing) binary partition of the
@@ -476,10 +474,9 @@ class CustomNumericSplit
- ***Note***: `BestBinaryCategoricalSplit` should not be chosen when there
are multiple classes and many categories.
- ***Note***: for regression tasks,
- [W. Fisher's proof of correctness](http://www.csiss.org/SPACE/workshops/2004/SAC/files/fisher.pdf)
+ [W. Fisher's proof of correctness](https://www.mlpack.org/papers/fisher.pdf)
only applies to when `FitnessFunction` is `MSEGain`; therefore,
`BestBinaryCategoricalSplit` requires the use of `MSEGain`.
-
* A custom class must take a [`FitnessFunction`](#fitnessfunction) as a
template parameter, implement three functions, and have an internal
structure `AuxiliarySplitInfo` that is used at classification time:
diff --git a/doc/user/methods/lmnn.md b/doc/user/methods/lmnn.md
new file mode 100644
index 0000000000..322a5f683e
--- /dev/null
+++ b/doc/user/methods/lmnn.md
@@ -0,0 +1,454 @@
+## LMNN
+
+The `LMNN` class implements large margin nearest neighbor, which can be used
+as both a linear dimensionality reduction technique and a distance learning
+technique (also called metric learning). LMNN finds a linear transformation of
+the dataset that improves `k`-nearest-neighbor classification performance.
+
+#### Simple usage example:
+
+```c++
+// Learn a distance metric that improves kNN classification performance.
+
+// All data and labels are uniform random; 10 dimensional data, 5 classes.
+// Replace with a data::Load() call or similar for a real application.
+arma::mat dataset(10, 1000, arma::fill::randu); // 1000 points.
+arma::Row labels =
+ arma::randi>(1000, arma::distr_param(0, 4));
+
+mlpack::LMNN lmnn(3 /* neighbors to consider */); // Step 1: create object.
+arma::mat distance;
+lmnn.LearnDistance(dataset, labels, distance); // Step 2: learn distance.
+
+// `distance` can now be used as a transformation matrix for the data.
+arma::mat transformedData = distance * dataset;
+// Or, you can create a MahalanobisDistance to evaluate points in the
+// transformed dataset space.
+arma::mat q = distance.t() * distance;
+mlpack::MahalanobisDistance d(std::move(q));
+
+std::cout << "Distance between points 0 and 1:" << std::endl;
+std::cout << " - Before LMNN: "
+ << mlpack::EuclideanDistance::Evaluate(dataset.col(0), dataset.col(1))
+ << "." << std::endl;
+std::cout << " - After LMNN: "
+ << d.Evaluate(dataset.col(0), dataset.col(1)) << "." << std::endl;
+```
+More examples...
+
+#### Quick links:
+
+ * [Constructors](#constructors): create `LMNN` objects.
+ * [`LearnDistance()`](#learning-distances): learn distance metrics.
+ * [Other functionality](#other-functionality) for loading and saving.
+ * [Examples](#simple-examples) of simple usage and integration with other
+ techniques.
+
+#### See also:
+
+
+
+ * [mlpack distance metrics](../core.md#distances)
+ * [`NCA`](nca.md)
+ * [Metric learning on Wikipedia](https://en.wikipedia.org/wiki/Similarity_learning#Metric_learning)
+ * [Large margin nearest neighbor on Wikipedia](https://en.wikipedia.org/wiki/Large_margin_nearest_neighbor)
+ * [Distance metric learning for Large Margin Nearest Neighbor Classification (pdf)](https://proceedings.neurips.cc/paper_files/paper/2005/file/a7f592cef8b130a6967a90617db5681b-Paper.pdf)
+
+### Constructors
+
+ * `lmnn = LMNN(k, regularization=0.5, updateInterval=1)`
+ - Create an `LMNN` object considering the specified number `k` of neighbors.
+ - Optionally, specify the regularization to be applied to the LMNN cost
+ function (a `double`), and the number of iterations between recomputation
+ of neighbors (`updateInterval`, a `size_t`).
+
+---
+
+ * `lmnn = LMNN(k, regularization=0.5, updateInterval=1)`
+ * `lmnn = LMNN(k, regularization, updateInterval, distance)`
+ - Create an `LMNN` object using a custom
+ [`DistanceType`](../core.md#distances).
+ - `k` specifies the number of neighbors to consider.
+ - `regularization` specifies the regularization penalty to be applied to the
+ LMNN cost function (a `double`).
+ - `updateInterval` specifies the number of iterations between recomputation
+ of neighbors (a `size_t`).
+ - An instantiated `DistanceType` can optionally be passed with the `distance`
+ parameter.
+ - Using a custom `DistanceType` means that `LearnDistance()` will learn a
+ linear transformation for the data *in the metric space of the custom
+ `DistanceType`*.
+ * This means any learned distance may not necessarily improve
+ classification performance with the
+ [Euclidean distance](../core.md#lmetric).
+ * Instead, classification performance will be improved when the learned
+ distance is used with the given `DistanceType` only.
+ - Any mlpack `DistanceType` can be used as a drop-in replacement, or a
+ [custom `DistanceType`](../../developer/distances.md).
+ * A list of mlpack's provided distance metrics can be found
+ [here](../core.md#distances).
+ - ***Note: be sure that you understand the implications of a custom
+ `DistanceType` before using this version.***
+
+---
+
+***Notes***:
+
+ - A larger `k` will cause `LearnDistance()` to take longer to compute, but will
+ give more accurate results. It is generally suggested to keep `k` in roughly
+ the `3` to `5` range, depending on the dataset. Using `k = 1` can provide
+ fast convergence, but the learned distance metric may be of lower quality.
+
+ - `regularization` controls the balance between encouraging small distances for
+ points of the same class and penalizing small distances for points of
+ different classes. When `regularization` is increased, small distances for
+ points of different classes are further penalized.
+
+ - Setting `updateInterval` greater than `1` will allow the LMNN algorithm to
+ take multiple steps without the expensive recomputation of neighbors, but
+ this means that subsequent optimization steps may not be using the true
+ nearest neighbors.
+ * If using an SGD-like algorithm (i.e. an optimizer for a
+ [differentiable separable function](https://www.ensmallen.org/docs.html#differentiable-separable-functions)),
+ this can often be set to a relatively high value (100 is not unreasonable).
+ * If using an optimizer like L-BFGS (i.e. a full-batch optimizer for
+ [differentiable functions](https://www.ensmallen.org/docs.html#differentiable-functions)),
+ this should be kept relatively low (going above 10 is not advised).
+ * It is worth cross-validating different values of the parameter to see what
+ works for your dataset.
+
+---
+
+### Learning Distances
+
+Once an `LMNN` object has been created, the `LearnDistance()` method can be used
+to learn a distance.
+
+ * `lmnn.LearnDistance(data, labels, distance, [callbacks...])`
+ * `lmnn.LearnDistance(data, labels, distance, optimizer, [callbacks...])`
+ - Learn a distance metric on the given `data` and `labels`, filling
+ `distance` with a transformation matrix that can be used to map the data
+ into the space of the learned distance.
+ - Optionally, pass an instantiated
+ [ensmallen optimizer](https://www.ensmallen.org) and/or
+ [ensmallen callbacks](https://www.ensmallen.org/docs.html#callback-documentation)
+ to be used for the learning process.
+ - If no optimizer is passed,
+ [`ens::AMSGrad`](https://www.ensmallen.org/docs.html#amsgrad) is used.
+ - If `distance` already has size `r` x `data.n_rows` for some `r` less than
+ or equal to `data.n_rows`, it will be used as the starting point for
+ optimization. Otherwise, the identity matrix with size `data.n_rows` x
+ `data.n_rows` will be used.
+ - When optimization is complete, `distance` will have size `r` x
+ `data.n_rows`, where `r` is less than or equal to `data.n_rows`.
+ * *Note*: If `r < data.n_rows`, then LMNN has learned a distance metric
+ that also reduces the dimensionality of the data. See the
+ [last example](#simple-examples).
+
+To use `distance`, either:
+
+ * Compute a new transformed dataset as `distance * data`, or
+ * Use an instantiated [`MahalanobisDistance`](../core.md#mahalanobisdistance)
+ with `distance.t() * distance` as the `Q` matrix.
+
+See the [examples section](#simple-examples) for more details.
+
+#### `LearnDistance()` Parameters:
+
+| **name** | **type** | **description** |
+|----------|----------|-----------------|
+| `data` | [`arma::mat`](../matrices.md) | [Column-major](../matrices.md#representing-data-in-mlpack) training matrix. |
+| `labels` | [`arma::Row`](../matrices.md) | Training labels, [between `0` and `numClasses - 1`](../load_save.md#normalizing-labels) (inclusive). Should have length `data.n_cols`. |
+| `distance` | [`arma::mat`](../matrices.md) | Output matrix to store transformation matrix representing learned distance. |
+| `optimizer` | [any ensmallen optimizer](https://www.ensmallen.org) | Instantiated ensmallen optimizer for [differentiable functions](https://www.ensmallen.org/docs.html#differentiable-functions) or [differentiable separable functions](https://www.ensmallen.org/docs.html#differentiable-separable-functions). | `ens::AMSGrad()` |
+| `callbacks...` | [any set of ensmallen callbacks](https://www.ensmallen.org/docs.html#callback-documentation) | Optional callbacks for the ensmallen optimizer, such as e.g. `ens::ProgressBar()`, `ens::Report()`, or others. | _(N/A)_ |
+
+***Note***: any matrix type can be used for `data` and `distance`, so long as
+that type implements the Armadillo API. So, e.g., `arma::fmat` can be used.
+
+### Other Functionality
+
+ * An `LMNN` object can be serialized with
+ [`data::Save()` and `data::Load()`](../load_save.md#mlpack-objects).
+ Note that this is only meaningful if a custom `DistanceType` is being used,
+ and that custom `DistanceType` has state to be saved.
+
+ * `lmnn.K()` returns the number of neighbors used by LMNN, and `lmnn.K() = k`
+ will set the number of neighbors to use to `k`.
+
+ * `lmnn.Regularization()` returns the current regularization value of the LMNN
+ object (as a `double`), and `lmnn.Regularization() = r` can be used to set
+ the regularization value to `r`.
+
+ * `lmnn.UpdateInterval()` returns the current number of iterations between
+ neighbor recomputation (as a `size_t`), and `lmnn.UpdateInterval() = i` sets
+ the number of iterations between neighbor recomputation to `i`.
+
+ * `lmnn.Distance()` will return the `DistanceType` being used for learning.
+ Unless a custom `DistanceType` was specified in the constructor,
+ this simply returns a [`SquaredEuclideanDistance`](../core.md#lmetric)
+ object.
+
+### Simple Examples
+
+Learn a distance metric to improve classification performance on the iris
+dataset, and show improved performance when using
+[`NaiveBayesClassifier`](naive_bayes_classifier.md).
+
+```c++
+// See https://datasets.mlpack.org/satellite.test.csv.
+// (We are using the test set here just because it is a little smaller and
+// we want this example to run quickly.)
+arma::mat dataset;
+mlpack::data::Load("satellite.test.csv", dataset, true);
+// See https://datasets.mlpack.org/satellite.test.labels.csv.
+arma::Row labels;
+mlpack::data::Load("satellite.test.labels.csv", labels, true);
+
+// Create an LMNN object using 5 nearest neighbors and learn a distance.
+arma::mat distance;
+mlpack::LMNN lmnn(5);
+lmnn.LearnDistance(dataset, labels, distance);
+
+// The distance matrix has size equal to the dimensionality of the data.
+std::cout << "Learned distance size: " << distance.n_rows << " x "
+ << distance.n_cols << "." << std::endl;
+
+// Learn a NaiveBayesClassifier model on the data and print the performance.
+mlpack::NaiveBayesClassifier nbc1(dataset, labels, 2);
+arma::Row predictions;
+nbc1.Classify(dataset, predictions);
+std::cout << "Naive Bayes Classifier without LMNN: "
+ << arma::accu(labels == predictions) << " of " << labels.n_elem
+ << " correct." << std::endl;
+
+// Now transform the data and learn another NaiveBayesClassifier.
+arma::mat transformedDataset = distance * dataset;
+mlpack::NaiveBayesClassifier nbc2(transformedDataset, labels, 2);
+nbc2.Classify(transformedDataset, predictions);
+std::cout << "Naive Bayes Classifier with LMNN: "
+ << arma::accu(labels == predictions) << " of " << labels.n_elem
+ << " correct." << std::endl;
+```
+
+---
+
+Learn a distance metric on the vehicle dataset, using 32-bit floating point to
+represent the data and metric.
+
+```c++
+// See https://datasets.mlpack.org/vehicle.csv.
+arma::fmat dataset;
+mlpack::data::Load("vehicle.csv", dataset, true);
+
+// The labels are contained as the last row of the dataset.
+arma::Row labels =
+ arma::conv_to>::from(dataset.row(dataset.n_rows - 1));
+dataset.shed_row(dataset.n_rows - 1);
+
+// Create an LMNN object with k=1 and learn distance on float32 data.
+// Set updateInterval to a large value (100) because we are using the default
+// AMSGrad optimizer (which will take very many small steps).
+arma::fmat distance;
+mlpack::LMNN lmnn(1, 0.5, 100);
+
+lmnn.LearnDistance(dataset, labels, distance, ens::ProgressBar());
+
+// We want to compute six quantities:
+//
+// - Average distance to points of the same class before LMNN.
+// - Average distance to points of the same class after LMNN, using
+// MahalanobisDistance.
+// - Average distance to points of the same class after LMNN, using the
+// transformed dataset.
+//
+// - The same three quantities above, but for points of the other class.
+//
+// LMNN should reduce the average distance to points in the same class, while
+// increasing the average distance to points in other classes.
+float distSums[6] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
+size_t sameCount = 0;
+arma::fmat q = distance.t() * distance;
+mlpack::MahalanobisDistance md(std::move(q));
+arma::fmat transformedDataset = distance * dataset;
+for (size_t i = 1; i < dataset.n_cols; ++i)
+{
+ const double d1 = mlpack::EuclideanDistance::Evaluate(
+ dataset.col(0), dataset.col(i));
+ const double d2 = md.Evaluate(dataset.col(0), dataset.col(i));
+ const double d3 = mlpack::EuclideanDistance::Evaluate(
+ transformedDataset.col(0), transformedDataset.col(i));
+
+ // Determine whether the point has the same label as point 0.
+ if (labels[i] == labels[0])
+ {
+ distSums[0] += d1;
+ distSums[1] += d2;
+ distSums[2] += d3;
+ ++sameCount;
+ }
+ else
+ {
+ distSums[3] += d1;
+ distSums[4] += d2;
+ distSums[5] += d3;
+ }
+}
+
+// Turn the results into average distances across the class.
+distSums[0] /= sameCount;
+distSums[1] /= sameCount;
+distSums[2] /= sameCount;
+distSums[3] /= (dataset.n_cols - sameCount);
+distSums[4] /= (dataset.n_cols - sameCount);
+distSums[5] /= (dataset.n_cols - sameCount);
+
+// Print the results.
+std::cout << "Average distance between point 0 and other points of the same "
+ << "class:" << std::endl;
+std::cout << " - Before LMNN: " << distSums[0] << "."
+ << std::endl;
+std::cout << " - After LMNN (with MahalanobisDistance): " << distSums[1] << "."
+ << std::endl;
+std::cout << " - After LMNN (with transformed dataset): " << distSums[2] << "."
+ << std::endl;
+std::cout << std::endl;
+
+std::cout << "Average distance between point 0 and points of other classes: "
+ << std::endl;
+std::cout << " - Before LMNN: " << distSums[3] << "."
+ << std::endl;
+std::cout << " - After LMNN (with MahalanobisDistance): " << distSums[4] << "."
+ << std::endl;
+std::cout << " - After LMNN (with transformed dataset): " << distSums[5] << "."
+ << std::endl;
+std::cout << std::endl;
+
+std::cout << "Ratio of other-class to same-class distances:" << std::endl;
+std::cout << "(We expect this to go up.)" << std::endl;
+std::cout << " - Before LMNN: " << (distSums[3] / distSums[0]) << "."
+ << std::endl;
+std::cout << " - After LMNN: " << (distSums[5] / distSums[2]) << "."
+ << std::endl;
+```
+
+---
+
+Learn a distance metric on the iris dataset, using the L-BFGS optimizer with
+callbacks.
+
+```c++
+// See https://datasets.mlpack.org/iris.csv.
+arma::mat dataset;
+mlpack::data::Load("iris.csv", dataset, true);
+// See https://datasets.mlpack.org/iris.labels.csv.
+arma::Row labels;
+mlpack::data::Load("iris.labels.csv", labels, true);
+
+// Learn a distance with ensmallen's L-BFGS optimizer.
+ens::L_BFGS lbfgs;
+lbfgs.NumBasis() = 5;
+lbfgs.MaxIterations() = 1000;
+
+// Use 5 neighbors for LMNN, and leave updateInterval at the default of 1,
+// because we are using L-BFGS (a full-back optimizer).
+mlpack::LMNN lmnn(5);
+
+// Use a callback that prints a final optimization report.
+arma::mat distance;
+lmnn.LearnDistance(dataset, labels, distance, lbfgs, ens::Report());
+```
+
+---
+
+Learn a distance metric on the vehicle dataset, but instead of using the
+Euclidean distance as the underlying metric, use the Manhattan distance. This
+means that LMNN is optimizing k-NN performance under the Manhattan distance, not
+under the Euclidean distance.
+
+```c++
+// See https://datasets.mlpack.org/vehicle.csv.
+arma::mat dataset;
+mlpack::data::Load("vehicle.csv", dataset, true);
+
+// The labels are contained as the last row of the dataset.
+arma::Row labels =
+ arma::conv_to>::from(dataset.row(dataset.n_rows - 1));
+dataset.shed_row(dataset.n_rows - 1);
+
+// Create the LMNN object and optimize. Use k=3 and Nesterov momentum SGD,
+// printing a progress bar during optimization. Because Nesterov momentum SGD
+// is an ensmallen optimizer for differentiable separable functions, we increase
+// updateInterval to reduce the number of neighbor recomputations. We also set
+// the regularization parameter to 1.0 to increase the penalty for nearby
+// neighbors of a different class.
+mlpack::LMNN lmnn(3, 1.0, 100);
+arma::mat distance;
+ens::NesterovMomentumSGD opt(0.000001 /* step size */,
+ 32 /* batch size */,
+ 20 * dataset.n_cols /* 20 epochs */);
+lmnn.LearnDistance(dataset, labels, distance, opt, ens::ProgressBar());
+
+// Now inspect distances between points with the Euclidean distance and with the
+// inner product distance.
+arma::mat transformedDataset = distance * dataset;
+
+// Points 0 and 1 have the same label (0). See their original distance---with
+// both the Euclidean and Manhattan distances---and their transformed distances.
+// We expect these points to get closer together, in the Manhattan distance.
+const double d1 = mlpack::ManhattanDistance::Evaluate(
+ dataset.col(0), dataset.col(1));
+const double d2 = mlpack::ManhattanDistance::Evaluate(
+ transformedDataset.col(0), transformedDataset.col(1));
+
+std::cout << "Distance between points 0 and 1 (same class):" << std::endl;
+std::cout << " - Manhattan distance:" << std::endl;
+std::cout << " * Before LMNN: " << d1 << std::endl;
+std::cout << " * After LMNN: " << d2 << std::endl;
+std::cout << std::endl;
+
+// Point 3 has a different label. We therefore expect this point to get further
+// from point 0 with the Manhattan distance, but not necessarily with the
+// Euclidean distance.
+const double d3 = mlpack::ManhattanDistance::Evaluate(
+ dataset.col(0), dataset.col(3));
+const double d4 = mlpack::ManhattanDistance::Evaluate(
+ transformedDataset.col(0), transformedDataset.col(3));
+
+std::cout << "Distance between points 0 and 3 (different class):" << std::endl;
+std::cout << " - Manhattan distance:" << std::endl;
+std::cout << " * Before LMNN: " << d3 << std::endl;
+std::cout << " * After LMNN: " << d4 << std::endl;
+
+// Note that point 3 has been moved further away from point 0 than point 1.
+```
+
+---
+
+Learn a distance metric while also performing dimensionality reduction, reducing
+the dimensionality of the satellite dataset by 3 dimensions.
+
+```c++
+// See https://datasets.mlpack.org/satellite.train.csv.
+arma::mat dataset;
+mlpack::data::Load("satellite.train.csv", dataset, true);
+// See https://datasets.mlpack.org/satellite.labels.csv.
+arma::Row labels;
+mlpack::data::Load("satellite.train.labels.csv", labels, true);
+
+// Use a random initialization for the distance transformation, with the
+// specified output dimensionality.
+arma::mat distance(dataset.n_rows - 3, dataset.n_rows, arma::fill::randu);
+mlpack::LMNN lmnn(3);
+ens::L_BFGS opt;
+opt.MaxIterations() = 10; // You may want more in a real application.
+lmnn.LearnDistance(dataset, labels, distance, opt, ens::Report());
+
+// Now transform the dataset.
+arma::mat transformedData = distance * dataset;
+
+std::cout << "Original data has size " << dataset.n_rows << " x "
+ << dataset.n_cols << "." << std::endl;
+std::cout << "Transformed data has size " << transformedData.n_rows << " x "
+ << transformedData.n_cols << "." << std::endl;
+```
diff --git a/doc/user/methods/mean_shift.md b/doc/user/methods/mean_shift.md
new file mode 100644
index 0000000000..a885e45e07
--- /dev/null
+++ b/doc/user/methods/mean_shift.md
@@ -0,0 +1,341 @@
+## `MeanShift`
+
+The `MeanShift` class implements mean shift, a clustering technique. Mean shift
+models the density of the data using a kernel function (also called Parzen
+window), producing a number of clusters that represent the data density. Mean
+shift does not require the user to guess the number of clusters, and does not
+make any assumptions on the shape of the data.
+
+mlpack's `MeanShift` class allows control of the kernel function used via
+template parameters.
+
+#### Simple usage example:
+
+```c++
+// Use mean shift to cluster random data and print the number of points that
+// fall into each cluster.
+
+// Create random dataset with two separated 10-dimensional Gaussians.
+arma::mat dataset = arma::join_rows(
+ arma::randn(10, 1000) + 3.0, // 1000 points from N(-3, 1).
+ arma::randn(10, 1000) - 3.0); // 1000 points from N( 3, 1).
+
+mlpack::MeanShift ms; // Step 1: create object.
+arma::Row assignments;
+arma::mat centroids;
+ms.Cluster(dataset, assignments, centroids); // Step 2: perform clustering.
+
+// Print the number of clusters.
+std::cout << "Found " << centroids.n_cols << " centroids." << std::endl;
+
+// Print the number of points in each cluster.
+for (size_t c = 0; c < centroids.n_cols; ++c)
+{
+ std::cout << " * Cluster " << c << " has " << arma::accu(assignments == c)
+ << " points." << std::endl;
+}
+```
+More examples...
+
+#### Quick links:
+
+ * [Constructors](#constructors): create `MeanShift` objects.
+ * [`Cluster()`](#clustering): perform clustering.
+ * [Other functionality](#other-functionality) for loading, saving, inspecting,
+ and estimating the radius to use.
+ * [Examples](#simple-examples) of simple usage and links to detailed example
+ projects.
+ * [Template parameters](#advanced-functionality-template-parameters) for custom
+ behavior.
+
+#### See also:
+
+ * [mlpack clustering algorithms](../../index.md#clustering-algorithms)
+ * [mlpack kernels](../core.md#kernels)
+ * [Mean shift on Wikipedia](https://en.wikipedia.org/wiki/Mean_shift)
+ * [Mean Shift, Mode Seeking, and Clustering (pdf)](http://users.isr.ist.utl.pt/~alex/Resources/meanshift.pdf)
+
+### Constructors
+
+ * `ms = MeanShift(radius=0, maxIterations=1000)`
+ - Create a `MeanShift` object that will use the
+ default [`GaussianKernel`](../core.md#gaussiankernel) to weight points for
+ cluster centroid recalculations.
+
+---
+
+ * `ms = MeanShift(radius=0, maxIterations=1000)`
+ - Create a `MeanShift` object that will not weight points differently when
+ recalculating cluster centroids.
+ - Centroid recalculation will use all points within a distance of `radius`
+ from the current cluster centroid, uniformly weighted.
+
+---
+
+ * `ms = MeanShift(radius, maxIterations, kernel)`
+ - Create a `MeanShift` object that will use the given `kernel` object (a
+ `GaussianKernel`) for weighting points during cluster centroid
+ recalculations.
+
+---
+
+ * `ms = MeanShift(radius, maxIterations, kernel=KernelType())`
+ - Create a `MeanShift` object that will use the given
+ [`KernelType`](../core.md#kernels) for weighting points during cluster
+ centroid recalculations.
+ - [mlpack kernels](../core.md#kernels) or custom kernel classes implementing
+ a [`Gradient()` function](#advanced-functionality-template-parameters) can
+ be used for the `KernelType` template parameter.
+ - If `kernel` is not specified, a default-constructed `KernelType` will be
+ used.
+ - A list of usable `KernelType`s supplied with mlpack can be found in the
+ [advanced functionality section](#advanced-functionality-template-parameters).
+
+---
+
+#### Constructor Parameters:
+
+| **name** | **type** | **description** | **default** |
+|----------|----------|-----------------|-------------|
+| `radius` | `double` | Radius around each centroid for weighting during centroid recomputation. Larger means higher weights for faraway points. Values less than or equal to 0 mean that the radius will be estimated from data. | `0.0` |
+| `maxIterations` | `size_t` | Maximum number of iterations of the mean shift algorithm to run. | `1000` |
+| `kernel` | [`KernelType`](#advanced-functionality-template-parameters) | Instantiated kernel object to use for density calculations. | [`GaussianKernel()`](../core.md#gaussiankernel) |
+
+***Notes:***
+
+ - A larger `radius` value will generally result in fewer clusters (e.g. a
+ coarser clustering); smaller `radius` values will generally result in more
+ clusters.
+
+ - When `MeanShift` is used, `radius` is the hard distance threshold for
+ points to be considered in the recomputation of a centroid.
+
+### Clustering
+
+ * `ms.Cluster(data, centroids, forceConvergence=true, useSeeds=true)`
+ - Cluster the given data, storing the resulting cluster centroids in
+ `centroids`.
+ - `centroids` will be set to size `data.n_rows` x `numClusters`, where
+ `numClusters` is the number of clusters found by the mean shift algorithm.
+ - The `i`th cluster centroid can be obtained with `clusters.col(i)`.
+
+---
+
+ * `ms.Cluster(data, assignments, centroids, forceConvergence=true, useSeeds=true)`
+ - Cluster the given data, storing the resulting cluster centroids in
+ `centroids` and cluster assignments for each data point in `assignments`.
+ - `centroids` will be set to size `data.n_rows` x `numClusters`, where
+ `numClusters` is the number of clusters found by the mean shift algorithm.
+ - `assignments` will be set to length `data.n_cols`; the assignment of the
+ `i`th point can be obtained with `assignments[i]`.
+ - The cluster centroid of the `i`th point's cluster can be obtained with
+ `centroids.col(assignments[i])`.
+
+---
+
+#### Clustering Parameters:
+
+| **name** | **type** | **description** | **default** |
+|----------|----------|-----------------|-------------|
+| `data` | [`arma::mat`](../matrices.md) | [Column-major](../matrices.md#representing-data-in-mlpack) matrix holding the dataset to be clustered. | _(N/A)_ |
+| `centroids` | [`arma::mat`](../matrices.md) | [Column-major](../matrices.md#representing-data-in-mlpack) matrix that centroids will be stored into. | _(N/A)_ |
+| `assignments` | [`arma::Row`](../matrices.md) | Vector to store cluster assignments for each point into. | _(N/A)_ |
+| `forceConvergence` | `bool` | If `true`, forces convergence of every cluster, ignoring `maxIterations`. | `false` |
+| `useSeeds` | `bool` | If `true`, estimates of high-density regions in the dataset will be used as initial centroids, instead of the full dataset. | `true`
+
+***Notes***:
+
+ * It is recommended to leave `useSeeds` to its default value of `true`. When
+ `useSeeds` is set to `false`, the entire dataset is used as the initial set
+ of centroids. For large datasets, this can be slow!
+
+ * Different types can be used for `data` and `centroids` (e.g., `arma::fmat` or
+ any dense matrix type implementing the Armadillo API). The types of `data`
+ and `centroids` must be the same.
+
+### Other Functionality
+
+ * A `MeanShift` object can be serialized with
+ [`data::Save()` and `data::Load()`](../load_save.md#mlpack-objects).
+
+ * `EstimateRadius(data, ratio=0.2)` returns a `double` that estimates a good
+ value to use for the radius parameter. `ratio` (between 0 and 1) controls
+ the percentage of the dataset used for the estimate.
+ - This function is called internally by `Cluster()` at the start of
+ clustering to choose a radius, if `radius` is less than or equal to 0.
+
+ * As an alternative to constructor parameters, the radius can be set with
+ `ms.Radius(newRadius)`, and the maximum number of iterations can be set with
+ `ms.MaxIterations() = newMaxIter`.
+
+ * `ms.Radius()` returns the current radius for clustering.
+ `ms.Radius(r)` sets the radius to `r`.
+
+ * `ms.MaxIterations()` returns the current maximum number of iterations for
+ clustering. `ms.MaxIterations() = m` sets the maximum number of iterations
+ to `m`.
+
+### Simple Examples
+
+Perform mean shift clustering on the satellite dataset and print the average
+distance from each point to its assigned centroid.
+
+```c++
+// See https://datasets.mlpack.org/satellite.train.csv.
+arma::mat dataset;
+mlpack::data::Load("satellite.train.csv", dataset, true);
+
+// Create MeanShift object with default parameters and perform clustering.
+mlpack::MeanShift ms;
+arma::mat centroids;
+arma::Row assignments;
+ms.Cluster(dataset, assignments, centroids);
+
+// Print the number of clusters.
+std::cout << "MeanShift computed " << centroids.n_cols << " clusters."
+ << std::endl;
+
+// Compute the average distance from each point to its assigned centroid.
+double sumDist = 0.0;
+for (size_t i = 0; i < dataset.n_cols; ++i)
+{
+ sumDist += mlpack::EuclideanDistance::Evaluate(
+ dataset.col(i), centroids.col(assignments[i]));
+}
+const double avgDist = sumDist / (double) dataset.n_cols;
+
+std::cout << "Average distance from a point to its assigned centroid: "
+ << avgDist << "." << std::endl;
+```
+
+---
+
+Perform mean shift clustering with custom settings of `radius` and
+`maxIterations` on the wave energy farm dataset, using `EstimateRadius()`
+to set the initial radius.
+
+```c++
+// See https://datasets.mlpack.org/wave_energy_farm_100.csv.
+arma::mat dataset;
+mlpack::data::Load("wave_energy_farm_100.csv", dataset, true);
+
+// Create MeanShift object and set parameters.
+mlpack::MeanShift ms;
+const double radiusEstimate = ms.EstimateRadius(dataset, 0.2);
+
+// Use 2x the estimate for a coarser clustering.
+ms.Radius(2.0 * radiusEstimate);
+// Use only 100 iterations.
+ms.MaxIterations() = 100;
+
+// Perform the clustering.
+arma::mat centroids;
+ms.Cluster(dataset, centroids);
+
+std::cout << "MeanShift found " << centroids.n_cols << " clusters."
+ << std::endl;
+
+// Save the centroids to disk.
+mlpack::data::Save("wave_energy_centroids.csv", centroids);
+```
+
+---
+
+Perform mean shift clustering with no kernel (e.g. unit weighting of points in a
+centroid) on the cloud dataset.
+
+```c++
+// See https://datasets.mlpack.org/cloud.csv.
+arma::mat dataset;
+mlpack::data::Load("cloud.csv", dataset, true);
+
+// Don't use a kernel for clustering. This means all points within the radius
+// are weighted equally. Use a custom radius of 25.
+mlpack::MeanShift ms(25.0, 100 /* max iterations */);
+
+arma::mat centroids;
+arma::Row assignments;
+ms.Cluster(dataset, assignments, centroids);
+
+// Print the number of clusters and the number of points in each cluster.
+std::cout << "MeanShift found " << centroids.n_cols << " clusters."
+ << std::endl;
+for (size_t i = 0; i < centroids.n_cols; ++i)
+{
+ std::cout << " - Cluster " << i << " has " << arma::accu(assignments == i)
+ << " points assigned to it." << std::endl;
+}
+```
+
+---
+
+Perform mean shift clustering with the triangular kernel on the cloud dataset,
+using 32-bit floating point matrices to represent the data.
+
+```c++
+// See https://datasets.mlpack.org/cloud.csv.
+arma::fmat dataset;
+mlpack::data::Load("cloud.csv", dataset, true);
+
+// Create the MeanShift object using a TriangularKernel.
+mlpack::TriangularKernel tk;
+mlpack::MeanShift ms(50.0 /* radius */,
+ 1000 /* max iterations */,
+ tk);
+
+// Perform clustering.
+arma::fmat centroids;
+arma::Row assignments;
+ms.Cluster(dataset, assignments, centroids);
+
+// Print the number of clusters and the number of points in each cluster.
+std::cout << "MeanShift found " << centroids.n_cols << " clusters."
+ << std::endl;
+for (size_t i = 0; i < centroids.n_cols; ++i)
+{
+ std::cout << " - Cluster " << i << " has " << arma::accu(assignments == i)
+ << " points assigned to it." << std::endl;
+}
+```
+
+---
+
+### Advanced Functionality: Template Parameters
+
+The `MeanShift` class has two template parameters that can be used for custom
+behavior. The full signature of the class is:
+
+```
+MeanShift
+```
+
+ * `UseKernel` (default `true`) is a `bool` parameter representing whether a
+ kernel function is used to weight points during centroid recomputation. If
+ it is `false`, then each point within distance `radius` of the centroid will
+ be used (without weighting) to recompute the centroid. This strategy (with
+ `UseKernel = false`) is also known as using a 'flat kernel'.
+
+ * `KernelType` represents the kernel function (or Parzen window) to be used to
+ weight points during centroid recomputation. Although many
+ [mlpack kernels](../core.md#kernels) are available, only those with
+ `Gradient()` functions (described below) are supported. Available kernels
+ for drop-in usage include:
+ - [`GaussianKernel`](../core.md#gaussiankernel) *(default)*
+ - [`EpanechnikovKernel`](../core.md#epanechnikovkernel)
+ - [`LaplacianKernel`](../core.md#laplaciankernel)
+ - [`SphericalKernel`](../core.md#sphericalkernel) *(note: this is equivalent
+ to the flat kernel, or, setting `UseKernel = false`)*
+ - [`TriangularKernel`](../core.md#triangularkernel)
+
+Custom kernels for mean shift can be easily implemented, and must implement only
+one function (`Gradient()`):
+
+```c++
+class CustomKernel
+{
+ // Evaluate the gradient of the kernel function given the distance between two
+ // points. Specifically, given that the kernel function is K(t) (where t is
+ // the distance between the two points), this function should return K'(t).
+ double Gradient(const double t);
+};
+```
diff --git a/doc/user/methods/nca.md b/doc/user/methods/nca.md
new file mode 100644
index 0000000000..4870907af8
--- /dev/null
+++ b/doc/user/methods/nca.md
@@ -0,0 +1,440 @@
+## NCA
+
+The `NCA` class implements neighborhood components analysis, which can be used
+as both a linear dimensionality reduction technique and a distance learning
+technique (also called metric learning). Neighborhood components analysis finds
+a linear transformation of the dataset that improves `k`-nearest-neighbor
+classification performance.
+
+Note that `NCA` is a computationally intensive technique (each optimization
+iteration takes time quadratic in the data size!), and may be slow to run even
+for datasets of only moderate size. See [`LMNN`](lmnn.md) for another distance
+learning technique that scales better to larger datasets.
+
+#### Simple usage example:
+
+```c++
+// Learn a distance metric that improves kNN classification performance.
+
+// All data and labels are uniform random; 10 dimensional data, 5 classes.
+// Replace with a data::Load() call or similar for a real application.
+arma::mat dataset(10, 1000, arma::fill::randu); // 1000 points.
+arma::Row labels =
+ arma::randi>(1000, arma::distr_param(0, 4));
+
+mlpack::NCA nca; // Step 1: create object.
+arma::mat distance;
+nca.LearnDistance(dataset, labels, distance); // Step 2: learn distance.
+
+// `distance` can now be used as a transformation matrix for the data.
+arma::mat transformedData = distance * dataset;
+// Or, you can create a MahalanobisDistance to evaluate points in the
+// transformed dataset space.
+arma::mat q = distance.t() * distance;
+mlpack::MahalanobisDistance d(std::move(q));
+
+std::cout << "Distance between points 0 and 1:" << std::endl;
+std::cout << " - Before NCA: "
+ << mlpack::EuclideanDistance::Evaluate(dataset.col(0), dataset.col(1))
+ << "." << std::endl;
+std::cout << " - After NCA: "
+ << d.Evaluate(dataset.col(0), dataset.col(1)) << "." << std::endl;
+```
+More examples...
+
+#### Quick links:
+
+ * [Constructors](#constructors): create `NCA` objects.
+ * [`LearnDistance()`](#learning-distances): learn distance metrics.
+ * [Other functionality](#other-functionality) for loading and saving.
+ * [Examples](#simple-examples) of simple usage and integration with other
+ techniques.
+
+#### See also:
+
+
+
+ * [mlpack distance metrics](../core.md#distances)
+ * [`LMNN`](lmnn.md)
+ * [Metric learning on Wikipedia](https://en.wikipedia.org/wiki/Similarity_learning#Metric_learning)
+ * [Neighborhood Components Analysis on Wikipedia](https://en.wikipedia.org/wiki/Neighbourhood_components_analysis)
+ * [Neighbourhood Components Analysis (pdf)](https://proceedings.neurips.cc/paper_files/paper/2004/file/42fe880812925e520249e808937738d2-Paper.pdf)
+
+### Constructors
+
+ * `nca = NCA()`
+ - Create an `NCA` object with default parameters.
+
+---
+
+ * `nca = NCA()`
+ * `nca = NCA(distance)`
+ - Create an `NCA` object using a custom
+ [`DistanceType`](../core.md#distances).
+ - An instantiated `DistanceType` can optionally be passed with the `distance`
+ parameter.
+ - Using a custom `DistanceType` means that `LearnDistance()` will learn a
+ linear transformation for the data *in the metric space of the custom
+ `DistanceType`*.
+ * This means any learned distance may not necessarily improve
+ classification performance with the
+ [Euclidean distance](../core.md#lmetric).
+ * Instead, classification performance will be improved when the learned
+ distance is used with the given `DistanceType` only.
+ - Any mlpack `DistanceType` can be used as a drop-in replacement, or a
+ [custom `DistanceType`](../../developer/distances.md).
+ * A list of mlpack's provided distance metrics can be found
+ [here](../core.md#distances).
+ - ***Note: be sure that you understand the implications of a custom
+ `DistanceType` before using this version.***
+
+---
+
+### Learning Distances
+
+Once an `NCA` object has been created, the `LearnDistance()` method can be used
+to learn a distance.
+
+ * `nca.LearnDistance(data, labels, distance, [callbacks...])`
+ * `nca.LearnDistance(data, labels, distance, optimizer, [callbacks...])`
+ - Learn a distance metric on the given `data` and `labels`, filling
+ `distance` with a transformation matrix that can be used to map the data
+ into the space of the learned distance.
+ - Optionally, pass an instantiated
+ [ensmallen optimizer](https://www.ensmallen.org) and/or
+ [ensmallen callbacks](https://www.ensmallen.org/docs.html#callback-documentation)
+ to be used for the learning process.
+ - If `distance` already has size `r` x `data.n_rows` for some `r` less than
+ or equal to `data.n_rows`, it will be used as the starting point for
+ optimization. Otherwise, the identity matrix with size `data.n_rows` x
+ `data.n_rows` will be used.
+ - When optimization is complete, `distance` will have size `r` x
+ `data.n_rows`, where `r` is less than or equal to `data.n_rows`.
+ * *Note*: If `r < data.n_rows`, then NCA has learned a distance metric that
+ also reduces the dimensionality of the data. See the
+ [last example](#simple-examples).
+
+To use `distance`, either:
+
+ * Compute a new transformed dataset as `distance * data`, or
+ * Use an instantiated [`MahalanobisDistance`](../core.md#mahalanobisdistance)
+ with `distance.t() * distance` as the `Q` matrix.
+
+See the [examples section](#simple-examples) for more details.
+
+***Caveat:*** NCA operates by repeatedly computing expressions of the form
+`exp(-distance.Evaluate(data.col(i), data.col(j)))` (that is, the exponential of
+the negative distance between two points). When distances are very large, this
+***quantity underflows to 0*** and results will not be reasonable.
+
+ - This situation can be detected, usually by a result where `distance` is equal
+ to the identity matrix.
+ - Alternately, if the [`ens::ProgressBar()`
+ callback](https://www.ensmallen.org/docs.html#progressbar) is used, a loss of
+ 0 often means this situation has occurred.
+ - To mitigate the problem, consider scaling data such that the maximum pairwise
+ distance is less than 10. See the [simple examples](#simple-examples) that
+ use the `vehicle` dataset.
+
+#### `LearnDistance()` Parameters:
+
+| **name** | **type** | **description** |
+|----------|----------|-----------------|
+| `data` | [`arma::mat`](../matrices.md) | [Column-major](../matrices.md#representing-data-in-mlpack) training matrix. |
+| `labels` | [`arma::Row`](../matrices.md) | Training labels, [between `0` and `numClasses - 1`](../load_save.md#normalizing-labels) (inclusive). Should have length `data.n_cols`. |
+| `distance` | [`arma::mat`](../matrices.md) | Output matrix to store transformation matrix representing learned distance. |
+| `optimizer` | [any ensmallen optimizer](https://www.ensmallen.org) | Instantiated ensmallen optimizer for [differentiable functions](https://www.ensmallen.org/docs.html#differentiable-functions) or [differentiable separable functions](https://www.ensmallen.org/docs.html#differentiable-separable-functions). | `ens::StandardSGD()` |
+| `callbacks...` | [any set of ensmallen callbacks](https://www.ensmallen.org/docs.html#callback-documentation) | Optional callbacks for the ensmallen optimizer, such as e.g. `ens::ProgressBar()`, `ens::Report()`, or others. | _(N/A)_ |
+
+***Note***: any matrix type can be used for `data` and `distance`, so long as
+that type implements the Armadillo API. So, e.g., `arma::fmat` can be used.
+
+### Other Functionality
+
+ * An `NCA` object can be serialized with
+ [`data::Save()` and `data::Load()`](../load_save.md#mlpack-objects).
+ Note that this is only meaningful if a custom `DistanceType` is being used,
+ and that custom `DistanceType` has state to be saved.
+
+ * `nca.Distance()` will return the `DistanceType` being used for learning.
+ Unless a custom `DistanceType` was specified in the constructor,
+ this simply returns a [`SquaredEuclideanDistance`](../core.md#lmetric)
+ object.
+
+### Simple Examples
+
+Learn a distance metric to improve classification performance on the iris
+dataset, and show improved performance when using
+[`NaiveBayesClassifier`](naive_bayes_classifier.md).
+
+```c++
+// See https://datasets.mlpack.org/iris.csv.
+arma::mat dataset;
+mlpack::data::Load("iris.csv", dataset, true);
+// See https://datasets.mlpack.org/iris.labels.csv.
+arma::Row labels;
+mlpack::data::Load("iris.labels.csv", labels, true);
+
+// Create an NCA object and learn a distance.
+arma::mat distance;
+mlpack::NCA nca;
+nca.LearnDistance(dataset, labels, distance);
+
+// The distance matrix has size equal to the dimensionality of the data.
+std::cout << "Learned distance size: " << distance.n_rows << " x "
+ << distance.n_cols << "." << std::endl;
+
+// Learn a NaiveBayesClassifier model on the data and print the performance.
+mlpack::NaiveBayesClassifier nbc1(dataset, labels, 3);
+arma::Row predictions;
+nbc1.Classify(dataset, predictions);
+std::cout << "Naive Bayes Classifier without NCA: "
+ << arma::accu(labels == predictions) << " of " << labels.n_elem
+ << " correct." << std::endl;
+
+// Now transform the data and learn another NaiveBayesClassifier.
+arma::mat transformedDataset = distance * dataset;
+mlpack::NaiveBayesClassifier nbc2(transformedDataset, labels, 3);
+nbc2.Classify(transformedDataset, predictions);
+std::cout << "Naive Bayes Classifier with NCA: "
+ << arma::accu(labels == predictions) << " of " << labels.n_elem
+ << " correct." << std::endl;
+```
+
+---
+
+Learn a distance metric on the ionosphere dataset, using 32-bit floating point
+to represent the data and metric.
+
+```c++
+// See https://datasets.mlpack.org/ionosphere.csv.
+arma::fmat dataset;
+mlpack::data::Load("ionosphere.csv", dataset, true);
+
+// The labels are the last row of the dataset.
+arma::Row labels =
+ arma::conv_to>::from(dataset.row(dataset.n_rows - 1));
+dataset.shed_row(dataset.n_rows - 1);
+
+// Create an NCA object and learn distance on float32 data.
+// To keep computation time down, we use an instantiated optimizer that will
+// only perform 10 epochs of training. (In a real application you may want to
+// train for longer!)
+arma::fmat distance;
+mlpack::NCA nca;
+
+ens::StandardSGD opt;
+opt.MaxIterations() = 10 * dataset.n_cols;
+nca.LearnDistance(dataset, labels, distance, opt, ens::ProgressBar());
+
+// We want to compute six quantities:
+//
+// - Average distance to points of the same class before NCA.
+// - Average distance to points of the same class after NCA, using
+// MahalanobisDistance.
+// - Average distance to points of the same class after NCA, using the
+// transformed dataset.
+//
+// - The same three quantities above, but for points of the other class.
+//
+// NCA should reduce the average distance to points in the same class, while
+// increasing the average distance to points in other classes.
+float distSums[6] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
+size_t sameCount = 0;
+arma::fmat q = distance.t() * distance;
+mlpack::MahalanobisDistance md(std::move(q));
+arma::fmat transformedDataset = distance * dataset;
+for (size_t i = 1; i < dataset.n_cols; ++i)
+{
+ const double d1 = mlpack::EuclideanDistance::Evaluate(
+ dataset.col(0), dataset.col(i));
+ const double d2 = md.Evaluate(dataset.col(0), dataset.col(i));
+ const double d3 = mlpack::EuclideanDistance::Evaluate(
+ transformedDataset.col(0), transformedDataset.col(i));
+
+ // Determine whether the point has the same label as point 0.
+ if (labels[i] == labels[0])
+ {
+ distSums[0] += d1;
+ distSums[1] += d2;
+ distSums[2] += d3;
+ ++sameCount;
+ }
+ else
+ {
+ distSums[3] += d1;
+ distSums[4] += d2;
+ distSums[5] += d3;
+ }
+}
+
+// Turn the results into average distances across the class.
+distSums[0] /= sameCount;
+distSums[1] /= sameCount;
+distSums[2] /= sameCount;
+distSums[3] /= (dataset.n_cols - sameCount);
+distSums[4] /= (dataset.n_cols - sameCount);
+distSums[5] /= (dataset.n_cols - sameCount);
+
+// Print the results.
+std::cout << "Average distance between point 0 and other points of the same "
+ << "class:" << std::endl;
+std::cout << " - Before NCA: " << distSums[0] << "."
+ << std::endl;
+std::cout << " - After NCA (with MahalanobisDistance): " << distSums[1] << "."
+ << std::endl;
+std::cout << " - After NCA (with transformed dataset): " << distSums[2] << "."
+ << std::endl;
+std::cout << std::endl;
+
+std::cout << "Average distance between point 0 and points of other classes: "
+ << std::endl;
+std::cout << " - Before NCA: " << distSums[3] << "."
+ << std::endl;
+std::cout << " - After NCA (with MahalanobisDistance): " << distSums[4] << "."
+ << std::endl;
+std::cout << " - After NCA (with transformed dataset): " << distSums[5] << "."
+ << std::endl;
+std::cout << std::endl;
+
+std::cout << "Ratio of other-class to same-class distances:" << std::endl;
+std::cout << "(We expect this to go up.)" << std::endl;
+std::cout << " - Before NCA: " << (distSums[3] / distSums[0]) << "."
+ << std::endl;
+std::cout << " - After NCA: " << (distSums[5] / distSums[2]) << "."
+ << std::endl;
+```
+
+---
+
+Learn a distance metric on the iris dataset, using the L-BFGS optimizer with
+callbacks.
+
+```c++
+// See https://datasets.mlpack.org/iris.csv.
+arma::mat dataset;
+mlpack::data::Load("iris.csv", dataset, true);
+// See https://datasets.mlpack.org/iris.labels.csv.
+arma::Row labels;
+mlpack::data::Load("iris.labels.csv", labels, true);
+
+// Learn a distance with ensmallen's L-BFGS optimizer.
+ens::L_BFGS lbfgs;
+lbfgs.NumBasis() = 5;
+lbfgs.MaxIterations() = 1000;
+
+arma::mat distance;
+mlpack::NCA nca;
+
+// Use a callback that prints a final optimization report.
+nca.LearnDistance(dataset, labels, distance, lbfgs, ens::Report());
+```
+
+---
+
+
+
+Learn a distance metric on the vehicle dataset, but instead of using the
+Euclidean distance as the underlying metric, use the Manhattan distance. This
+means that NCA is optimizing k-NN performance under the Manhattan distance, not
+under the Euclidean distance.
+
+```c++
+// See https://datasets.mlpack.org/vehicle.csv.
+arma::mat dataset;
+mlpack::data::Load("vehicle.csv", dataset, true);
+
+// The labels are contained as the last row of the dataset.
+arma::Row labels =
+ arma::conv_to>::from(dataset.row(dataset.n_rows - 1));
+dataset.shed_row(dataset.n_rows - 1);
+
+// Because typical distances between points in the vehicle dataset are large,
+// we will center the dataset and scale it to have points in the unit ball.
+// (That is, all points will have values in each dimension between -1 and 1.)
+// This means that the maximum pairwise distance is 2.
+dataset.each_col() -= arma::mean(dataset, 1);
+dataset /= arma::max(arma::max(arma::abs(dataset)));
+
+// Create the NCA object and optimize. Use Nesterov momentum SGD, printing a
+// progress bar during optimization.
+mlpack::NCA nca;
+arma::mat distance;
+ens::NesterovMomentumSGD opt(0.01 /* step size */,
+ 32 /* batch size */,
+ 20 * dataset.n_cols /* 20 epochs */);
+nca.LearnDistance(dataset, labels, distance, opt, ens::ProgressBar());
+
+// Now inspect distances between points with the Euclidean distance and with the
+// inner product distance.
+arma::mat transformedDataset = distance * dataset;
+
+// Points 0 and 1 have the same label (0). See their original distance---with
+// both the Euclidean and Manhattan distances---and their transformed distances.
+// We expect these points to get closer together, in the Manhattan distance.
+const double d1 = mlpack::ManhattanDistance::Evaluate(
+ dataset.col(0), dataset.col(1));
+const double d2 = mlpack::ManhattanDistance::Evaluate(
+ transformedDataset.col(0), transformedDataset.col(1));
+
+std::cout << "Distance between points 0 and 1 (same class):" << std::endl;
+std::cout << " - Manhattan distance:" << std::endl;
+std::cout << " * Before NCA: " << d1 << std::endl;
+std::cout << " * After NCA: " << d2 << std::endl;
+std::cout << std::endl;
+
+// Point 3 has a different label. We therefore expect this point to get further
+// from point 0 with the Manhattan distance, but not necessarily with the
+// Euclidean distance.
+const double d3 = mlpack::ManhattanDistance::Evaluate(
+ dataset.col(0), dataset.col(3));
+const double d4 = mlpack::ManhattanDistance::Evaluate(
+ transformedDataset.col(0), transformedDataset.col(3));
+
+std::cout << "Distance between points 0 and 3 (different class):" << std::endl;
+std::cout << " - Manhattan distance:" << std::endl;
+std::cout << " * Before NCA: " << d3 << std::endl;
+std::cout << " * After NCA: " << d4 << std::endl;
+
+// Note that point 3 has been moved further away from point 0 than point 1.
+```
+
+---
+
+Learn a distance metric while also performing dimensionality reduction, reducing
+the dimensionality of the vehicle dataset by 2 dimensions.
+
+```c++
+// See https://datasets.mlpack.org/vehicle.csv.
+arma::mat dataset;
+mlpack::data::Load("vehicle.csv", dataset, true);
+
+// The labels are contained as the last row of the dataset.
+arma::Row labels =
+ arma::conv_to>::from(dataset.row(dataset.n_rows - 1));
+dataset.shed_row(dataset.n_rows - 1);
+
+// Because typical distances between points in the vehicle dataset are large,
+// we will center the dataset and scale it to have points in the unit ball.
+// (That is, all points will have values in each dimension between -1 and 1.)
+// This means that the maximum pairwise distance is 2.
+dataset.each_col() -= arma::mean(dataset, 1);
+dataset /= arma::max(arma::max(arma::abs(dataset)));
+
+// Use a random initialization for the distance transformation, with the
+// specified output dimensionality.
+arma::mat distance(dataset.n_rows - 2, dataset.n_rows, arma::fill::randu);
+mlpack::NCA nca;
+ens::L_BFGS opt;
+opt.MaxIterations() = 10; // You may want more in a real application.
+nca.LearnDistance(dataset, labels, distance, opt);
+
+// Now transform the dataset.
+arma::mat transformedData = distance * dataset;
+
+std::cout << std::endl << std::endl;
+std::cout << "Original data has size " << dataset.n_rows << " x "
+ << dataset.n_cols << "." << std::endl;
+std::cout << "Transformed data has size " << transformedData.n_rows << " x "
+ << transformedData.n_cols << "." << std::endl;
+```
diff --git a/scripts/build-docs.sh b/scripts/build-docs.sh
index d3ddd010ea..0e1187ec56 100755
--- a/scripts/build-docs.sh
+++ b/scripts/build-docs.sh
@@ -422,6 +422,7 @@ then
--suppress-broken 405 \
--suppress-broken 503 \
--suppress-broken 301 \
+ --suppress-broken 400 \
-X "https://eigen.tuxfamily.org/index.php\?title=Main_Page" \
-X "https://mlpack.slack.com/" "$f" 2>&1 |
grep -v 'Use of uninitialized value' > checklink_out;
diff --git a/src/mlpack/base.hpp b/src/mlpack/base.hpp
index 780435d4c5..c296733d27 100644
--- a/src/mlpack/base.hpp
+++ b/src/mlpack/base.hpp
@@ -84,6 +84,7 @@
// Now include Armadillo and traits that we use for it.
#include
#include
+#include
// On Visual Studio, disable C4519 (default arguments for function templates)
// since it's by default an error, which doesn't even make any sense because
diff --git a/src/mlpack/bindings/go/mlpack/capi/arma_util.hpp b/src/mlpack/bindings/go/mlpack/capi/arma_util.hpp
index 0c57590e26..4034934ba7 100644
--- a/src/mlpack/bindings/go/mlpack/capi/arma_util.hpp
+++ b/src/mlpack/bindings/go/mlpack/capi/arma_util.hpp
@@ -39,9 +39,7 @@ inline typename T::elem_type* GetMemory(T& m)
arma::access::rw(m.mem_state) = 1;
// With Armadillo 10 and newer, we must set `n_alloc` to 0 so that
// Armadillo does not deallocate the memory.
- #if ARMA_VERSION_MAJOR >= 10
- arma::access::rw(m.n_alloc) = 0;
- #endif
+ arma::access::rw(m.n_alloc) = 0;
return m.memptr();
}
}
diff --git a/src/mlpack/bindings/julia/julia_util.cpp b/src/mlpack/bindings/julia/julia_util.cpp
index 5c0320a311..c3340296a6 100644
--- a/src/mlpack/bindings/julia/julia_util.cpp
+++ b/src/mlpack/bindings/julia/julia_util.cpp
@@ -428,9 +428,7 @@ double* GetParamMat(void* params, const char* paramName)
else
{
arma::access::rw(mat.mem_state) = 1;
- #if ARMA_VERSION_MAJOR >= 10
- arma::access::rw(mat.n_alloc) = 0;
- #endif
+ arma::access::rw(mat.n_alloc) = 0;
return mat.memptr();
}
}
@@ -475,9 +473,7 @@ size_t* GetParamUMat(void* params, const char* paramName)
else
{
arma::access::rw(mat.mem_state) = 1;
- #if ARMA_VERSION_MAJOR >= 10
- arma::access::rw(mat.n_alloc) = 0;
- #endif
+ arma::access::rw(mat.n_alloc) = 0;
return mat.memptr();
}
}
@@ -513,9 +509,7 @@ double* GetParamCol(void* params, const char* paramName)
else
{
arma::access::rw(vec.mem_state) = 1;
- #if ARMA_VERSION_MAJOR >= 10
- arma::access::rw(vec.n_alloc) = 0;
- #endif
+ arma::access::rw(vec.n_alloc) = 0;
return vec.memptr();
}
}
@@ -552,9 +546,7 @@ size_t* GetParamUCol(void* params, const char* paramName)
else
{
arma::access::rw(vec.mem_state) = 1;
- #if ARMA_VERSION_MAJOR >= 10
- arma::access::rw(vec.n_alloc) = 0;
- #endif
+ arma::access::rw(vec.n_alloc) = 0;
return vec.memptr();
}
}
@@ -590,9 +582,7 @@ double* GetParamRow(void* params, const char* paramName)
else
{
arma::access::rw(vec.mem_state) = 1;
- #if ARMA_VERSION_MAJOR >= 10
- arma::access::rw(vec.n_alloc) = 0;
- #endif
+ arma::access::rw(vec.n_alloc) = 0;
return vec.memptr();
}
}
@@ -629,9 +619,7 @@ size_t* GetParamURow(void* params, const char* paramName)
else
{
arma::access::rw(vec.mem_state) = 1;
- #if ARMA_VERSION_MAJOR >= 10
- arma::access::rw(vec.n_alloc) = 0;
- #endif
+ arma::access::rw(vec.n_alloc) = 0;
return vec.memptr();
}
}
@@ -707,9 +695,7 @@ double* GetParamMatWithInfoPtr(void* params, const char* paramName)
else
{
arma::access::rw(m.mem_state) = 1;
- #if ARMA_VERSION_MAJOR >= 10
- arma::access::rw(m.n_alloc) = 0;
- #endif
+ arma::access::rw(m.n_alloc) = 0;
return m.memptr();
}
}
diff --git a/src/mlpack/bindings/python/mlpack/arma_util.hpp b/src/mlpack/bindings/python/mlpack/arma_util.hpp
index 70f0dd1b3e..ca6a8d1a71 100644
--- a/src/mlpack/bindings/python/mlpack/arma_util.hpp
+++ b/src/mlpack/bindings/python/mlpack/arma_util.hpp
@@ -25,9 +25,8 @@ void SetMemState(T& t, int state)
// If we just "released" the memory, so that the matrix does not own it, with
// Armadillo 10 we must also ensure that the matrix does not deallocate the
// memory by specifying `n_alloc = 0`.
- #if ARMA_VERSION_MAJOR >= 10
- const_cast(t.n_alloc) = 0;
- #endif
+
+ const_cast(t.n_alloc) = 0;
}
/**
diff --git a/src/mlpack/bindings/python/mlpack/matrix_utils.py b/src/mlpack/bindings/python/mlpack/matrix_utils.py
index dc6362cc43..3cf39126d5 100644
--- a/src/mlpack/bindings/python/mlpack/matrix_utils.py
+++ b/src/mlpack/bindings/python/mlpack/matrix_utils.py
@@ -160,7 +160,10 @@ def to_matrix_with_info(x, dtype, copy=False):
dims = len(x)
d = np.zeros([dims])
- out = np.array(x, dtype=dtype, copy=copy) # Try to avoid copy...
+ if np.lib.NumpyVersion(np.__version__) >= '2.0.0b1':
+ out = np.array(x, dtype=dtype, copy=(True if copy else None))
+ else:
+ out = np.array(x, dtype=dtype, copy=copy)
# Since we don't have a great way to check if these are using the same
# memory location, we will probe manually (ugh).
diff --git a/src/mlpack/core.hpp b/src/mlpack/core.hpp
index 73eeaaece4..7205587ab5 100644
--- a/src/mlpack/core.hpp
+++ b/src/mlpack/core.hpp
@@ -37,6 +37,7 @@
// Now the core mlpack classes.
#include
#include
+#include
#include
#include
#include
diff --git a/src/mlpack/core/distributions/gamma_distribution_impl.hpp b/src/mlpack/core/distributions/gamma_distribution_impl.hpp
index 87c02d068e..875a437013 100644
--- a/src/mlpack/core/distributions/gamma_distribution_impl.hpp
+++ b/src/mlpack/core/distributions/gamma_distribution_impl.hpp
@@ -74,9 +74,9 @@ inline void GammaDistribution::Train(const arma::mat& rdata,
if (arma::size(rdata) == arma::size(arma::mat()))
return;
- arma::vec meanLogxVec(rdata.n_rows, arma::fill::zeros);
- arma::vec meanxVec(rdata.n_rows, arma::fill::zeros);
- arma::vec logMeanxVec(rdata.n_rows, arma::fill::zeros);
+ arma::vec meanLogxVec(rdata.n_rows);
+ arma::vec meanxVec(rdata.n_rows);
+ arma::vec logMeanxVec(rdata.n_rows);
for (size_t i = 0; i < rdata.n_cols; ++i)
{
diff --git a/src/mlpack/core/tree/ballbound.hpp b/src/mlpack/core/tree/ballbound.hpp
index 0f1f8f9636..9a7e5f1977 100644
--- a/src/mlpack/core/tree/ballbound.hpp
+++ b/src/mlpack/core/tree/ballbound.hpp
@@ -27,12 +27,11 @@ namespace mlpack {
* @tparam VecType Type of vector (arma::vec or arma::sp_vec or similar).
*/
template,
- typename VecType = arma::vec>
+ typename ElemType = double,
+ typename VecType = arma::Col>
class BallBound
{
public:
- //! The underlying data type.
- typedef typename VecType::elem_type ElemType;
//! A public version of the vector type.
typedef VecType Vec;
diff --git a/src/mlpack/core/tree/ballbound_impl.hpp b/src/mlpack/core/tree/ballbound_impl.hpp
index 123f0200cb..8793c3077c 100644
--- a/src/mlpack/core/tree/ballbound_impl.hpp
+++ b/src/mlpack/core/tree/ballbound_impl.hpp
@@ -20,8 +20,8 @@
namespace mlpack {
//! Empty Constructor.
-template
-BallBound::BallBound() :
+template
+BallBound::BallBound() :
radius(std::numeric_limits::lowest()),
distance(new DistanceType()),
ownsDistance(true)
@@ -32,8 +32,8 @@ BallBound::BallBound() :
*
* @param dimension Dimensionality of ball bound.
*/
-template
-BallBound::BallBound(const size_t dimension) :
+template
+BallBound::BallBound(const size_t dimension) :
radius(std::numeric_limits::lowest()),
center(dimension),
distance(new DistanceType()),
@@ -46,9 +46,9 @@ BallBound::BallBound(const size_t dimension) :
* @param radius Radius of ball bound.
* @param center Center of ball bound.
*/
-template
-BallBound::BallBound(const ElemType radius,
- const VecType& center) :
+template
+BallBound::BallBound(const ElemType radius,
+ const VecType& center) :
radius(radius),
center(center),
distance(new DistanceType()),
@@ -56,8 +56,8 @@ BallBound::BallBound(const ElemType radius,
{ /* Nothing to do. */ }
//! Copy Constructor. To prevent memory leaks.
-template
-BallBound::BallBound(const BallBound& other) :
+template
+BallBound::BallBound(const BallBound& other) :
radius(other.radius),
center(other.center),
distance(other.distance),
@@ -65,8 +65,9 @@ BallBound::BallBound(const BallBound& other) :
{ /* Nothing to do. */ }
//! For the same reason as the copy constructor: to prevent memory leaks.
-template
-BallBound& BallBound::operator=(
+template
+BallBound&
+BallBound::operator=(
const BallBound& other)
{
if (this != &other)
@@ -80,8 +81,8 @@ BallBound& BallBound::operator=(
}
//! Move constructor.
-template
-BallBound::BallBound(BallBound&& other) :
+template
+BallBound::BallBound(BallBound&& other) :
radius(other.radius),
center(other.center),
distance(other.distance),
@@ -95,8 +96,9 @@ BallBound::BallBound(BallBound&& other) :
}
//! Move assignment operator.
-template
-BallBound& BallBound::operator=(
+template
+BallBound&
+BallBound::operator=(
BallBound&& other)
{
if (this != &other)
@@ -115,29 +117,30 @@ BallBound& BallBound::operator=(
}
//! Destructor to release allocated memory.
-template
-BallBound::~BallBound()
+template
+BallBound::~BallBound()
{
if (ownsDistance)
delete distance;
}
//! Get the range in a certain dimension.
-template
-RangeType::ElemType>
-BallBound::operator[](const size_t i) const
+template
+RangeType
+BallBound::operator[](const size_t i) const
{
if (radius < 0)
- return Range();
+ return RangeType();
else
- return Range(center[i] - radius, center[i] + radius);
+ return RangeType(center[i] - radius, center[i] + radius);
}
/**
* Determines if a point is within the bound.
*/
-template
-bool BallBound::Contains(const VecType& point) const
+template
+bool BallBound::Contains(const VecType& point)
+ const
{
if (radius < 0)
return false;
@@ -148,10 +151,9 @@ bool BallBound::Contains(const VecType& point) const
/**
* Calculates minimum bound-to-point squared distance.
*/
-template
+template
template
-typename BallBound::ElemType
-BallBound::MinDistance(
+ElemType BallBound::MinDistance(
const OtherVecType& point,
typename std::enable_if_t::value>* /* junk */) const
{
@@ -164,10 +166,9 @@ BallBound::MinDistance(
/**
* Calculates minimum bound-to-bound squared distance.
*/
-template
-typename BallBound::ElemType
-BallBound::MinDistance(const BallBound& other)
- const
+template
+ElemType BallBound::MinDistance(
+ const BallBound& other) const
{
if (radius < 0)
return std::numeric_limits::max();
@@ -182,10 +183,9 @@ BallBound::MinDistance(const BallBound& other)
/**
* Computes maximum distance.
*/
-template
+template
template
-typename BallBound::ElemType
-BallBound::MaxDistance(
+ElemType BallBound::MaxDistance(
const OtherVecType& point,
typename std::enable_if_t::value>* /* junk */) const
{
@@ -198,10 +198,9 @@ BallBound::MaxDistance(
/**
* Computes maximum distance.
*/
-template
-typename BallBound::ElemType
-BallBound::MaxDistance(const BallBound& other)
- const
+template
+ElemType BallBound::MaxDistance(
+ const BallBound& other) const
{
if (radius < 0)
return std::numeric_limits::max();
@@ -214,36 +213,36 @@ BallBound::MaxDistance(const BallBound& other)
*
* Example: bound1.MinDistanceSq(other) for minimum squared distance.
*/
-template
+template
template
-RangeType::ElemType>
-BallBound::RangeDistance(
+RangeType BallBound::RangeDistance(
const OtherVecType& point,
typename std::enable_if_t::value>* /* junk */) const
{
if (radius < 0)
- return Range(std::numeric_limits::max(),
- std::numeric_limits::max());
+ return RangeType(std::numeric_limits::max(),
+ std::numeric_limits::max());
else
{
const ElemType dist = distance->Evaluate(center, point);
- return Range(std::max(dist - radius, (ElemType) 0.0), dist + radius);
+ return RangeType(std::max(dist - radius, (ElemType) 0.0),
+ dist + radius);
}
}
-template
-RangeType::ElemType>
-BallBound::RangeDistance(
+template
+RangeType BallBound::RangeDistance(
const BallBound& other) const
{
if (radius < 0)
- return Range(std::numeric_limits::max(),
- std::numeric_limits::max());
+ return RangeType(std::numeric_limits::max(),
+ std::numeric_limits::max());
else
{
const ElemType dist = distance->Evaluate(center, other.center);
const ElemType sumradius = radius + other.radius;
- return Range(std::max(dist - sumradius, (ElemType) 0.0), dist + sumradius);
+ return RangeType(std::max(dist - sumradius, (ElemType) 0.0),
+ dist + sumradius);
}
}
@@ -253,10 +252,10 @@ BallBound::RangeDistance(
* The difference lies in the way we initialize the ball bound. The way we
* expand the bound is same.
*/
-template
+template
template
-const BallBound&
-BallBound::operator|=(const MatType& data)
+const BallBound&
+BallBound::operator|=(const MatType& data)
{
if (radius < 0)
{
@@ -284,9 +283,9 @@ BallBound::operator|=(const MatType& data)
}
//! Serialize the BallBound.
-template
+template
template
-void BallBound::serialize(
+void BallBound::serialize(
Archive& ar,
const uint32_t /* version */)
{
diff --git a/src/mlpack/core/tree/binary_space_tree/binary_space_tree.hpp b/src/mlpack/core/tree/binary_space_tree/binary_space_tree.hpp
index 050f26e553..500226524e 100644
--- a/src/mlpack/core/tree/binary_space_tree/binary_space_tree.hpp
+++ b/src/mlpack/core/tree/binary_space_tree/binary_space_tree.hpp
@@ -46,10 +46,11 @@ namespace mlpack {
template class BoundType =
- HRectBound,
- template
- class SplitType = MidpointSplit>
+ template class BoundType = HRectBound,
+ template class SplitType = MidpointSplit>
class BinarySpaceTree
{
public:
@@ -58,7 +59,7 @@ class BinarySpaceTree
//! The type of element held in MatType.
typedef typename MatType::elem_type ElemType;
- typedef SplitType, MatType> Split;
+ typedef SplitType, MatType> Split;
private:
//! The left child node.
@@ -74,7 +75,7 @@ class BinarySpaceTree
//! children).
size_t count;
//! The bound object for this node.
- BoundType bound;
+ BoundType bound;
//! Any extra data contained in the node.
StatisticType stat;
//! The distance from the centroid of this node to the centroid of the parent.
@@ -210,7 +211,8 @@ class BinarySpaceTree
BinarySpaceTree(BinarySpaceTree* parent,
const size_t begin,
const size_t count,
- SplitType, MatType>& splitter,
+ SplitType, MatType>&
+ splitter,
const size_t maxLeafSize = 20);
/**
@@ -236,7 +238,8 @@ class BinarySpaceTree
const size_t begin,
const size_t count,
std::vector& oldFromNew,
- SplitType, MatType>& splitter,
+ SplitType, MatType>&
+ splitter,
const size_t maxLeafSize = 20);
/**
@@ -266,7 +269,8 @@ class BinarySpaceTree
const size_t count,
std::vector& oldFromNew,
std::vector& newFromOld,
- SplitType, MatType>& splitter,
+ SplitType, MatType>&
+ splitter,
const size_t maxLeafSize = 20);
/**
@@ -315,9 +319,9 @@ class BinarySpaceTree
~BinarySpaceTree();
//! Return the bound object for this node.
- const BoundType& Bound() const { return bound; }
+ const BoundType& Bound() const { return bound; }
//! Return the bound object for this node.
- BoundType& Bound() { return bound; }
+ BoundType& Bound() { return bound; }
//! Return the statistic object for this node.
const StatisticType& Stat() const { return stat; }
@@ -517,8 +521,9 @@ class BinarySpaceTree
* @param maxLeafSize Maximum number of points held in a leaf.
* @param splitter Instantiated SplitType object.
*/
- void SplitNode(const size_t maxLeafSize,
- SplitType, MatType>& splitter);
+ void SplitNode(
+ const size_t maxLeafSize,
+ SplitType, MatType>& splitter);
/**
* Splits the current node, assigning its left and right children recursively.
@@ -528,9 +533,10 @@ class BinarySpaceTree
* @param maxLeafSize Maximum number of points held in a leaf.
* @param splitter Instantiated SplitType object.
*/
- void SplitNode(std::vector& oldFromNew,
- const size_t maxLeafSize,
- SplitType, MatType>& splitter);
+ void SplitNode(
+ std::vector& oldFromNew,
+ const size_t maxLeafSize,
+ SplitType, MatType>& splitter);
/**
* Update the bound of the current node. This method does not take into
@@ -547,7 +553,7 @@ class BinarySpaceTree
*
* @param boundToUpdate The bound to update.
*/
- void UpdateBound(HollowBallBound& boundToUpdate);
+ void UpdateBound(HollowBallBound& boundToUpdate);
protected:
/**
diff --git a/src/mlpack/core/tree/binary_space_tree/binary_space_tree_impl.hpp b/src/mlpack/core/tree/binary_space_tree/binary_space_tree_impl.hpp
index 7859364462..74c73d3a25 100644
--- a/src/mlpack/core/tree/binary_space_tree/binary_space_tree_impl.hpp
+++ b/src/mlpack/core/tree/binary_space_tree/binary_space_tree_impl.hpp
@@ -24,9 +24,11 @@ namespace mlpack {
template class BoundType,
- template
- class SplitType>
+ template class BoundType,
+ template class SplitType>
BinarySpaceTree::
BinarySpaceTree(
const MatType& data,
@@ -41,7 +43,7 @@ BinarySpaceTree(
dataset(new MatType(data)) // Copies the dataset.
{
// Do the actual splitting of this node.
- SplitType, MatType> splitter;
+ SplitType, MatType> splitter;
SplitNode(maxLeafSize, splitter);
// Create the statistic depending on if we are a leaf or not.
@@ -51,9 +53,11 @@ BinarySpaceTree(
template class BoundType,
- template
- class SplitType>
+ template class BoundType,
+ template class SplitType>
BinarySpaceTree::
BinarySpaceTree(
const MatType& data,
@@ -74,7 +78,7 @@ BinarySpaceTree(
oldFromNew[i] = i; // Fill with unharmed indices.
// Now do the actual splitting.
- SplitType, MatType> splitter;
+ SplitType, MatType> splitter;
SplitNode(oldFromNew, maxLeafSize, splitter);
// Create the statistic depending on if we are a leaf or not.
@@ -84,9 +88,11 @@ BinarySpaceTree(
template class BoundType,
- template
- class SplitType>
+ template class BoundType,
+ template class SplitType>
BinarySpaceTree::
BinarySpaceTree(
const MatType& data,
@@ -108,7 +114,7 @@ BinarySpaceTree(
oldFromNew[i] = i; // Fill with unharmed indices.
// Now do the actual splitting.
- SplitType, MatType> splitter;
+ SplitType, MatType> splitter;
SplitNode(oldFromNew, maxLeafSize, splitter);
// Create the statistic depending on if we are a leaf or not.
@@ -123,9 +129,11 @@ BinarySpaceTree(
template class BoundType,
- template
- class SplitType>
+ template class BoundType,
+ template class SplitType>
BinarySpaceTree::
BinarySpaceTree(MatType&& data, const size_t maxLeafSize) :
left(NULL),
@@ -138,7 +146,7 @@ BinarySpaceTree(MatType&& data, const size_t maxLeafSize) :
dataset(new MatType(std::move(data)))
{
// Do the actual splitting of this node.
- SplitType, MatType> splitter;
+ SplitType, MatType> splitter;
SplitNode(maxLeafSize, splitter);
// Create the statistic depending on if we are a leaf or not.
@@ -148,9 +156,11 @@ BinarySpaceTree(MatType&& data, const size_t maxLeafSize) :
template class BoundType,
- template