diff --git a/.appveyor.yml b/.appveyor.yml index 08c8dc4468..722a597468 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -71,7 +71,6 @@ build_script: -DARMADILLO_LIBRARY:FILEPATH=%ARMADILLO_LIBRARY% -DCEREAL_INCLUDE_DIR="C:/projects/mlpack/unofficial-flayan-cereal.1.2.2/build/native/include" -DBOOST_INCLUDEDIR:PATH=%BOOST_INCLUDE% - -DBOOST_LIBRARYDIR:PATH="C:/projects/mlpack/boost_libs" -DDEBUG=OFF -DPROFILE=OFF -DBUILD_PYTHON_BINDINGS=OFF diff --git a/.ci/ci.yaml b/.ci/ci.yaml index 5348c55cc9..26efee03ae 100644 --- a/.ci/ci.yaml +++ b/.ci/ci.yaml @@ -59,25 +59,6 @@ jobs: steps: - template: macos-steps.yaml -# - job: WindowsVS15 -# timeoutInMinutes: 360 -# displayName: Windows VS15 -# pool: -# vmImage: vs2017-win2016 -# strategy: -# matrix: -# Plain: -# CMakeArgs: '-DDEBUG=ON -DPROFILE=OFF -DBUILD_PYTHON_BINDINGS=OFF -DBUILD_GO_BINDINGS=OFF -DBUILD_R_BINDINGS=OFF' -# python.version: '2.7' -# CMakeGenerator: '-G "Visual Studio 15 2017 Win64"' -# MSBuildVersion: '15.0' -# ArchiveNoLibs: 'mlpack-windows-vs15-no-libs.zip' -# ArchiveLibs: 'mlpack-windows-vs15.zip' -# ArchiveTests: 'mlpack_test-vs15.xml' - -# steps: -# - template: windows-steps.yaml - - job: WindowsVS16 timeoutInMinutes: 360 displayName: Windows VS16 diff --git a/.ci/windows-steps.yaml b/.ci/windows-steps.yaml index 4281ab23f8..e2a9ed38e0 100644 --- a/.ci/windows-steps.yaml +++ b/.ci/windows-steps.yaml @@ -116,6 +116,56 @@ steps: replaceExistingArchive: true displayName: 'Build artifacts' +# Build MSI installer. +- powershell: | + # Pull the documentation for the installer. + try { + $url = "http://ci.mlpack.org/job/mlpack%20-%20doxygen%20build/lastSuccessfulBuild/artifact/build/doc/html/*zip*/html.zip" + (new-object net.webclient).DownloadFile($url, 'dist\win-installer\jenkinsdoc.zip') + } + catch { + Write-Output "Unable to download precompiled Doxygen documentation from Jenkins!" + } + try { + (Add-Type -AssemblyName System.IO.Compression.FileSystem); + [System.IO.Compression.ZipFile]::ExtractToDirectory('dist\win-installer\jenkinsdoc.zip', 'dist\win-installer\mlpack-win-installer\Sources\doc') + } + catch{Write-Output "Unable to add doc to installer, skipping!"} + # Preparing installer staging. + mkdir dist\win-installer\mlpack-win-installer\Sources\lib + cp build\Release\*.lib dist\win-installer\mlpack-win-installer\Sources\lib\ + cp build\Release\*.exp dist\win-installer\mlpack-win-installer\Sources\lib\ + cp build\Release\*.dll dist\win-installer\mlpack-win-installer\Sources\ + cp build\Release\*.exe dist\win-installer\mlpack-win-installer\Sources\ + cp $(Agent.ToolsDirectory)\OpenBLAS.0.2.14.1\lib\native\bin\x64\*.dll dist\win-installer\mlpack-win-installer\Sources\ + cp build\include\mlpack dist\win-installer\mlpack-win-installer\Sources -recurse + cp doc\examples dist\win-installer\mlpack-win-installer\Sources -recurse + cp src\mlpack\tests\data\german.csv dist\win-installer\mlpack-win-installer\Sources\examples\sample-ml-app\sample-ml-app\data\ + # Check current git version or mlpack version. + $ver = (Get-Content "src\mlpack\core\util\version.hpp" | where {$_ -like "*MLPACK_VERSION*"}); + $env:MLPACK_VERSION += $ver[0].substring($ver[0].length - 1, 1) + '.'; + $env:MLPACK_VERSION += $ver[1].substring($ver[1].length - 1, 1) + '.'; + $env:MLPACK_VERSION += $ver[2].substring($ver[2].length - 1, 1); + + if (Test-Path "src/mlpack/core/util/gitversion.hpp") + { + $ver = (Get-Content "src/mlpack/core/util/gitversion.hpp"); + $env:INSTALL_VERSION = $ver.Split('"')[1].Split(' ')[1]; + } + else + { + $env:INSTALL_VERSION = $env:MLPACK_VERSION; + } + + # Build the MSI installer. + cd dist\win-installer\mlpack-win-installer + & 'C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\MSBuild.exe' ` + -t:rebuild ` + -p:Configuration=Release ` + -p:TreatWarningsAsErrors=True ` + mlpack-win-installer.wixproj + displayName: 'Build MSI Windows installer' + # Publish artifacts to Azure Pipelines - task: PublishBuildArtifacts@1 inputs: @@ -132,6 +182,11 @@ steps: pathtoPublish: 'build/Testing/' artifactName: 'Tests' displayName: 'Publish artifacts test results' +- task: PublishBuildArtifacts@1 + inputs: + pathtoPublish: 'dist\win-installer\mlpack-win-installer\bin\Release\mlpack-windows.msi' + artifactName: mlpack-windows-installer + displayName: 'Publish Windows MSI installer' # Publish test results to Azure Pipelines - task: PublishTestResults@2 diff --git a/CMakeLists.txt b/CMakeLists.txt index 0804d28039..a184ca588a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -289,7 +289,6 @@ endif() # ARMADILLO_INCLUDE_DIRS - directories necessary for Armadillo includes # BOOST_ROOT - root of Boost installation # BOOST_INCLUDEDIR - include directory for Boost -# BOOST_LIBRARYDIR - library directory for Boost # ENSMALLEN_INCLUDE_DIR - include directory for ensmallen # STB_IMAGE_INCLUDE_DIR - include directory for STB image library # MATHJAX_ROOT - root of MathJax installation @@ -443,31 +442,11 @@ set(Boost_ADDITIONAL_VERSIONS # TODO for the brave: transition all mlpack's CMake to 'target-based modern # CMake'. Good luck! You'll need it. set(Boost_NO_BOOST_CMAKE 1) -find_package(Boost "${BOOST_VERSION}" - COMPONENTS - REQUIRED -) - -link_directories(${Boost_LIBRARY_DIRS}) - -# In Visual Studio, automatic linking is performed, so we don't need to worry -# about it. Clear the list of libraries to link against and let Visual Studio -# handle it. -if (MSVC) - link_directories(${Boost_LIBRARY_DIRS}) - set(CMAKE_MSVCIDE_RUN_PATH ${CMAKE_MSVCIDE_RUN_PATH} ${Boost_LIBRARY_DIRS}) - message("boost lib dirs ${Boost_LIBRARY_DIRS}") - set(Boost_LIBRARIES "") -endif () +find_package(Boost "${BOOST_VERSION}") set(MLPACK_INCLUDE_DIRS ${MLPACK_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}) -set(MLPACK_LIBRARIES ${MLPACK_LIBRARIES} ${Boost_LIBRARIES}) -set(MLPACK_LIBRARY_DIRS ${MLPACK_LIBRARY_DIRS} ${Boost_LIBRARY_DIRS}) - -# For Boost testing framework (will have no effect on non-testing executables). -# This specifies to Boost that we are dynamically linking to the Boost test -# library. -add_definitions(-DBOOST_TEST_DYN_LINK) +set(MLPACK_LIBRARIES ${MLPACK_LIBRARIES}) +set(MLPACK_LIBRARY_DIRS ${MLPACK_LIBRARY_DIRS}) # Detect OpenMP support in a compiler. If the compiler supports OpenMP, flags # to compile with OpenMP are returned and added and the HAS_OPENMP definition diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt index 8e4088dceb..32ca43b3c6 100644 --- a/COPYRIGHT.txt +++ b/COPYRIGHT.txt @@ -136,6 +136,8 @@ Copyright: Copyright 2020, Aakash Kaushik Copyright 2020, Anush Kini Copyright 2020, Nippun Sharma + Copyright 2020, Rishabh Garg + Copyright 2020, Sudhakar Brar License: BSD-3-clause All rights reserved. diff --git a/dist/win-installer/mlpack-win-installer/Product.wxs b/dist/win-installer/mlpack-win-installer/Product.wxs index 4adafc2a23..3cd7598a85 100644 --- a/dist/win-installer/mlpack-win-installer/Product.wxs +++ b/dist/win-installer/mlpack-win-installer/Product.wxs @@ -2,47 +2,38 @@ - - - + + - - - + + - - - - $(env.MLPACK_VERSION) - - - - - - + + + + + + + - - - - - - - - - - - - - - + + + + $(env.MLPACK_VERSION) + + + + + + diff --git a/dist/win-installer/mlpack-win-installer/mlpack-win-installer.wixproj b/dist/win-installer/mlpack-win-installer/mlpack-win-installer.wixproj index 8795a920eb..0e89ee825c 100644 --- a/dist/win-installer/mlpack-win-installer/mlpack-win-installer.wixproj +++ b/dist/win-installer/mlpack-win-installer/mlpack-win-installer.wixproj @@ -9,33 +9,36 @@ mlpack-windows Package mlpack-win-installer + false + SourceDir=.\Sources + $(MSBuildExtensionsPath)\Microsoft\WiX\v3.x\Wix.targets bin\$(Configuration)\ obj\$(Configuration)\ - Debug + Debug;$(DefineConstants) bin\$(Configuration)\ obj\$(Configuration)\ - Debug bin\$(Platform)\$(Configuration)\ obj\$(Platform)\$(Configuration)\ + Debug;$(DefineConstants) bin\$(Platform)\$(Configuration)\ obj\$(Platform)\$(Configuration)\ - - HarvestPath=..\staging - - - - + + Sources + Sources + var.SourceDir + true + $(WixExtDir)\WixUIExtension.dll WixUIExtension @@ -46,14 +49,4 @@ - - - - - - \ No newline at end of file + diff --git a/doc/guide/build.hpp b/doc/guide/build.hpp index 064d5ffd5b..91695d6ab6 100644 --- a/doc/guide/build.hpp +++ b/doc/guide/build.hpp @@ -2,10 +2,10 @@ @section build_buildintro Introduction -This document discusses how to build mlpack from source. These build directions +This document discusses how to build mlpack from source. These build directions will work for any Linux-like shell environment (for example Ubuntu, macOS, -FreeBSD etc). However, mlpack is in the repositories of many Linux distributions -and so it may be easier to use the package manager for your system. For example, +FreeBSD etc). However, mlpack is in the repositories of many Linux distributions +and so it may be easier to use the package manager for your system. For example, on Ubuntu, you can install mlpack with the following command: @code @@ -25,7 +25,7 @@ mlpack uses CMake as a build system and allows several flexible build configuration options. One can consult any of numerous CMake tutorials for further documentation, but this tutorial should be enough to get mlpack built and installed on most Linux and UNIX-like systems (including OS X). If you want -to build mlpack on Windows, see \ref build_windows (alternatively, you can read +to build mlpack on Windows, see \ref build_windows (alternatively, you can read Keon's excellent tutorial which is based on older versions). @@ -78,7 +78,7 @@ mlpack depends on the following libraries, which need to be installed on the system and have headers present: - Armadillo >= 8.400.0 (with LAPACK support) - - Boost (math_c99, unit_test_framework, heap, spirit) >= 1.58 + - Boost (math_c99, spirit) >= 1.58 - cereal >= 1.1.2 - ensmallen >= 2.10.0 (will be downloaded if not found) @@ -95,11 +95,11 @@ For Python bindings, the following packages are required: - pandas >= 0.15.0 - pytest-runner -In Ubuntu (>= 18.04) and Debian (>= 10) all of these dependencies can be +In Ubuntu (>= 18.04) and Debian (>= 10) all of these dependencies can be installed through apt: @code -# apt-get install libboost-math-dev libboost-test-dev libcereal-dev +# apt-get install libboost-math-dev libcereal-dev libarmadillo-dev binutils-dev python3-pandas python3-numpy cython3 python3-setuptools @endcode @@ -112,18 +112,18 @@ packages: # apt-get install libensmallen-dev libstb-dev @endcode -@note For older versions of Ubuntu and Debian, Armadillo needs to be built from -source as apt installs an older version. So you need to omit +@note For older versions of Ubuntu and Debian, Armadillo needs to be built from +source as apt installs an older version. So you need to omit \c libarmadillo-dev from the code snippet above and instead use this link - to download the required file. Extract this file and follow the README in the + to download the required file. Extract this file and follow the README in the uncompressed folder to build and install Armadillo. On Fedora, Red Hat, or CentOS, these same dependencies can be obtained via dnf: @code -# dnf install boost-devel boost-test boost-math armadillo-devel binutils-devel - python3-Cython python3-setuptools python3-numpy python3-pandas ensmallen-devel +# dnf install boost-devel boost-math armadillo-devel binutils-devel + python3-Cython python3-setuptools python3-numpy python3-pandas ensmallen-devel stbi-devel cereal-devel @endcode diff --git a/doc/guide/build_windows.hpp b/doc/guide/build_windows.hpp index 90fe93c0db..41eb911659 100644 --- a/doc/guide/build_windows.hpp +++ b/doc/guide/build_windows.hpp @@ -9,8 +9,13 @@ @section build_windows_intro Introduction -This tutorial will show you how to build mlpack for Windows from source, so you can -later create your own C++ applications. Before you try building mlpack, you may +This tutorial will show you how to build mlpack for Windows from source, so +you can later create your own C++ applications, using two different ways: + + - Using CMake to generate an intermeditate Visual Studio solution (`.sln`). + - @ref build_visual_studio_cmake_integration "Use Visual Studio's CMake integration to directly build from the `CMakeLists`." + +Before you try building mlpack, you may want to install mlpack using vcpkg for Windows. If you don't want to install using vcpkg, skip this section and continue with the build tutorial. @@ -78,6 +83,23 @@ system environment variables or manually set the PATH before running CMake) - Click on OpenBlas and check the mlpack project, then click Install - Once it has finished installing, close Visual Studio + Building OpenBLAS from Source + +Unfortunately, the support for building `LAPACK` and `BLAS` on Windows is quite poor, due to the need for Fortran +compiler and libraries. The easiest method to get the necessary `BLAS/LAPACK` libraries built on Windows is to +compile OpenBLAS with LLVM's `clang-cl` and `flang` to produce the required static library (`.lib`) files +compatible with the MSVC compiler. A comprehensive guide on the +compilation +of OpenBLAS for Windows can be found here. + +One could always download prebuilt `LAPACK` and `BLAS` libraries for Windows. However, there are few official +sources, and some of those libraries may require further `dll`s at runtime which may not be available in your +system. + +It you choose to build `OpenBLAS` from source, make sure that `LAPACK` functions are also built. Finally, make +sure that the `openblas.lib` library is linked in your `Armadillo` build (see below), as well as the library +path used for the CMake options `BLAS_LIBRARIES` and `LAPACK_LIBRARIES` in the mlpack CMake project. + Boost Dependency You can either get Boost via NuGet or you can download the prebuilt Windows binaries separately. @@ -110,7 +132,7 @@ compiler version, check if the Visual Studio compiler and Windows SDK are instal - Build > Build Solution - Once it has successfully finished, close Visual Studio -@section build_windows_mlpack Building mlpack +@section build_windows_mlpack Building mlpack with CMake-Generated Solution - Create a "build" directory into "C:\mlpack\mlpack\" - You can generate the project using either cmake via command line or GUI. If you prefer to use GUI, refer to the \ref build_windows_appendix "appendix" @@ -129,6 +151,96 @@ cmake -G "Visual Studio 16 2019" -A x64 -DBLAS_LIBRARIES:FILEPATH="C:/mlpack/mlp You are ready to create your first application, take a look at the @ref sample_ml_app "Sample C++ ML App" +@section build_visual_studio_cmake_integration Building mlpack with Visual Studio's CMake Integration + +This project can be directly built from the `CMakeLists.txt` with the latest version of MS Visual Studio, +given you have CMake integration via the +C++ +CMake tools for Windows. To open the CMake project with Visual Studio, select File->Open->CMake +in the top menu, followed by selecting the root `CMakeLists.txt` located in mlpack's root directory. + +In order to allow Visual Studio to configure the CMake project, the CMake configuration json will have +to be edited to provide the relevant options +shown in the `README` needed to find all the dependencies. The options that you +must provide to Visual Studio's CMake are: + + - `ARMADILLO_INCLUDE_DIR` + - `ARMADILLO_LIBRARY` + - `BOOST_ROOT` + - `CEREAL_INCLUDE_DIR` + - `BLAS_LIBRARIES` + - `LAPACK_LIBRARIES` + +The CMake configuration json can be editted in Visual Studio by right clicking the root `CMakeLists.txt` +in the project view, selecting CMake settings for mlpack and finally clicking on edit JSON. +Adding a new CMake option can be done by adding object fields with the following format to the variables +array in the `CMakeSettings.json`: + +@code +{ + "name": "options_name_string", + "value": "options_value_string", + "type" : "{BOOL|FILEPATH|PATH|STRING}" +} +@endcode + +Here is a full example of the `CMakeSettings.json`file: + +@code +{ + "configurations": [ + { + "name": "x64-Debug (default)", + "generator": "Ninja", + "configurationType": "Debug", + "inheritEnvironments": [ "msvc_x64_x64" ], + "buildRoot": "${projectDir}\\out\\build\\${name}", + "installRoot": "${projectDir}\\out\\install\\${name}", + "cmakeCommandArgs": "", + "buildCommandArgs": "", + "ctestCommandArgs": "", + "variables": [ + { + "name": "ARMADILLO_INCLUDE_DIR", + "value": "PATH/TO/CPP/DEPENDENCY/armadillo-10.1.2/include", + "type": "PATH" + }, + { + "name": "ARMADILLO_LIBBRARY", + "value": "PATH/TO/CPP/DEPENDENCY/armadillo-10.1.2/lib/armadillo.lib", + "type": "PATH" + }, + { + "name": "CEREAL_INCLUDE_DIR", + "value": "PATH/TO/CPP/DEPENDENCY/cereal-1.3.0/include", + "type": "PATH" + }, + { + "name": "BUILD_ROOT", + "value": "PATH/TO/CPP/DEPENDENCY/boost_1_66_0", + "type": "PATH" + }, + { + "name": "BOOST_INCLUDEDIR", + "value": "PATH/TO/CPP/DEPENDENCY/boost_1_66_0", + "type": "PATH" + }, + { + "name": "BLAS_LIBRARIES", + "value": "PATH/TO/CPP/DEPENDENCY/OpenBLAS/lib/openblas.lib", + "type": "PATH" + }, + { + "name": "LAPACK_LIBRARIES", + "value": "PATH/TO/CPP/DEPENDENCY/OpenBLAS/lib/openblas.lib", + "type": "PATH" + } + ] + } + ] +} +@endcode + @section build_windows_appendix Appendix If you prefer to use cmake GUI, follow these instructions: @@ -147,13 +259,6 @@ If you prefer to use cmake GUI, follow these instructions: following variables and reconfigure: - Name: `BOOST_INCLUDEDIR`; type `PATH`; value `C:/boost/` - Name: `BOOST_LIBRARYDIR`; type `PATH`; value `C:/boost/lib64-msvc-14.2` - - If Boost is still not found, try adding the following variables and - reconfigure: - - Name: `Boost_INCLUDE_DIR`; type `PATH`; value `C:/boost/` - - Name: `Boost_SERIALIZATION_LIBRARY_DEBUG`; type `FILEPATH`; value should be `C:/boost/lib64-msvc-14.2/boost_serialization-vc142-mt-gd-x64-1_71.lib` - - Name: `Boost_SERIALIZATION_LIBRARY_RELEASE`; type `FILEPATH`; value should be `C:/boost/lib64-msvc-14.2/boost_serialization-vc142-mt-x64-1_71.lib` - - Name: `Boost_UNIT_TEST_FRAMEWORK_LIBRARY_DEBUG`; type `FILEPATH`; value should be `C:/boost/lib64-msvc-14.2/boost_unit_test_framework-vc142-mt-gd-x64-1_71.lib` - - Name: `Boost_UNIT_TEST_FRAMEWORK_LIBRARY_RELEASE`; type `FILEPATH`; value should be `C:/boost/lib64-msvc-14.2/boost_unit_test_framework-vc142-mt-x64-1_71.lib` - Once CMake has configured successfully, hit "Generate" to create the `.sln` file. @section build_windows_additional_information Additional Information diff --git a/doc/guide/sample_ml_app.hpp b/doc/guide/sample_ml_app.hpp index 72a7253d5a..b8282a9ade 100644 --- a/doc/guide/sample_ml_app.hpp +++ b/doc/guide/sample_ml_app.hpp @@ -34,7 +34,6 @@ mlpack and dependencies in Release Mode). - Under Linker > Input > Additional Dependencies add: @code - C:\mlpack\mlpack-3.4.2\build\Debug\mlpack.lib - - C:\boost\boost_1_71_0\lib64-msvc-14.2\libboost_serialization-vc142-mt-gd-x64-1_71.lib @endcode - Under Build Events > Post-Build Event > Command Line add: @code diff --git a/src/mlpack/bindings/cli/CMakeLists.txt b/src/mlpack/bindings/cli/CMakeLists.txt index 1083ec41f2..4b94805fe5 100644 --- a/src/mlpack/bindings/cli/CMakeLists.txt +++ b/src/mlpack/bindings/cli/CMakeLists.txt @@ -53,7 +53,6 @@ if (BUILD_CLI_EXECUTABLES) target_link_libraries(mlpack_${name} mlpack ${ARMADILLO_LIBRARIES} - ${Boost_LIBRARIES} ${COMPILER_SUPPORT_LIBRARIES} ) # Make sure that we set BINDING_TYPE to cli so the command-line program is diff --git a/src/mlpack/bindings/python/CMakeLists.txt b/src/mlpack/bindings/python/CMakeLists.txt index 180014ed3c..65490997c3 100644 --- a/src/mlpack/bindings/python/CMakeLists.txt +++ b/src/mlpack/bindings/python/CMakeLists.txt @@ -18,7 +18,6 @@ macro (post_python_bindings) -D GENERATE_CPP_IN=${CMAKE_SOURCE_DIR}/src/mlpack/bindings/python/setup.py.in -D GENERATE_CPP_OUT=${CMAKE_BINARY_DIR}/src/mlpack/bindings/python/setup.py -D PACKAGE_VERSION="${PACKAGE_VERSION}" - -D Boost_LIBRARY_DIRS="${Boost_LIBRARY_DIRS}" -D ARMADILLO_LIBRARIES="${ARMADILLO_LIBRARIES}" -D MLPACK_LIBRARY=$ -D MLPACK_LIBDIR=$ @@ -240,14 +239,6 @@ if (WIN32) foreach (dll ${DLL_COPY_LIBS}) file(COPY ${dll} DESTINATION ${CMAKE_BINARY_DIR}/src/mlpack/bindings/python/mlpack/) endforeach () - - # We also need to copy the boost DLLs over. - file(GLOB boost_ser_dll_files "${Boost_LIBRARY_DIRS}/*serialization*.dll") - file(COPY ${boost_ser_dll_files} DESTINATION ${CMAKE_BINARY_DIR}/src/mlpack/bindings/python/mlpack/) - file(GLOB boost_po_dll_files "${Boost_LIBRARY_DIRS}/*program*options*.dll") - file(COPY ${boost_po_dll_files} DESTINATION ${CMAKE_BINARY_DIR}/src/mlpack/bindings/python/mlpack/) - file(GLOB boost_utf_dll_files "${Boost_LIBRARY_DIRS}/*unit*test*framework*.dll") - file(COPY ${boost_utf_dll_files} DESTINATION ${CMAKE_BINARY_DIR}/src/mlpack/bindings/python/mlpack/) endif () # Add a macro to build a python binding. diff --git a/src/mlpack/bindings/python/setup.py.in b/src/mlpack/bindings/python/setup.py.in index a69432f539..4762f3194b 100644 --- a/src/mlpack/bindings/python/setup.py.in +++ b/src/mlpack/bindings/python/setup.py.in @@ -34,8 +34,7 @@ else: # directories with a (valid) space in the name will be given to us as '\ '; so, # in order to split these right, we first convert all spaces to ';', then # convert '\;' back to ' ', then split on ';'. -library_dirs = list(filter(None, ['${MLPACK_LIBDIR}'] + - '${Boost_LIBRARY_DIRS}'.replace(' ', ';').replace('\;', ' ').split(' '))) +library_dirs = ['${MLPACK_LIBDIR}'] # We'll link with the exact paths to each library using extra_objects, instead # of linking with 'libraries' and 'library_dirs', because of differences in diff --git a/src/mlpack/core/data/image_info_impl.hpp b/src/mlpack/core/data/image_info_impl.hpp index b0257c5d89..3040a38415 100644 --- a/src/mlpack/core/data/image_info_impl.hpp +++ b/src/mlpack/core/data/image_info_impl.hpp @@ -1,77 +1,77 @@ -/** +/** * @file core/data/image_info_impl.hpp - * @author Mehul Kumar Nirala - * - * An image information holder implementation. - * - * mlpack is free software; you may redistribute it and/or modify it under the - * terms of the 3-clause BSD license. You should have received a copy of the - * 3-clause BSD license along with mlpack. If not, see - * http://www.opensource.org/licenses/BSD-3-Clause for more information. - */ - -#ifndef MLPACK_CORE_DATA_IMAGE_INFO_IMPL_HPP -#define MLPACK_CORE_DATA_IMAGE_INFO_IMPL_HPP - -#ifdef HAS_STB // Compile this only if stb is present. - -// In case it hasn't been included yet. -#include "image_info.hpp" - -namespace mlpack { -namespace data { - -static const std::vector loadFileTypes({"jpg", "png", "tga", - "bmp", "psd", "gif", "hdr", "pic", "pnm", "jpeg"}); - -static const std::vector saveFileTypes({"jpg", "png", "tga", - "bmp", "hdr"}); - -inline bool ImageFormatSupported(const std::string& fileName, const bool save) -{ - if (save) - { - // Iterate over all supported file types that can be saved. - for (auto extension : saveFileTypes) - { - if (extension == Extension(fileName)) - return true; - } - } - else - { - // Iterate over all supported file types that can be loaded. - for (auto extension : loadFileTypes) - { - if (extension == Extension(fileName)) - return true; - } - } - - return false; -} - -} // namespace data -} // namespace mlpack - -#endif // HAS_STB. - -namespace mlpack { -namespace data { - -inline ImageInfo::ImageInfo(const size_t width, - const size_t height, - const size_t channels, - const size_t quality) : - width(width), - height(height), - channels(channels), - quality(quality) -{ - // Do nothing. -} - -} // namespace data -} // namespace mlpack - -#endif + * @author Mehul Kumar Nirala + * + * An image information holder implementation. + * + * mlpack is free software; you may redistribute it and/or modify it under the + * terms of the 3-clause BSD license. You should have received a copy of the + * 3-clause BSD license along with mlpack. If not, see + * http://www.opensource.org/licenses/BSD-3-Clause for more information. + */ + +#ifndef MLPACK_CORE_DATA_IMAGE_INFO_IMPL_HPP +#define MLPACK_CORE_DATA_IMAGE_INFO_IMPL_HPP + +#ifdef HAS_STB // Compile this only if stb is present. + +// In case it hasn't been included yet. +#include "image_info.hpp" + +namespace mlpack { +namespace data { + +static const std::vector loadFileTypes({"jpg", "png", "tga", + "bmp", "psd", "gif", "hdr", "pic", "pnm", "jpeg"}); + +static const std::vector saveFileTypes({"jpg", "png", "tga", + "bmp", "hdr"}); + +inline bool ImageFormatSupported(const std::string& fileName, const bool save) +{ + if (save) + { + // Iterate over all supported file types that can be saved. + for (auto extension : saveFileTypes) + { + if (extension == Extension(fileName)) + return true; + } + } + else + { + // Iterate over all supported file types that can be loaded. + for (auto extension : loadFileTypes) + { + if (extension == Extension(fileName)) + return true; + } + } + + return false; +} + +} // namespace data +} // namespace mlpack + +#endif // HAS_STB. + +namespace mlpack { +namespace data { + +inline ImageInfo::ImageInfo(const size_t width, + const size_t height, + const size_t channels, + const size_t quality) : + width(width), + height(height), + channels(channels), + quality(quality) +{ + // Do nothing. +} + +} // namespace data +} // namespace mlpack + +#endif diff --git a/src/mlpack/core/data/load_image_impl.hpp b/src/mlpack/core/data/load_image_impl.hpp index 8cd9b9a2ef..9a757838b9 100644 --- a/src/mlpack/core/data/load_image_impl.hpp +++ b/src/mlpack/core/data/load_image_impl.hpp @@ -1,96 +1,96 @@ -/** +/** * @file core/data/load_image_impl.hpp - * @author Mehul Kumar Nirala - * - * An image loading utility implementation. - * - * mlpack is free software; you may redistribute it and/or modify it under the - * terms of the 3-clause BSD license. You should have received a copy of the - * 3-clause BSD license along with mlpack. If not, see - * http://www.opensource.org/licenses/BSD-3-Clause for more information. - */ - -#ifndef MLPACK_CORE_DATA_LOAD_IMAGE_IMPL_HPP -#define MLPACK_CORE_DATA_LOAD_IMAGE_IMPL_HPP - -// In case it hasn't been included yet. -#include "load.hpp" - -namespace mlpack { -namespace data { - -// Image loading API. -template -bool Load(const std::string& filename, - arma::Mat& matrix, - ImageInfo& info, - const bool fatal) -{ - Timer::Start("loading_image"); - - // STB loads into unsigned char matrices, so we may have to convert once - // loaded. - arma::Mat tempMatrix; - const bool result = LoadImage(filename, tempMatrix, info, fatal); - - // If fatal is true, then the program will have already thrown an exception. - if (!result) - { - Timer::Stop("loading_image"); - return false; - } - - matrix = arma::conv_to>::from(tempMatrix); - Timer::Stop("loading_image"); - return true; -} - -// Image loading API for multiple files. -template -bool Load(const std::vector& files, - arma::Mat& matrix, - ImageInfo& info, - const bool fatal) -{ - if (files.size() == 0) - { - std::ostringstream oss; - oss << "Load(): vector of image files is empty." << std::endl; - - if (fatal) - Log::Fatal << oss.str(); - else - Log::Warn << oss.str(); - - return false; - } - - arma::Mat img; - bool status = LoadImage(files[0], img, info, fatal); - - if (!status) - return false; - - // Decide matrix dimension using the image height and width. - arma::Mat tmpMatrix( - info.Width() * info.Height() * info.Channels(), files.size()); - tmpMatrix.col(0) = img; - - for (size_t i = 1; i < files.size() ; ++i) - { - arma::Mat colImg(tmpMatrix.colptr(i), tmpMatrix.n_rows, 1, - false, true); - status = LoadImage(files[i], colImg, info, fatal); - - if (!status) - return false; - } - - matrix = arma::conv_to>::from(tmpMatrix); - return true; -} - -} // namespace data -} // namespace mlpack - -#endif + * @author Mehul Kumar Nirala + * + * An image loading utility implementation. + * + * mlpack is free software; you may redistribute it and/or modify it under the + * terms of the 3-clause BSD license. You should have received a copy of the + * 3-clause BSD license along with mlpack. If not, see + * http://www.opensource.org/licenses/BSD-3-Clause for more information. + */ + +#ifndef MLPACK_CORE_DATA_LOAD_IMAGE_IMPL_HPP +#define MLPACK_CORE_DATA_LOAD_IMAGE_IMPL_HPP + +// In case it hasn't been included yet. +#include "load.hpp" + +namespace mlpack { +namespace data { + +// Image loading API. +template +bool Load(const std::string& filename, + arma::Mat& matrix, + ImageInfo& info, + const bool fatal) +{ + Timer::Start("loading_image"); + + // STB loads into unsigned char matrices, so we may have to convert once + // loaded. + arma::Mat tempMatrix; + const bool result = LoadImage(filename, tempMatrix, info, fatal); + + // If fatal is true, then the program will have already thrown an exception. + if (!result) + { + Timer::Stop("loading_image"); + return false; + } + + matrix = arma::conv_to>::from(tempMatrix); + Timer::Stop("loading_image"); + return true; +} + +// Image loading API for multiple files. +template +bool Load(const std::vector& files, + arma::Mat& matrix, + ImageInfo& info, + const bool fatal) +{ + if (files.size() == 0) + { + std::ostringstream oss; + oss << "Load(): vector of image files is empty." << std::endl; + + if (fatal) + Log::Fatal << oss.str(); + else + Log::Warn << oss.str(); + + return false; + } + + arma::Mat img; + bool status = LoadImage(files[0], img, info, fatal); + + if (!status) + return false; + + // Decide matrix dimension using the image height and width. + arma::Mat tmpMatrix( + info.Width() * info.Height() * info.Channels(), files.size()); + tmpMatrix.col(0) = img; + + for (size_t i = 1; i < files.size() ; ++i) + { + arma::Mat colImg(tmpMatrix.colptr(i), tmpMatrix.n_rows, 1, + false, true); + status = LoadImage(files[i], colImg, info, fatal); + + if (!status) + return false; + } + + matrix = arma::conv_to>::from(tmpMatrix); + return true; +} + +} // namespace data +} // namespace mlpack + +#endif diff --git a/src/mlpack/methods/ann/layer/concatenate.hpp b/src/mlpack/methods/ann/layer/concatenate.hpp index b27d92afee..561ecf2595 100644 --- a/src/mlpack/methods/ann/layer/concatenate.hpp +++ b/src/mlpack/methods/ann/layer/concatenate.hpp @@ -41,6 +41,18 @@ class Concatenate */ Concatenate(); + //! Copy constructor. + Concatenate(const Concatenate& layer); + + //! Move constructor. + Concatenate(Concatenate&& layer); + + //! Operator= copy constructor. + Concatenate& operator=(const Concatenate& layer); + + //! Operator= move constructor. + Concatenate& operator=(Concatenate&& layer); + /** * Ordinary feed forward pass of a neural network, evaluating the function * f(x) by propagating the activity forward through f. @@ -82,7 +94,7 @@ class Concatenate //! Get the concat matrix. OutputDataType const& Concat() const { return concat; } - //! Modify the delta. + //! Modify the concat. OutputDataType& Concat() { return concat; } /** diff --git a/src/mlpack/methods/ann/layer/concatenate_impl.hpp b/src/mlpack/methods/ann/layer/concatenate_impl.hpp index 20c7ba6d15..bfede6c162 100644 --- a/src/mlpack/methods/ann/layer/concatenate_impl.hpp +++ b/src/mlpack/methods/ann/layer/concatenate_impl.hpp @@ -20,11 +20,63 @@ namespace mlpack { namespace ann /** Artificial Neural Network. */ { template -Concatenate::Concatenate() +Concatenate::Concatenate() : + inRows(0) { // Nothing to do here. } +template +Concatenate::Concatenate(const Concatenate& layer) : + inRows(layer.inRows), + weights(layer.weights), + delta(layer.delta), + concat(layer.concat) +{ + // Nothing to to here. +} + +template +Concatenate::Concatenate(Concatenate&& layer) : + inRows(layer.inRows), + weights(std::move(layer.weights)), + delta(std::move(layer.delta)), + concat(std::move(layer.concat)) +{ + // Nothing to do here. +} + +template +Concatenate& +Concatenate:: +operator=(const Concatenate& layer) +{ + if (this != &layer) + { + inRows = layer.inRows; + weights = layer.weights; + delta = layer.delta; + concat = layer.concat; + } + + return *this; +} + +template +Concatenate& +Concatenate:: +operator=(Concatenate&& layer) +{ + if (this != &layer) + { + inRows = layer.inRows; + weights = std::move(layer.weights); + delta = std::move(layer.delta); + concat = std::move(layer.concat); + } + return *this; +} + template template void Concatenate::Forward( diff --git a/src/mlpack/methods/ann/layer/fast_lstm.hpp b/src/mlpack/methods/ann/layer/fast_lstm.hpp index bcf1a1c201..80ddcabca6 100644 --- a/src/mlpack/methods/ann/layer/fast_lstm.hpp +++ b/src/mlpack/methods/ann/layer/fast_lstm.hpp @@ -73,6 +73,18 @@ class FastLSTM //! Create the Fast LSTM object. FastLSTM(); + //! Copy Constructor + FastLSTM(const FastLSTM& layer); + + //! Move Constructor + FastLSTM(FastLSTM&& layer); + + //! Copy assignment operator + FastLSTM& operator=(const FastLSTM& layer); + + //! Move assignment operator + FastLSTM& operator=(FastLSTM&& layer); + /** * Create the Fast LSTM layer object using the specified parameters. * diff --git a/src/mlpack/methods/ann/layer/fast_lstm_impl.hpp b/src/mlpack/methods/ann/layer/fast_lstm_impl.hpp index 5f5502cf9a..752b132ae4 100644 --- a/src/mlpack/methods/ann/layer/fast_lstm_impl.hpp +++ b/src/mlpack/methods/ann/layer/fast_lstm_impl.hpp @@ -45,6 +45,90 @@ FastLSTM::FastLSTM( weights.set_size(WeightSize(), 1); } +template +FastLSTM::FastLSTM(const FastLSTM& layer) : + inSize(layer.inSize), + outSize(layer.outSize), + rho(layer.rho), + forwardStep(layer.forwardStep), + backwardStep(layer.backwardStep), + gradientStep(layer.gradientStep), + weights(layer.weights), + batchSize(layer.batchSize), + batchStep(layer.batchStep), + gradientStepIdx(layer.gradientStepIdx), + grad(layer.grad), + rhoSize(layer.rho), + bpttSteps(layer.bpttSteps) +{ + // Nothing to do here. +} + +template +FastLSTM::FastLSTM(FastLSTM&& layer) : + inSize(std::move(layer.inSize)), + outSize(std::move(layer.outSize)), + rho(std::move(layer.rho)), + forwardStep(std::move(layer.forwardStep)), + backwardStep(std::move(layer.backwardStep)), + gradientStep(std::move(layer.gradientStep)), + weights(std::move(layer.weights)), + batchSize(std::move(layer.batchSize)), + batchStep(std::move(layer.batchStep)), + gradientStepIdx(std::move(layer.gradientStepIdx)), + grad(std::move(layer.grad)), + rhoSize(std::move(layer.rho)), + bpttSteps(std::move(layer.bpttSteps)) +{ + // Nothing to do here. +} + +template +FastLSTM& +FastLSTM::operator=(const FastLSTM& layer) +{ + if (this != &layer) + { + inSize = layer.inSize; + outSize = layer.outSize; + rho = layer.rho; + forwardStep = layer.forwardStep; + backwardStep = layer.backwardStep; + gradientStep = layer.gradientStep; + weights = layer.weights; + batchSize = layer.batchSize; + batchStep = layer.batchStep; + gradientStepIdx = layer.gradientStepIdx; + grad = layer.grad; + rhoSize = layer.rho; + bpttSteps = layer.bpttSteps; + } + return *this; +} + +template +FastLSTM& +FastLSTM::operator=(FastLSTM&& layer) +{ + if (this != &layer) + { + inSize = std::move(layer.inSize); + outSize = std::move(layer.outSize); + rho = std::move(layer.rho); + forwardStep = std::move(layer.forwardStep); + backwardStep = std::move(layer.backwardStep); + gradientStep = std::move(layer.gradientStep); + weights = std::move(layer.weights); + batchSize = std::move(layer.batchSize); + batchStep = std::move(layer.batchStep); + gradientStepIdx = std::move(layer.gradientStepIdx); + grad = std::move(layer.grad); + rhoSize = std::move(layer.rho); + bpttSteps = std::move(layer.bpttSteps); + } + return *this; +} + template void FastLSTM::Reset() { diff --git a/src/mlpack/methods/ann/layer/linear3d.hpp b/src/mlpack/methods/ann/layer/linear3d.hpp index 7bd03e8176..24e3de56e6 100644 --- a/src/mlpack/methods/ann/layer/linear3d.hpp +++ b/src/mlpack/methods/ann/layer/linear3d.hpp @@ -54,6 +54,18 @@ class Linear3D const size_t outSize, RegularizerType regularizer = RegularizerType()); + //! Copy constructor. + Linear3D(const Linear3D& layer); + + //! Move constructor. + Linear3D(Linear3D&&); + + //! Copy assignment operator. + Linear3D& operator=(const Linear3D& layer); + + //! Move assignment operator. + Linear3D& operator=(Linear3D&& layer); + /* * Reset the layer parameter. */ diff --git a/src/mlpack/methods/ann/layer/linear3d_impl.hpp b/src/mlpack/methods/ann/layer/linear3d_impl.hpp index 5aced4493b..37cc9e39e5 100644 --- a/src/mlpack/methods/ann/layer/linear3d_impl.hpp +++ b/src/mlpack/methods/ann/layer/linear3d_impl.hpp @@ -40,6 +40,62 @@ Linear3D::Linear3D( weights.set_size(outSize * inSize + outSize, 1); } +template +Linear3D::Linear3D( + const Linear3D& layer) : + inSize(layer.inSize), + outSize(layer.outSize), + weights(layer.weights), + regularizer(layer.regularizer) +{ + // Nothing to do here. +} + +template +Linear3D::Linear3D( + Linear3D&& layer) : + inSize(0), + outSize(0), + weights(std::move(layer.weights)), + regularizer(std::move(layer.regularizer)) +{ + // Nothing to do here. +} + +template +Linear3D& +Linear3D:: +operator=(const Linear3D& layer) +{ + if (this != &layer) + { + inSize = layer.inSize; + outSize = layer.outSize; + weights = layer.weights; + regularizer = layer.regularizer; + } + return *this; +} + +template +Linear3D& +Linear3D:: +operator=(Linear3D&& layer) +{ + if (this != &layer) + { + inSize = 0; + outSize = 0; + weights = std::move(layer.weights); + regularizer = std::move(layer.regularizer); + } + return *this; +} + template void Linear3D::Reset() diff --git a/src/mlpack/methods/ann/layer/noisylinear.hpp b/src/mlpack/methods/ann/layer/noisylinear.hpp index af10496d61..2812a9dfc3 100644 --- a/src/mlpack/methods/ann/layer/noisylinear.hpp +++ b/src/mlpack/methods/ann/layer/noisylinear.hpp @@ -48,6 +48,15 @@ class NoisyLinear //! Copy constructor. NoisyLinear(const NoisyLinear&); + //! Move constructor. + NoisyLinear(NoisyLinear&&); + + //! Operator= copy constructor. + NoisyLinear& operator=(const NoisyLinear& layer); + + //! Operator= move constructor. + NoisyLinear& operator=(NoisyLinear&& layer); + /* * Reset the layer parameter. */ diff --git a/src/mlpack/methods/ann/layer/noisylinear_impl.hpp b/src/mlpack/methods/ann/layer/noisylinear_impl.hpp index 82a3a35fc6..f1ae97ffd6 100644 --- a/src/mlpack/methods/ann/layer/noisylinear_impl.hpp +++ b/src/mlpack/methods/ann/layer/noisylinear_impl.hpp @@ -48,6 +48,50 @@ NoisyLinear::NoisyLinear( biasEpsilon.set_size(outSize, 1); } +template +NoisyLinear::NoisyLinear( + NoisyLinear&& layer) : + inSize(std::move(layer.inSize)), + outSize(std::move(layer.outSize)), + weights(std::move(layer.weights)) +{ + layer.inSize = 0; + layer.outSize = 0; + layer.weights = nullptr; + Reset(); +} + +template +NoisyLinear& +NoisyLinear::operator=(const NoisyLinear& layer) +{ + if (this != &layer) + { + inSize = layer.inSize; + outSize = layer.outSize; + weights = layer.weights; + Reset(); + } + return *this; +} + +template +NoisyLinear& +NoisyLinear::operator=(NoisyLinear&& layer) +{ + if (this != &layer) + { + inSize = std::move(layer.inSize); + layer.inSize = 0; + outSize = std::move(layer.outSize); + layer.outSize = 0; + weights = std::move(layer.weights); + layer.weights = nullptr; + Reset(); + } + return *this; +} + template void NoisyLinear::Reset() { diff --git a/src/mlpack/methods/ann/layer/recurrent.hpp b/src/mlpack/methods/ann/layer/recurrent.hpp index c6e396ddad..b82fcb175b 100644 --- a/src/mlpack/methods/ann/layer/recurrent.hpp +++ b/src/mlpack/methods/ann/layer/recurrent.hpp @@ -2,8 +2,7 @@ * @file methods/ann/layer/recurrent.hpp * @author Marcus Edel * - * Definition of the LinearLayer class also known as fully-connected layer or - * affine transformation. + * Definition of the Recurrent class. * * mlpack is free software; you may redistribute it and/or modify it under the * terms of the 3-clause BSD license. You should have received a copy of the diff --git a/src/mlpack/methods/ann/layer/recurrent_impl.hpp b/src/mlpack/methods/ann/layer/recurrent_impl.hpp index e37df89b69..975103df12 100644 --- a/src/mlpack/methods/ann/layer/recurrent_impl.hpp +++ b/src/mlpack/methods/ann/layer/recurrent_impl.hpp @@ -2,8 +2,7 @@ * @file methods/ann/layer/recurrent_impl.hpp * @author Marcus Edel * - * Implementation of the LinearLayer class also known as fully-connected layer - * or affine transformation. + * Implementation of the Recurrent class. * * mlpack is free software; you may redistribute it and/or modify it under the * terms of the 3-clause BSD license. You should have received a copy of the diff --git a/src/mlpack/methods/ann/layer/spatial_dropout_impl.hpp b/src/mlpack/methods/ann/layer/spatial_dropout_impl.hpp index 79114d2471..4bd2cb767b 100644 --- a/src/mlpack/methods/ann/layer/spatial_dropout_impl.hpp +++ b/src/mlpack/methods/ann/layer/spatial_dropout_impl.hpp @@ -51,6 +51,9 @@ template void SpatialDropout::Forward( const arma::Mat& input, arma::Mat& output) { + Log::Assert(input.n_rows % size == 0, "Input features must be divisible \ + by feature maps."); + if (!reset) { batchSize = input.n_cols; diff --git a/src/mlpack/methods/ann/layer/virtual_batch_norm_impl.hpp b/src/mlpack/methods/ann/layer/virtual_batch_norm_impl.hpp index 2b93960b82..7bd20415a2 100644 --- a/src/mlpack/methods/ann/layer/virtual_batch_norm_impl.hpp +++ b/src/mlpack/methods/ann/layer/virtual_batch_norm_impl.hpp @@ -67,6 +67,9 @@ template void VirtualBatchNorm::Forward( const arma::Mat& input, arma::Mat& output) { + Log::Assert(input.n_rows % size == 0, "Input features must be divisible \ + by feature maps."); + inputParameter = input; arma::mat inputMean = arma::mean(input, 1); arma::mat inputMeanSquared = arma::mean(arma::square(input), 1); diff --git a/src/mlpack/methods/ann/loss_functions/mean_squared_error.hpp b/src/mlpack/methods/ann/loss_functions/mean_squared_error.hpp index 3c36b33611..0cc3f6378d 100644 --- a/src/mlpack/methods/ann/loss_functions/mean_squared_error.hpp +++ b/src/mlpack/methods/ann/loss_functions/mean_squared_error.hpp @@ -42,24 +42,26 @@ class MeanSquaredError /** * Computes the mean squared error function. * - * @param input Input data used for evaluating the specified function. + * @param prediction Predictions used for evaluating the specified loss + * function. * @param target The target vector. */ - template - typename InputType::elem_type Forward(const InputType& input, - const TargetType& target); + template + typename PredictionType::elem_type Forward(const PredictionType& prediction, + const TargetType& target); /** * Ordinary feed backward pass of a neural network. * - * @param input The propagated input activation. + * @param prediction Predictions used for evaluating the specified loss + * function * @param target The target vector. - * @param output The calculated error. + * @param loss The calculated error. */ - template - void Backward(const InputType& input, + template + void Backward(const PredictionType& prediction, const TargetType& target, - OutputType& output); + LossType& loss); //! Get the output parameter. OutputDataType& OutputParameter() const { return outputParameter; } diff --git a/src/mlpack/methods/ann/loss_functions/mean_squared_error_impl.hpp b/src/mlpack/methods/ann/loss_functions/mean_squared_error_impl.hpp index 85cdbb3b45..ee4ae8c021 100644 --- a/src/mlpack/methods/ann/loss_functions/mean_squared_error_impl.hpp +++ b/src/mlpack/methods/ann/loss_functions/mean_squared_error_impl.hpp @@ -25,23 +25,23 @@ MeanSquaredError::MeanSquaredError() } template -template -typename InputType::elem_type +template +typename PredictionType::elem_type MeanSquaredError::Forward( - const InputType& input, + const PredictionType& prediction, const TargetType& target) { - return arma::accu(arma::square(input - target)) / target.n_cols; + return arma::accu(arma::square(prediction - target)) / target.n_cols; } template -template +template void MeanSquaredError::Backward( - const InputType& input, + const PredictionType& prediction, const TargetType& target, - OutputType& output) + LossType& loss) { - output = 2 * (input - target) / target.n_cols; + loss = 2 * (prediction - target) / target.n_cols; } template diff --git a/src/mlpack/methods/ann/rnn.hpp b/src/mlpack/methods/ann/rnn.hpp index e9e6815de4..949aecee9b 100644 --- a/src/mlpack/methods/ann/rnn.hpp +++ b/src/mlpack/methods/ann/rnn.hpp @@ -70,6 +70,18 @@ class RNN OutputLayerType outputLayer = OutputLayerType(), InitializationRuleType initializeRule = InitializationRuleType()); + //! Copy constructor. + RNN(const RNN&); + + //! Move constructor. + RNN(RNN&&); + + //! Copy assignment operator. + RNN& operator=(const RNN&); + + //! Move assignment operator + RNN& operator=(RNN&&); + //! Destructor to release allocated memory. ~RNN(); @@ -412,6 +424,9 @@ class RNN //! Locally-stored weight size visitor. WeightSizeVisitor weightSizeVisitor; + //! Locally-stored copy visitor + CopyVisitor copyVisitor; + //! Locally-stored reset visitor. ResetVisitor resetVisitor; diff --git a/src/mlpack/methods/ann/rnn_impl.hpp b/src/mlpack/methods/ann/rnn_impl.hpp index 365056a739..2852dcceef 100644 --- a/src/mlpack/methods/ann/rnn_impl.hpp +++ b/src/mlpack/methods/ann/rnn_impl.hpp @@ -51,6 +51,50 @@ RNN::RNN( /* Nothing to do here */ } +template +RNN::RNN( + const RNN& network) : + rho(network.rho), + outputLayer(network.outputLayer), + initializeRule(network.initializeRule), + inputSize(network.inputSize), + outputSize(network.outputSize), + targetSize(network.targetSize), + reset(network.reset), + single(network.single), + parameter(network.parameter), + numFunctions(network.numFunctions), + deterministic(network.deterministic) +{ + for (size_t i = 0; i < network.network.size(); ++i) + { + this->network.push_back(boost::apply_visitor(copyVisitor, + network.network[i])); + boost::apply_visitor(resetVisitor, this->network.back()); + } +} + +template +RNN::RNN( + RNN&& network) : + rho(std::move(network.rho)), + outputLayer(std::move(network.outputLayer)), + initializeRule(std::move(network.initializeRule)), + inputSize(std::move(network.inputSize)), + outputSize(std::move(network.outputSize)), + targetSize(std::move(network.targetSize)), + reset(std::move(network.reset)), + single(std::move(network.single)), + parameter(std::move(network.parameter)), + numFunctions(std::move(network.numFunctions)), + deterministic(std::move(network.deterministic)), + network(std::move(network.network)) +{ + // Nothing to do here. +} + template RNN::~RNN() diff --git a/src/mlpack/tests/CMakeLists.txt b/src/mlpack/tests/CMakeLists.txt index 48d05959cf..3b7ebb4b05 100644 --- a/src/mlpack/tests/CMakeLists.txt +++ b/src/mlpack/tests/CMakeLists.txt @@ -179,7 +179,6 @@ add_executable(mlpack_test target_link_libraries(mlpack_test mlpack ${ARMADILLO_LIBRARIES} - ${BOOST_LIBRARIES} ${COMPILER_SUPPORT_LIBRARIES} ) diff --git a/src/mlpack/tests/ann_layer_test.cpp b/src/mlpack/tests/ann_layer_test.cpp index 63da44b2ee..3950c1a2fd 100644 --- a/src/mlpack/tests/ann_layer_test.cpp +++ b/src/mlpack/tests/ann_layer_test.cpp @@ -31,6 +31,52 @@ using namespace mlpack; using namespace mlpack::ann; +// network1 should be allocated with `new`, and trained on some data. +template +void CheckRNNCopyFunction(ModelType* network1, + MatType& trainData, + MatType& trainLabels, + const size_t maxEpochs) +{ + arma::cube predictions1; + arma::cube predictions2; + ens::StandardSGD opt(0.1, 1, maxEpochs * trainData.n_slices, -100, false); + + network1->Train(trainData, trainLabels, opt); + network1->Predict(trainData, predictions1); + + RNN<> network2 = *network1; + delete network1; + + // Deallocating all of network1's memory, so that network2 does not use any + // of that memory. + network2.Predict(trainData, predictions2); + CheckMatrices(predictions1, predictions2); +} + +// network1 should be allocated with `new`, and trained on some data. +template +void CheckRNNMoveFunction(ModelType* network1, + MatType& trainData, + MatType& trainLabels, + const size_t maxEpochs) +{ + arma::cube predictions1; + arma::cube predictions2; + ens::StandardSGD opt(0.1, 1, maxEpochs * trainData.n_slices, -100, false); + + network1->Train(trainData, trainLabels, opt); + network1->Predict(trainData, predictions1); + + RNN<> network2(std::move(*network1)); + delete network1; + + // Deallocating all of network1's memory, so that network2 does not use any + // of that memory. + network2.Predict(trainData, predictions2); + CheckMatrices(predictions1, predictions2); +} + /** * Simple add module test. */ @@ -1183,6 +1229,40 @@ TEST_CASE("FastLSTMLayerParametersTest", "[ANNLayerTest]") REQUIRE(layer1.Rho() == layer2.Rho()); } +/** + * Check whether copying and moving network with FastLSTM is working or not. + */ +TEST_CASE("CheckCopyMoveFastLSTMTest", "[ANNLayerTest]") +{ + arma::cube input = arma::randu(1, 1, 5); + arma::cube target = arma::ones(1, 1, 5); + const size_t rho = 5; + + RNN > *model1 = + new RNN >(rho); + model1->Predictors() = input; + model1->Responses() = target; + model1->Add >(); + model1->Add >(1, 10); + model1->Add >(10, 3, rho); + model1->Add >(); + + RNN > *model2 = + new RNN >(rho); + model2->Predictors() = input; + model2->Responses() = target; + model2->Add >(); + model2->Add >(1, 10); + model2->Add >(10, 3, rho); + model2->Add >(); + + // Check whether copy constructor is working or not. + CheckRNNCopyFunction<>(model1, input, target, 1); + + // Check whether move constructor is working or not. + CheckRNNMoveFunction<>(model2, input, target, 1); +} + /** * Testing the overloaded Forward() of the LSTM layer, for retrieving the cell * state. Besides output, the overloaded function provides read access to cell diff --git a/src/mlpack/tests/callback_test.cpp b/src/mlpack/tests/callback_test.cpp index e66fc1e051..0d532df67c 100644 --- a/src/mlpack/tests/callback_test.cpp +++ b/src/mlpack/tests/callback_test.cpp @@ -42,9 +42,9 @@ TEST_CASE("FFNCallbackTest", "[CallbackTest]") arma::mat data; arma::mat labels; - if (!data::Load("lab1.csv", data, true)) + if (!data::Load("lab1.csv", data)) FAIL("Cannot load test dataset lab1.csv!"); - if (!data::Load("lab3.csv", labels, true)) + if (!data::Load("lab3.csv", labels)) FAIL("Cannot load test dataset lab3.csv!"); FFN, RandomInitialization> model; @@ -68,9 +68,9 @@ TEST_CASE("FFNWithOptimizerCallbackTest", "[CallbackTest]") arma::mat data; arma::mat labels; - if (!data::Load("lab1.csv", data, true)) + if (!data::Load("lab1.csv", data)) FAIL("Cannot load test dataset lab1.csv!"); - if (!data::Load("lab3.csv", labels, true)) + if (!data::Load("lab3.csv", labels)) FAIL("Cannot load test dataset lab3.csv!"); FFN, RandomInitialization> model; diff --git a/src/mlpack/tests/data/nbc_high_dim_test_labels.csv b/src/mlpack/tests/data/nbc_high_dim_test_labels.csv index dd6bde5f4b..59847158f6 100644 --- a/src/mlpack/tests/data/nbc_high_dim_test_labels.csv +++ b/src/mlpack/tests/data/nbc_high_dim_test_labels.csv @@ -1,50 +1,50 @@ -3 -2 -0 -0 -0 -1 -2 -3 -3 -2 -4 -2 -1 -2 -3 -1 -2 -4 -4 -1 -3 -0 -2 -0 -0 -2 -0 -1 -3 -3 -2 -2 -2 -3 -3 -3 -3 -3 -0 -0 -4 -3 -3 -0 -3 -2 -3 -2 -1 -1 +3 +2 +0 +0 +0 +1 +2 +3 +3 +2 +4 +2 +1 +2 +3 +1 +2 +4 +4 +1 +3 +0 +2 +0 +0 +2 +0 +1 +3 +3 +2 +2 +2 +3 +3 +3 +3 +3 +0 +0 +4 +3 +3 +0 +3 +2 +3 +2 +1 +1 diff --git a/src/mlpack/tests/data/nbc_high_dim_train_labels.csv b/src/mlpack/tests/data/nbc_high_dim_train_labels.csv index 064f0e24a2..c25922d7a4 100644 --- a/src/mlpack/tests/data/nbc_high_dim_train_labels.csv +++ b/src/mlpack/tests/data/nbc_high_dim_train_labels.csv @@ -1,200 +1,200 @@ -1 -4 -2 -2 -1 -0 -1 -0 -0 -4 -0 -4 -3 -4 -3 -2 -4 -2 -2 -2 -4 -1 -2 -1 -3 -0 -4 -1 -4 -4 -4 -0 -3 -4 -3 -1 -3 -2 -3 -0 -4 -1 -4 -1 -4 -2 -1 -4 -2 -1 -2 -0 -2 -2 -4 -2 -0 -2 -0 -3 -3 -3 -0 -2 -1 -4 -3 -1 -2 -2 -4 -0 -1 -3 -4 -4 -4 -2 -4 -2 -3 -4 -4 -3 -2 -3 -3 -4 -3 -4 -2 -4 -0 -3 -3 -1 -3 -4 -2 -1 -2 -3 -1 -3 -3 -0 -4 -0 -0 -3 -2 -1 -0 -3 -2 -1 -0 -0 -1 -0 -2 -2 -4 -2 -3 -1 -4 -4 -2 -3 -4 -0 -2 -2 -0 -4 -0 -3 -1 -4 -4 -2 -0 -0 -0 -0 -3 -4 -3 -2 -0 -4 -3 -3 -4 -0 -3 -1 -3 -4 -3 -2 -2 -4 -0 -0 -0 -0 -1 -4 -0 -3 -4 -3 -1 -4 -0 -1 -4 -3 -2 -1 -3 -2 -4 -3 -2 -0 -1 -4 -2 -0 -2 -3 -0 -0 -2 -1 -3 -1 +1 +4 +2 +2 +1 +0 +1 +0 +0 +4 +0 +4 +3 +4 +3 +2 +4 +2 +2 +2 +4 +1 +2 +1 +3 +0 +4 +1 +4 +4 +4 +0 +3 +4 +3 +1 +3 +2 +3 +0 +4 +1 +4 +1 +4 +2 +1 +4 +2 +1 +2 +0 +2 +2 +4 +2 +0 +2 +0 +3 +3 +3 +0 +2 +1 +4 +3 +1 +2 +2 +4 +0 +1 +3 +4 +4 +4 +2 +4 +2 +3 +4 +4 +3 +2 +3 +3 +4 +3 +4 +2 +4 +0 +3 +3 +1 +3 +4 +2 +1 +2 +3 +1 +3 +3 +0 +4 +0 +0 +3 +2 +1 +0 +3 +2 +1 +0 +0 +1 +0 +2 +2 +4 +2 +3 +1 +4 +4 +2 +3 +4 +0 +2 +2 +0 +4 +0 +3 +1 +4 +4 +2 +0 +0 +0 +0 +3 +4 +3 +2 +0 +4 +3 +3 +4 +0 +3 +1 +3 +4 +3 +2 +2 +4 +0 +0 +0 +0 +1 +4 +0 +3 +4 +3 +1 +4 +0 +1 +4 +3 +2 +1 +3 +2 +4 +3 +2 +0 +1 +4 +2 +0 +2 +3 +0 +0 +2 +1 +3 +1 diff --git a/src/mlpack/tests/feedforward_network_test.cpp b/src/mlpack/tests/feedforward_network_test.cpp index 880106e8ce..f7c0b99ea9 100644 --- a/src/mlpack/tests/feedforward_network_test.cpp +++ b/src/mlpack/tests/feedforward_network_test.cpp @@ -71,8 +71,8 @@ void CheckCopyFunction(ModelType* network1, network2 = *network1; delete network1; - // Deallocating all of network1's memory, so that - // if network2 is trying to use any of that memory. + // Deallocating all of network1's memory, so that network2 does not use any + // of that memory. arma::mat predictions2; network2.Predict(trainData, predictions2); CheckMatrices(predictions1, predictions2); @@ -93,8 +93,8 @@ void CheckMoveFunction(ModelType* network1, FFN<> network2(std::move(*network1)); delete network1; - // Deallocating all of network1's memory, so that - // if network2 is trying to use any of that memory. + // Deallocating all of network1's memory, so that network2 does not use any + // of that memory. arma::mat predictions2; network2.Predict(trainData, predictions2); CheckMatrices(predictions1, predictions2); @@ -154,7 +154,145 @@ TEST_CASE("CheckCopyMovingVanillaNetworkTest", "[FeedForwardNetworkTest]") } /** - * Check whether copying and moving network with dropout is working or not. + * Check whether copying and moving network with linear3d is working or not. + */ +TEST_CASE("CheckCopyMovingLinear3DNetworkTest", "[FeedForwardNetworkTest]") +{ + // Load the dataset. + arma::mat trainData; + data::Load("thyroid_train.csv", trainData, true); + + arma::mat trainLabels = trainData.row(trainData.n_rows - 1); + trainData.shed_row(trainData.n_rows - 1); + + /* + * Construct a feed forward network with trainData.n_rows input nodes, + * hiddenLayerSize hidden nodes and trainLabels.n_rows output nodes. The + * network structure looks like: + * + * Input Hidden Output + * Layer Layer Layer + * +-----+ +-----+ +-----+ + * | | | | | | + * | +------>| +------>| | + * | | +>| | +>| | + * +-----+ | +--+--+ | +-----+ + * | | + * Bias | Bias | + * Layer | Layer | + * +-----+ | +-----+ | + * | | | | | | + * | +-----+ | +-----+ + * | | | | + * +-----+ +-----+ + */ + + FFN > *model = new FFN >; + model->Add >(trainData.n_rows, 8); + model->Add >(); + model->Add >(8, 3); + model->Add >(); + + FFN > *model1 = new FFN >; + model1->Add >(trainData.n_rows, 8); + model1->Add >(); + model1->Add >(8, 3); + model1->Add >(); + + // Check whether copy constructor is working or not. + CheckCopyFunction<>(model, trainData, trainLabels, 1); + + // Check whether move constructor is working or not. + CheckMoveFunction<>(model1, trainData, trainLabels, 1); +} + +/** + * Check whether copying and moving of Noisy Linear layer is working or not. + */ +TEST_CASE("CheckCopyMovingNoisyLinearTest", "[FeedForwardNetworkTest]") +{ + // Create training input by 5x5 matrix. + arma::mat input = arma::randu(10,1); + // Create training output by 1 matrix. + arma::mat output = arma::mat("1"); + + // Check copying constructor. + FFN> *model1 = new FFN>(); + model1->Predictors() = input; + model1->Responses() = output; + model1->Add>(); + model1->Add>(10, 5); + model1->Add >(5, 1); + model1->Add>(); + + // Check whether copy constructor is working or not. + CheckCopyFunction<>(model1, input, output, 1); + + // Check moving constructor. + FFN> *model2 = new FFN>(); + model2->Predictors() = input; + model2->Responses() = output; + model2->Add>(); + model2->Add>(10, 5); + model2->Add >(5, 1); + model2->Add>(); + + // Check whether move constructor is working or not. + CheckMoveFunction<>(model2, input, output, 1); +} + +/** + * Check whether copying and moving of concatenate layer is working or not. + */ +TEST_CASE("CheckCopyMovingConcatenateTest", "[FeedForwardNetworkTest]") +{ + // Create training input by 5x5 matrix. + arma::mat input = arma::randu(10,1); + // Create training output by 1 matrix. + arma::mat output = arma::mat("1"); + + // Check copying constructor. + FFN> *model1 = new FFN>(); + model1->Predictors() = input; + model1->Responses() = output; + model1->Add>(); + model1->Add>(10, 5); + + // Create concatenate layer. + arma::mat concatMatrix = arma::ones(5, 1); + Concatenate<>* concatLayer = new Concatenate<>(); + concatLayer->Concat() = concatMatrix; + + // Add concatenate layer to the current network. + model1->Add(concatLayer); + model1->Add >(10, 5); + model1->Add>(); + + // Check whether copy constructor is working or not. + CheckCopyFunction<>(model1, input, output, 1); + + // Check moving constructor. + FFN> *model2 = new FFN>(); + model2->Predictors() = input; + model2->Responses() = output; + model2->Add>(); + model2->Add>(10, 5); + + // Create new concat layer. + Concatenate<>* concatLayer2 = new Concatenate<>(); + concatLayer2->Concat() = concatMatrix; + + // Add concatenate layer to the current network. + model2->Add(concatLayer2); + model2->Add >(10, 5); + model2->Add>(); + + // Check whether move constructor is working or not. + CheckMoveFunction<>(model2, input, output, 1); +} + +/** + * Check whether copying and moving of Dropout network is working or not. */ TEST_CASE("CheckCopyMovingDropoutNetworkTest", "[FeedForwardNetworkTest]") { diff --git a/src/mlpack/tests/svd_batch_test.cpp b/src/mlpack/tests/svd_batch_test.cpp index bac6df4917..41b005a28d 100644 --- a/src/mlpack/tests/svd_batch_test.cpp +++ b/src/mlpack/tests/svd_batch_test.cpp @@ -70,7 +70,8 @@ class SpecificRandomInitialization TEST_CASE("SVDBatchMomentumTest", "[SVDBatchTest]") { mat dataset; - data::Load("GroupLensSmall.csv", dataset); + if (!data::Load("GroupLensSmall.csv", dataset)) + FAIL("Cannot load dataset GroupLensSmall.csv!"); // Generate list of locations for batch insert constructor for sparse // matrices. @@ -117,7 +118,8 @@ TEST_CASE("SVDBatchMomentumTest", "[SVDBatchTest]") TEST_CASE("SVDBatchRegularizationTest", "[SVDBatchTest]") { mat dataset; - data::Load("GroupLensSmall.csv", dataset); + if (!data::Load("GroupLensSmall.csv", dataset)) + FAIL("Cannot load dataset GroupLensSmall.csv!"); // Generate list of locations for batch insert constructor for sparse // matrices.