diff --git a/.gitignore b/.gitignore index 7c5dad8bf2564dfbf787c686d28e314edf9b8d74..2ccc3f51f9f6b63f01302636c90c9d4f242dc617 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,10 @@ +*.orig +*.4n *.o *.mod *.out /Debug/ -/build/ +build/* compilers.env *TestRunner* *_fun.f90 @@ -10,3 +12,14 @@ src/funit.tmp *.swp /Release/ .idea/* +CMakeCache.txt +external_dependencies/* +CMakeFiles +Makefile +cmake_install.cmake +lib4neuro.cbp +_deps +*.vcxproj* +*.sln +.log +set_env_n_cores diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 8ad8068f230d7d463f57971d4a9f7a06d84bd886..207f13ad266fb3e491aab31340be090ac1c9e8d1 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,121 +1,105 @@ -# Windows 10 with Boost and Exprtk -# downloaded and compiled locally as -# submodules and linked statically -win_visual_studio_static_deps: +stages: + - build + +# Windows 10 with dependencies +# downloaded (and compiled) locally +# compiled by VisualStudio 2017 +win_visual_studio_static_local_deps: tags: - Win + image: windows:latest + + stage: build + before_script: + - rmdir /s /q build external_dependencies/* - call VsDevCmd.bat - - cd build_scripts\windows - - call win_download_dependencies.bat - set DEPENDENCIES_LINK_TYPE=static - - call win_VS_build_x64_debug.bat - - cd ..\.. + - set clean_after=yes + - set BUILD_LIB=yes + - set BUILD_EXAMPLES=yes + - set BUILD_TESTS=yes + + script: + - call build_scripts\windows\win_VS_build_x64_release.bat + +# Windows 10 with dependencies +# downloaded (and compiled) locally +# compiled by VisualStudio 2015 +win_visual_studio_2015_static_local_deps: + tags: + - Win + + image: windows:latest + + stage: build + + before_script: + - rmdir /s /q build external_dependencies/* + - set DEPENDENCIES_LINK_TYPE=static + - set clean_after=yes + - set BUILD_LIB=yes + - set BUILD_EXAMPLES=yes + - set BUILD_TESTS=yes script: - - cd build_scripts\windows - - call win_run_tests.bat - - cd ..\.. - -# Windows 10 with Boost and Exprtk -# downloaded and compiled locally as -# submodules and link dynamically -# -#win_visual_studio_shared_deps: -# tags: -# - Win -# -# before_script: -# - call VsDevCmd.bat -# - cd build_scripts\windows -# - call win_download_dependencies.bat -# - set DEPENDENCIES_LINK_TYPE=shared -# - call win_VS_build_x64_debug.bat -# - cd ..\.. -# -# script: -# - cd build_scripts\windows -# - call win_run_tests.bat -# - cd ..\.. -# -# -# Latest Ubuntu with Boost and Exprtk + - call build_scripts\windows\win_VS2015_build_x64_release.bat + +# Latest Ubuntu with dependencies # in system directories, Boost # installed from the official repository -# => only dynamical linking possible -#ubuntu_boost_system: -# tags: -# - centos7 -# -# image: martinbeseda/dockertest:latest -# -# before_script: -# - rm -rf external_dependencies -# - git clone https://github.com/ArashPartow/exprtk.git -# - cp exprtk/exprtk.hpp /usr/include -# - export TERM=xterm -# - cd build_scripts/linux -# - export DEPENDENCIES_LINK_TYPE=shared -# - ./linux_gcc_build_x64_debug_system.sh -# -# script: -# - './linux_run_tests.sh' -# -## Latest Ubuntu with Boost and Exprtk -## compiled locally as submodules and -## linked statically -#ubuntu_boost_local_static_deps: -# tags: -# - centos7 -# -# image: martinbeseda/ubuntu-ci:latest -# -# before_script: -# - cd build_scripts/linux -# - ./download_dependencies.sh -# - cd ../.. -# - cd build_scripts/linux -# - export DEPENDENCIES_LINK_TYPE=static -# - ./linux_gcc_build_x64_debug_local.sh -# -# script: -# - './linux_run_tests.sh' -# -## Latest Ubuntu with Boost and Exprtk -## compiled locally as submodules and -## linked dynamically -#ubuntu_boost_local_dynamic_deps: -# tags: -# - centos7 -# -# image: martinbeseda/ubuntu-ci:latest -# -# before_script: -# - cd build_scripts/linux -# - ./download_dependencies.sh -# - cd ../.. -# - cd build_scripts/linux -# - export DEPENDENCIES_LINK_TYPE=shared -# - ./linux_gcc_build_x64_debug_local.sh -# -# script: -# - './linux_run_tests.sh' - - -#code_quality: -# image: docker:stable -# variables: -# DOCKER_DRIVER: overlay2 -# allow_failure: true -# services: -# - docker:stable-dind -# script: -# - export SP_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/') -# - docker run -# --env SOURCE_CODE="$PWD" -# --volume "$PWD":/code -# --volume /var/run/docker.sock:/var/run/docker.sock -# "registry.gitlab.com/gitlab-org/security-products/codequality:$SP_VERSION" /code -# artifacts: -# paths: [gl-code-quality-report.json] +ubuntu_boost_system: + image: martinbeseda/lib4neuro-ubuntu-system-deps:latest + + stage: build + + before_script: + - rm -rf build external_dependencies/* + - export TERM=xterm + - export DEPENDENCIES_LINK_TYPE=shared + - export CLEAN_AFTER=yes + - export BUILD_LIB=yes + - export BUILD_EXAMPLES=yes + - export BUILD_TESTS=yes + + script: + - build_scripts/linux/linux_gcc_build_x64_debug_system.sh + +# Latest Ubuntu with dependencies +# downloaded (and compiled) locally +ubuntu_boost_local_static_deps: + image: martinbeseda/ubuntu-ci:latest + + stage: build + + before_script: + - rm -rf build external_dependencies/* + - export TERM=xterm + - export DEPENDENCIES_LINK_TYPE=static + - export CLEAN_AFTER=yes + - export BUILD_LIB=yes + - export BUILD_EXAMPLES=yes + - export BUILD_TESTS=yes + + script: + - build_scripts/linux/linux_gcc_build_x64_debug_local.sh + +# Latest CentOS with dependencies +# downloaded (and compiled) locally +centos_local_deps: + image: martinbeseda/centos-ci:latest + + stage: build + + before_script: + - rm -rf build external_dependencies/* + - export TERM=xterm + - export DEPENDENCIES_LINK_TYPE=static + - export CLEAN_AFTER=yes + - export BUILD_LIB=yes + - export BUILD_EXAMPLES=yes + - export BUILD_TESTS=yes + + script: + - scl enable devtoolset-8 -- build_scripts/linux/linux_gcc_build_x64_debug_local.sh diff --git a/.gitlab/issue_templates/Bug.md b/.gitlab/issue_templates/Bug.md index c61a027fa5e51dd3dd03908ae3effd74d975e884..92082de38adc90a6b06b8863a2cc9163c0f5554b 100644 --- a/.gitlab/issue_templates/Bug.md +++ b/.gitlab/issue_templates/Bug.md @@ -1,32 +1,32 @@ -Summary +### Summary (Summarize the bug encountered concisely) -Steps to reproduce +### Steps to reproduce (How one can reproduce the issue - this is very important) -What is the current bug behavior? +### What is the current bug behavior? (What actually happens) -What is the expected correct behavior? +### What is the expected correct behavior? (What you should see instead) -Relevant logs and/or screenshots +### Relevant logs and/or screenshots (Paste any relevant logs - please use code blocks (```) to format console output, logs, and code as it's very hard to read otherwise.) -Possible fixes +### Possible fixes (If you can, link to the line of code that might be responsible for the problem) -/label ~bug ~needs-investigation +/label ~bug diff --git a/.gitlab/issue_templates/Todo.md b/.gitlab/issue_templates/TODO.md similarity index 55% rename from .gitlab/issue_templates/Todo.md rename to .gitlab/issue_templates/TODO.md index 811759ba9402a4a67b37019c0d1851783412192d..d78b4742e2afebefd788e6cfa273f1f7b2503621 100644 --- a/.gitlab/issue_templates/Todo.md +++ b/.gitlab/issue_templates/TODO.md @@ -1,7 +1,7 @@ -Summary +### Summary (Summarize the task concisely) -/label ~task ~todo +/label ~TODO diff --git a/.gitlab/issue_templates/Task.md b/.gitlab/issue_templates/Task.md index 068c42c8615c8aeb0d13b564c17bfe9bb80d58e2..fa1006d6ccaf265cd1975ae1285dfdc1483af982 100644 --- a/.gitlab/issue_templates/Task.md +++ b/.gitlab/issue_templates/Task.md @@ -1,7 +1,7 @@ -Summary +### Summary (Summarize the task concisely) -/label ~task +/label ~Task diff --git a/.gitlab/issue_templates/documentation.md b/.gitlab/issue_templates/documentation.md new file mode 100644 index 0000000000000000000000000000000000000000..8cb3225cc8727f9bf59c3980fb6584ab9618a865 --- /dev/null +++ b/.gitlab/issue_templates/documentation.md @@ -0,0 +1,10 @@ +### Summary + +(Summarize the task concisely) + +### Parts of code to be documented + +(Identify classes, functions etc., which should be documented) + +/label ~documentation + diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 0cc6a91e81d7cbf0edb2f13ce8012dc5c775aeb8..0000000000000000000000000000000000000000 --- a/.gitmodules +++ /dev/null @@ -1,6 +0,0 @@ -[submodule "external_dependencies/exprtk"] - path = external_dependencies/exprtk - url = https://github.com/ArashPartow/exprtk.git -[submodule "external_dependencies/boost"] - path = external_dependencies/boost - url = https://github.com/boostorg/boost.git diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000000000000000000000000000000000000..a918415bea583f3f3a708e96c9c61677a16d6009 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,5 @@ +For detailed information about new features and bug fixes in different versions +of lib4neuro have a look at [Releases](https://code.it4i.cz/moldyn/lib4neuro/releases). + +Versions are labeled like `YEAR.MONTH.N`, which means, that it's the Nth release +of lib4neuro in that specific month. \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index bb654e3a38ddbf377a729b3f36854ba6096b1b34..6ec7b36d4220338d139c69c2014e06185a1b9903 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,128 +1,230 @@ -cmake_minimum_required(VERSION 3.0) -project(lib4neuro) +CMAKE_MINIMUM_REQUIRED(VERSION 3.13) -message("Using CMake ${CMAKE_VERSION}") +PROJECT(lib4neuro) -#TODO request newer version of CMake >=3.12 +MESSAGE(STATUS "lib4neuro CMake starting...") +MESSAGE(STATUS "Using CMake ${CMAKE_VERSION}") + +#TODO use 'option' instead of 'set' for boolean variables! + +#---------------# +# DEBUG options # +#---------------# +# Print target dependencies during a configuration phase +# set_property(GLOBAL PROPERTY GLOBAL_DEPENDS_DEBUG_MODE 1) + +# Debug memory errors +#add_compile_options(-fsanitize=address) +#add_link_options(-fsanitize=address -static-libasan) + +#------------------------------------------# +# Detect maximum available number of cores # +# and set corresponding build options # +#------------------------------------------# +MESSAGE("Detecting available cores count...") +INCLUDE(ProcessorCount) +PROCESSORCOUNT(N_CORES) +IF(N_CORES GREATER 1) + MATH(EXPR N_CORES "${N_CORES}-1") + SET(CTEST_BUILD_FLAGS -j ${N_CORES}) + + SET(ENV{N_CORES} ${N_CORES}) + + # Create scripts for setting N_CORES in the parent process + IF(WIN32) + FILE(WRITE set_env_n_cores.bat "set N_CORES=${N_CORES}") + ELSE() + FILE(WRITE set_env_n_cores "export N_CORES=${N_CORES}") + ENDIF() + + SET(ctest_test_args ${ctest_test_args} PARALLEL_LEVEL ${N_CORES}) +ENDIF() +MESSAGE(STATUS "Build can be performed on ${N_CORES} cores.") + +#TODO use just locally, where needed +IF(WIN32) + ADD_COMPILE_DEFINITIONS(BOOST_ALL_NO_LIB NOMINMAX) +ELSE() + ADD_COMPILE_DEFINITIONS(BOOST_TEST_DYN_LINK) +ENDIF() +ADD_COMPILE_DEFINITIONS(ARMA_DONT_USE_WRAPPER) +ADD_COMPILE_DEFINITIONS(BOOST_LIB_DIAGNOSTIC) #------------# # Build type # #------------# # Release / None / Debug -if (NOT CMAKE_BUILD_TYPE) - set (CMAKE_BUILD_TYPE RELEASE CACHE STRING - "Choose the type of build, options are: None Debug Release." - FORCE) -elseif("CMAKE_BUILD_TYPE" STREQUAL "Debug") - #TODO rewrite to use add_compile_definitions - add_compile_options(-DDEBUG) -endif (NOT CMAKE_BUILD_TYPE) +IF(NOT CMAKE_BUILD_TYPE) + SET(CMAKE_BUILD_TYPE RELEASE CACHE STRING + "Choose the type of build, options are: None Debug Release." + FORCE) +ELSEIF(${CMAKE_BUILD_TYPE} STREQUAL "Debug") + ADD_COMPILE_DEFINITIONS("L4N_DEBUG") + +ELSEIF(${CMAKE_BUILD_TYPE} STREQUAL "Release") + IF(WIN32) + ADD_COMPILE_DEFINITIONS(_ITERATOR_DEBUG_LEVEL=0) + ENDIF() +ENDIF(NOT CMAKE_BUILD_TYPE) +MESSAGE(STATUS CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}) #------------------------# # Dependencies link type # #------------------------# -if(NOT DEPENDENCIES_LINK_TYPE AND NOT ENV{DEPENDENCIES_LINK_TYPE}) - message(FATAL_ERROR "Please, set the variable DEPENDENCIES_LINK_TYPE to either 'static' or 'shared'!") -endif() - -#------------------------# -# Linking of boost_test_framework -#------------------------# -add_compile_options("-DBOOST_ALL_NO_LIB") -add_compile_options("-DBOOST_TEST_DYN_LINK") +IF(NOT DEPENDENCIES_LINK_TYPE AND NOT ENV{DEPENDENCIES_LINK_TYPE}) + SET(DEPENDENCIES_LINK_TYPE "static") + MESSAGE("DEPENDENCIES_LINK_TYPE is not specified - lib4neuro is going to be linked statically.") +ELSEIF(ENV{DEPENDENCIES_LINK_TYPE}) + SET(DEPENDENCIES_LINK_TYPE ENV{DEPENDENCIES_LINK_TYPE}) +ENDIF() #--------------------------------# # Setting C++ compiler flags # #--------------------------------# -set(CMAKE_CXX_STANDARD 17) - -#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}" ) - -if( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel" ) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -xHost" ) -elseif( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC" ) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W0 /bigobj") - add_compile_options("-D_SCL_SECURE_NO_WARNINGS") -else() - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") -endif() +SET(CMAKE_CXX_STANDARD 17) +SET(BOOST_TOOLSET "") +IF("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -xHost") + SET(BOOST_TOOLSET --toolset=intel) +ELSEIF("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W0 /bigobj") + ADD_COMPILE_OPTIONS("/D _SCL_SECURE_NO_WARNINGS") + ADD_COMPILE_OPTIONS("/D _CRT_SECURE_NO_WARNINGS") + + if("${MSVC_TOOLSET_VERSION}" STREQUAL "140") + SET(BOOST_TOOLSET --toolset=msvc-14.0) + elseif("${MSVC_TOOLSET_VERSION}" STREQUAL "141") + SET(BOOST_TOOLSET --toolset=msvc-15.0) + else() + SET(BOOST_TOOLSET --toolset=msvc) + endif() + +ELSEIF("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MINGW") + SET(BOOST_TOOLSET --toolset=gcc) + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mbig-obj") +ELSE() + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") +ENDIF() #--------------------# # Automatic settings # #--------------------# -if(CMAKE_BUILD_TYPE MATCHES DEBUG) - set(CMAKE_VERBOSE_MAKEFILE ON) -endif() +IF(CMAKE_BUILD_TYPE MATCHES DEBUG) + OPTION(CMAKE_VERBOSE_MAKEFILE ON) +ENDIF() + +#------------------------------------------------------------------------------------# +# Check, if the path to the current directory does contain some unallowed characters # +#------------------------------------------------------------------------------------# +STRING(REGEX MATCH "[A-Za-z0-9 \\\\/:_-]*" MATCH ${CMAKE_CURRENT_LIST_DIR}) +IF(NOT ${MATCH} STREQUAL ${CMAKE_CURRENT_LIST_DIR}) + MESSAGE(FATAL_ERROR "Illegal character(s) found in the path to the current directory!") +ENDIF() + +#---------------# +# Set variables # +#---------------# +SET(ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}) +SET(SRC_DIR ${ROOT_DIR}/src) +SET(PROJECT_BINARY_DIR ${ROOT_DIR}/build) + +#----------------------------------------# +# Set prefixes and suffixes of libraries # +#----------------------------------------# +SET(LIB_PREFIX "lib") +SET(LIB_SUFFIX "a") # suffix for Linux static libraries +IF("${DEPENDENCIES_LINK_TYPE}" STREQUAL "shared" AND WIN32) + SET(LIB_PREFIX "") + SET(LIB_SUFFIX "dll") +ELSEIF("${DEPENDENCIES_LINK_TYPE}" STREQUAL "static" AND WIN32) + SET(LIB_SUFFIX "lib") +ELSEIF("${DEPENDENCIES_LINK_TYPE}" STREQUAL "shared") + SET(LIB_SUFFIX "so") +ENDIF() #-------------------------# # Find external libraries # #-------------------------# -message("Looking for external libraries...") -set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}) - -set(Boost_USE_MULTITHREADED ON) -set(Boost_DEBUG ON) +MESSAGE("Looking for external libraries...") +SET(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) -#set(CMAKE_INCLUDE_PATH ${BOOST_INCLUDEDIR} "${CMAKE_CURRENT_LIST_DIR}/external_dependencies/boost" ${CMAKE_INCLUDE_PATH}) -#set(CMAKE_LIBRARY_PATH ${BOOST_LIBRARYDIR} "${CMAKE_CURRENT_LIST_DIR}/external_dependencies/boost/stage/lib" ${CMAKE_LIBRARY_PATH}) +#TODO make downloading dependencies arbitrary +OPTION(ALLOW_DEPENDENCIES_DOWNLOAD "Allow external dependencies to be downloaded locally, if they're not found." ON) +OPTION(ALLOW_OpenBLAS_DOWNLOAD "Allow OpenBLAS to be downloaded locally, if it's not found. It's not necessary, but +it will make lib4neuro faster." ON) -message("CMAKE_INCLUDE_PATH: ${CMAKE_INCLUDE_PATH}") -message("CMAKE_LIBRARY_PATH: ${CMAKE_LIBRARY_PATH}") +OPTION(Boost_USE_MULTITHREADED ON) +OPTION(Boost_DEBUG OFF) -find_package( +FIND_PACKAGE( Boost - COMPONENTS - system - serialization - random + COMPONENTS + system + serialization + random ) - -if(NOT Boost_FOUND) - message(FATAL_ERROR "Boost was NOT found! Specify variables BOOST_INCLUDEDIR and BOOST_LIBRARYDIR!") -endif() - -message("Boost_INCLUDE_DIRS: ${Boost_INCLUDE_DIRS}") -message("Boost_LIBRARY_DIRS: ${Boost_LIBRARY_DIRS}") -message("Boost_LIBRARIES: ${Boost_LIBRARIES}") - -message("lib4neuro LIB DIR: ${LIB4NEURO_DIR}") - -find_package(exprtk) - -#------------------------------------------# -# Detect maximum available number of cores # -# and set corresponding build options # -#------------------------------------------# -message("Detecting available cores count...") -include(ProcessorCount) -ProcessorCount(n_cores) -if(n_cores GREATER 1) - math(EXPR n_cores "${n_cores}-1") - message("Build will be performed on ${n_cores} cores.") - set(CTEST_BUILD_FLAGS -j${N}) - set(ENV{N_CORES} ${N}) - set(ctest_test_args ${ctest_test_args} PARALLEL_LEVEL ${N}) -endif() - -#---------------# -# Set variables # -#---------------# -set(ROOT_DIR ${CMAKE_CURRENT_LIST_DIR}) -set(SRC_DIR ${CMAKE_CURRENT_LIST_DIR}/src) -set(PROJECT_BINARY_DIR ${CMAKE_CURRENT_LIST_DIR}/build) -set(CMAKE_LIBRARY_OUTPUT_DIRECTORY lib) -set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY lib) -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin) - -#----------------------------------------# -# Dependencies for the lib4neuro library # -#----------------------------------------# -message("Running CMake in: ${SRC_DIR} ${PROJECT_BINARY_DIR}") - -if("${BUILD_LIB}" STREQUAL "no") - link_directories(${LIB4NEURO_DIR}) - include_directories(${Boost_INCLUDE_DIRS} ${ROOT_DIR}/include ${EXPRTK_INCLUDE_DIR}) -endif() - -add_subdirectory(${SRC_DIR} ${PROJECT_BINARY_DIR}) +IF((NOT Boost_FOUND) AND ALLOW_DEPENDENCIES_DOWNLOAD) + MESSAGE("Boost will be downloaded and compiled locally in 'external_dependencies' folder.") + INCLUDE( + DownloadBoost + RESULT_VARIABLE rv) + MESSAGE("Boost download: " ${rv}) +ELSEIF(NOT Boost_FOUND) + MESSAGE(FATAL_ERROR "Boost was not found! Set variables BOOST_LIBRARYDIR and BOOST_INCLUDEDIR manually or set ALLOW_DEPENDENCIES_DOWNLOAD to ON to allow automatic download and compilation of Boost.") +ENDIF() + +FIND_PACKAGE(Exprtk) +IF(NOT EXPRTK_FOUND AND ALLOW_DEPENDENCIES_DOWNLOAD) + MESSAGE("Exprt will be downloaded and compiled locally in 'external_dependencies' folder.") + INCLUDE(DownloadExprtk) +ENDIF() +MESSAGE(STATUS EXPRTK_INCLUDE_DIRS: ${EXPRTK_INCLUDE_DIRS}) + +FIND_PACKAGE(Turtle) +IF(NOT TURTLE_FOUND AND ALLOW_DEPENDENCIES_DOWNLOAD) + MESSAGE("Turtle will be downloaded and compiled locally in 'external_dependencies' folder.") + INCLUDE(DownloadTurtle) +ENDIF() +MESSAGE(STATUS TURTLE_INCLUDE_DIRS: ${TURTLE_INCLUDE_DIRS}) + +FIND_PACKAGE(Armadillo) +IF(NOT ARMADILLO_FOUND AND ALLOW_DEPENDENCIES_DOWNLOAD) + MESSAGE("Armadillo will be downloaded and compiled locally in 'external_dependencies' folder.") + INCLUDE(DownloadArmadillo) +ENDIF() + +#----------------------------------------------------------------------------# +# If BLAS or LAPACK are not set up explicitly, use x64 binaries in Armadillo # +# repository (ONLY on Windows) # +#----------------------------------------------------------------------------# +IF(NOT BLAS_DIR AND NOT LAPACK_DIR AND WIN32) + SET(BLAS_LIBRARIES ${ARMADILLO_ROOT}/examples/lib_win64/blas_win64_MT.lib) + SET(LAPACK_LIBRARIES ${ARMADILLO_ROOT}/examples/lib_win64/lapack_win64_MT.lib) +ENDIF() + +#-------------------------------------------------------# +# Look for linear algebra libraries needed by Armadillo # +# (ONLY on Linux systems) # +#-------------------------------------------------------# +IF(NOT WIN32) + FIND_PACKAGE(OpenBLAS) + IF(NOT OpenBLAS_FOUND) + IF(ALLOW_OpenBLAS_DOWNLOAD AND NOT WIN32) + INCLUDE(DownloadOpenBLAS) + ELSE() + FIND_PACKAGE(BLAS) + FIND_PACKAGE(LAPACK) + + IF(NOT BLAS_FOUND AND NOT LAPACKE_FOUND) + MESSAGE(FATAL_ERROR "No BLAS or LAPACK libraries are available!") + ENDIF() + + ENDIF() + ENDIF() +ENDIF() + +#---------------------------------------------------# +# Add subdirectory with source codes to be compiled # +#---------------------------------------------------# +ADD_SUBDIRECTORY(${SRC_DIR}) diff --git a/Doxyfile b/Doxyfile index b70fc203828d0883f952d9dcfa6eddd6f2a28934..8790372509d85eb3b0249938a54fab0822cdf2cc 100644 --- a/Doxyfile +++ b/Doxyfile @@ -1,4 +1,4 @@ -# Doxyfile 1.8.14 +# Doxyfile 1.8.13 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. @@ -20,8 +20,8 @@ # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all text # before the first occurrence of this tag. Doxygen uses libiconv (or the iconv -# built into libc) for the transcoding. See -# https://www.gnu.org/software/libiconv/ for the list of possible encodings. +# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv +# for the list of possible encodings. # The default value is: UTF-8. DOXYFILE_ENCODING = UTF-8 @@ -32,13 +32,13 @@ DOXYFILE_ENCODING = UTF-8 # title of most generated pages and in a few other places. # The default value is: My Project. -PROJECT_NAME = "lib4neuro" +PROJECT_NAME = lib4neuro # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = +PROJECT_NUMBER = # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a @@ -51,14 +51,14 @@ PROJECT_BRIEF = "Massivelly-parallel neural networks library" # pixels and the maximum width should not exceed 200 pixels. Doxygen will copy # the logo to the output directory. -PROJECT_LOGO = "img/lib4neuro_logo.png" +PROJECT_LOGO = img/lib4neuro_logo.png # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path # into which the generated documentation will be written. If a relative path is # entered, it will be relative to the location where doxygen was started. If # left blank the current directory will be used. -OUTPUT_DIRECTORY = "docs" +OUTPUT_DIRECTORY = docs # If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- # directories (in 2 levels) under the output directory of each output format and @@ -162,7 +162,7 @@ FULL_PATH_NAMES = YES # will be relative from the directory where doxygen is started. # This tag requires that the tag FULL_PATH_NAMES is set to YES. -STRIP_FROM_PATH = +STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the # path mentioned in the documentation of a class, which tells the reader which @@ -171,7 +171,7 @@ STRIP_FROM_PATH = # specify the list of include paths that are normally passed to the compiler # using the -I flag. -STRIP_FROM_INC_PATH = +STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but # less readable) file names. This can be useful is your file systems doesn't @@ -238,13 +238,13 @@ TAB_SIZE = 4 # "Side Effects:". You can put \n's in the value part of an alias to insert # newlines. -ALIASES = +ALIASES = # This tag can be used to specify a number of word-keyword mappings (TCL only). # A mapping has the form "name=value". For example adding "class=itcl::class" # will allow you to use the command class in the itcl::class meaning. -TCL_SUBST = +TCL_SUBST = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. For @@ -291,7 +291,7 @@ OPTIMIZE_OUTPUT_VHDL = NO # Note that for custom extensions you also need to set FILE_PATTERNS otherwise # the files are not read by doxygen. -EXTENSION_MAPPING = +EXTENSION_MAPPING = # If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments # according to the Markdown format, which allows for more readable @@ -337,7 +337,7 @@ BUILTIN_STL_SUPPORT = NO CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip (see: -# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen +# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen # will parse them like normal C++ but will assume all classes use public instead # of private inheritance when no explicit protection keyword is present. # The default value is: NO. @@ -435,13 +435,13 @@ LOOKUP_CACHE_SIZE = 0 # normally produced when WARNINGS is set to YES. # The default value is: NO. -EXTRACT_ALL = NO +EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will # be included in the documentation. # The default value is: NO. -EXTRACT_PRIVATE = YES +EXTRACT_PRIVATE = NO # If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal # scope will be included in the documentation. @@ -453,7 +453,7 @@ EXTRACT_PACKAGE = NO # included in the documentation. # The default value is: NO. -EXTRACT_STATIC = YES +EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined # locally in source files will be included in the documentation. If set to NO, @@ -524,7 +524,7 @@ INTERNAL_DOCS = NO # and Mac users are advised to set this option to NO. # The default value is: system dependent. -CASE_SENSE_NAMES = YES +CASE_SENSE_NAMES = NO # If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with # their full class and namespace scopes in the documentation. If set to YES, the @@ -648,7 +648,7 @@ GENERATE_DEPRECATEDLIST= YES # sections, marked by \if <section_label> ... \endif and \cond <section_label> # ... \endcond blocks. -ENABLED_SECTIONS = +ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the # initial value of a variable or macro / define can have for it to appear in the @@ -690,7 +690,7 @@ SHOW_NAMESPACES = YES # by doxygen. Whatever the program writes to standard output is used as the file # version. For an example see the documentation. -FILE_VERSION_FILTER = +FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated @@ -703,17 +703,17 @@ FILE_VERSION_FILTER = # DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE # tag is left empty. -LAYOUT_FILE = +LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files containing # the reference definitions. This must be a list of .bib files. The .bib # extension is automatically appended if omitted. This requires the bibtex tool -# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. +# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. # For LaTeX the style of the bibliography can be controlled using # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the # search path. See also \cite for info how to create references. -CITE_BIB_FILES = +CITE_BIB_FILES = #--------------------------------------------------------------------------- # Configuration options related to warning and progress messages @@ -778,7 +778,7 @@ WARN_FORMAT = "$file:$line: $text" # messages should be written. If left blank the output is written to standard # error (stderr). -WARN_LOGFILE = +WARN_LOGFILE = #--------------------------------------------------------------------------- # Configuration options related to the input files @@ -790,12 +790,12 @@ WARN_LOGFILE = # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. -INPUT = "src" +INPUT = src # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv -# documentation (see: https://www.gnu.org/software/libiconv/) for the list of +# documentation (see: http://www.gnu.org/software/libiconv) for the list of # possible encodings. # The default value is: UTF-8. @@ -873,7 +873,7 @@ RECURSIVE = YES # Note that relative paths are relative to the directory from which doxygen is # run. -EXCLUDE = +EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded @@ -889,7 +889,7 @@ EXCLUDE_SYMLINKS = NO # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories for example use the pattern */test/* -EXCLUDE_PATTERNS = +EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the @@ -900,13 +900,13 @@ EXCLUDE_PATTERNS = # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories use the pattern */test/* -EXCLUDE_SYMBOLS = +EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or directories # that contain example code fragments that are included (see the \include # command). -EXAMPLE_PATH = +EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and @@ -926,7 +926,7 @@ EXAMPLE_RECURSIVE = NO # that contain images that are to be included in the documentation (see the # \image command). -IMAGE_PATH = +IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program @@ -947,7 +947,7 @@ IMAGE_PATH = # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by doxygen. -INPUT_FILTER = +INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the @@ -960,7 +960,7 @@ INPUT_FILTER = # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by doxygen. -FILTER_PATTERNS = +FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will also be used to filter the input files that are used for @@ -975,14 +975,14 @@ FILTER_SOURCE_FILES = NO # *.ext= (so without naming a filter). # This tag requires that the tag FILTER_SOURCE_FILES is set to YES. -FILTER_SOURCE_PATTERNS = +FILTER_SOURCE_PATTERNS = # If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that # is part of the input, its contents will be placed on the main page # (index.html). This can be useful if you have a project on for instance GitHub # and want to reuse the introduction page also for the doxygen output. -USE_MDFILE_AS_MAINPAGE = +USE_MDFILE_AS_MAINPAGE = #--------------------------------------------------------------------------- # Configuration options related to source browsing @@ -995,7 +995,7 @@ USE_MDFILE_AS_MAINPAGE = # also VERBATIM_HEADERS is set to NO. # The default value is: NO. -SOURCE_BROWSER = NO +SOURCE_BROWSER = YES # Setting the INLINE_SOURCES tag to YES will include the body of functions, # classes and enums directly into the documentation. @@ -1043,7 +1043,7 @@ SOURCE_TOOLTIPS = YES # If the USE_HTAGS tag is set to YES then the references to source code will # point to the HTML generated by the htags(1) tool instead of doxygen built-in # source browser. The htags tool is part of GNU's global source tagging system -# (see https://www.gnu.org/software/global/global.html). You will need version +# (see http://www.gnu.org/software/global/global.html). You will need version # 4.8.6 or higher. # # To use it do the following: @@ -1070,6 +1070,25 @@ USE_HTAGS = NO VERBATIM_HEADERS = YES +# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the +# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the +# cost of reduced performance. This can be particularly helpful with template +# rich C++ code for which doxygen's built-in parser lacks the necessary type +# information. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse-libclang=ON option for CMake. +# The default value is: NO. + +CLANG_ASSISTED_PARSING = NO + +# If clang assisted parsing is enabled you can provide the compiler with command +# line options that you would normally use when invoking the compiler. Note that +# the include paths will already be set by doxygen for the files and directories +# specified with INPUT and INCLUDE_PATH. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_OPTIONS = + #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- @@ -1094,7 +1113,7 @@ COLS_IN_ALPHA_INDEX = 5 # while generating the index headers. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. -IGNORE_PREFIX = +IGNORE_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the HTML output @@ -1138,7 +1157,7 @@ HTML_FILE_EXTENSION = .html # of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_HEADER = +HTML_HEADER = # The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each # generated HTML page. If the tag is left blank doxygen will generate a standard @@ -1148,7 +1167,7 @@ HTML_HEADER = # that doxygen normally uses. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_FOOTER = +HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading style # sheet that is used by each HTML page. It can be used to fine-tune the look of @@ -1160,7 +1179,7 @@ HTML_FOOTER = # obsolete. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_STYLESHEET = +HTML_STYLESHEET = # The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined # cascading style sheets that are included after the standard style sheets @@ -1173,7 +1192,7 @@ HTML_STYLESHEET = # list). For an example see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_EXTRA_STYLESHEET = +HTML_EXTRA_STYLESHEET = # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note @@ -1183,12 +1202,12 @@ HTML_EXTRA_STYLESHEET = # files will be copied as-is; there are no commands or markers available. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_EXTRA_FILES = +HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the style sheet and background images according to # this color. Hue is specified as an angle on a colorwheel, see -# https://en.wikipedia.org/wiki/Hue for more information. For instance the value +# http://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. # Minimum value: 0, maximum value: 359, default value: 220. @@ -1224,17 +1243,6 @@ HTML_COLORSTYLE_GAMMA = 80 HTML_TIMESTAMP = NO -# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML -# documentation will contain a main index with vertical navigation menus that -# are dynamically created via Javascript. If disabled, the navigation index will -# consists of multiple levels of tabs that are statically embedded in every HTML -# page. Disable this option to support browsers that do not have Javascript, -# like the Qt help browser. -# The default value is: YES. -# This tag requires that the tag GENERATE_HTML is set to YES. - -HTML_DYNAMIC_MENUS = YES - # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. @@ -1258,12 +1266,12 @@ HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files will be # generated that can be used as input for Apple's Xcode 3 integrated development -# environment (see: https://developer.apple.com/tools/xcode/), introduced with +# environment (see: http://developer.apple.com/tools/xcode/), introduced with # OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a # Makefile in the HTML output directory. Running make will produce the docset in # that directory and running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at -# startup. See https://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. @@ -1323,7 +1331,7 @@ GENERATE_HTMLHELP = NO # written to the html output directory. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. -CHM_FILE = +CHM_FILE = # The HHC_LOCATION tag can be used to specify the location (absolute path # including file name) of the HTML help compiler (hhc.exe). If non-empty, @@ -1331,7 +1339,7 @@ CHM_FILE = # The file has to be specified with full path. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. -HHC_LOCATION = +HHC_LOCATION = # The GENERATE_CHI flag controls if a separate .chi index file is generated # (YES) or that it should be included in the master .chm file (NO). @@ -1344,7 +1352,7 @@ GENERATE_CHI = NO # and project file content. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. -CHM_INDEX_ENCODING = +CHM_INDEX_ENCODING = # The BINARY_TOC flag controls whether a binary table of contents is generated # (YES) or a normal table of contents (NO) in the .chm file. Furthermore it @@ -1375,11 +1383,11 @@ GENERATE_QHP = NO # the HTML output folder. # This tag requires that the tag GENERATE_QHP is set to YES. -QCH_FILE = +QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace -# (see: http://doc.qt.io/qt-4.8/qthelpproject.html#namespace). +# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1387,7 +1395,8 @@ QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual -# Folders (see: http://doc.qt.io/qt-4.8/qthelpproject.html#virtual-folders). +# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- +# folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. @@ -1395,31 +1404,33 @@ QHP_VIRTUAL_FOLDER = doc # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom -# Filters (see: http://doc.qt.io/qt-4.8/qthelpproject.html#custom-filters). +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). # This tag requires that the tag GENERATE_QHP is set to YES. -QHP_CUST_FILTER_NAME = +QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom -# Filters (see: http://doc.qt.io/qt-4.8/qthelpproject.html#custom-filters). +# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- +# filters). # This tag requires that the tag GENERATE_QHP is set to YES. -QHP_CUST_FILTER_ATTRS = +QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's filter section matches. Qt Help Project / Filter Attributes (see: -# http://doc.qt.io/qt-4.8/qthelpproject.html#filter-attributes). +# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). # This tag requires that the tag GENERATE_QHP is set to YES. -QHP_SECT_FILTER_ATTRS = +QHP_SECT_FILTER_ATTRS = # The QHG_LOCATION tag can be used to specify the location of Qt's # qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the # generated .qhp file. # This tag requires that the tag GENERATE_QHP is set to YES. -QHG_LOCATION = +QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be # generated, together with the HTML files, they form an Eclipse help plugin. To @@ -1502,7 +1513,7 @@ EXT_LINKS_IN_WINDOW = NO FORMULA_FONTSIZE = 10 -# Use the FORMULA_TRANSPARENT tag to determine whether or not the images +# Use the FORMULA_TRANPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are not # supported properly for IE 6.0, but are supported on all modern browsers. # @@ -1514,7 +1525,7 @@ FORMULA_FONTSIZE = 10 FORMULA_TRANSPARENT = YES # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see -# https://www.mathjax.org) which uses client side Javascript for the rendering +# http://www.mathjax.org) which uses client side Javascript for the rendering # instead of using pre-rendered bitmaps. Use this if you do not have LaTeX # installed or if you want to formulas look prettier in the HTML output. When # enabled you may also need to install MathJax separately and configure the path @@ -1541,18 +1552,18 @@ MATHJAX_FORMAT = HTML-CSS # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of -# MathJax from https://www.mathjax.org before deployment. -# The default value is: https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.2/. +# MathJax from http://www.mathjax.org before deployment. +# The default value is: http://cdn.mathjax.org/mathjax/latest. # This tag requires that the tag USE_MATHJAX is set to YES. -MATHJAX_RELPATH = https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.2/ +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax # extension names that should be enabled during MathJax rendering. For example # MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols # This tag requires that the tag USE_MATHJAX is set to YES. -MATHJAX_EXTENSIONS = +MATHJAX_EXTENSIONS = # The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces # of code that will be used on startup of the MathJax code. See the MathJax site @@ -1560,7 +1571,7 @@ MATHJAX_EXTENSIONS = # example see the documentation. # This tag requires that the tag USE_MATHJAX is set to YES. -MATHJAX_CODEFILE = +MATHJAX_CODEFILE = # When the SEARCHENGINE tag is enabled doxygen will generate a search box for # the HTML output. The underlying search engine uses javascript and DHTML and @@ -1603,7 +1614,7 @@ SERVER_BASED_SEARCH = NO # # Doxygen ships with an example indexer (doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library -# Xapian (see: https://xapian.org/). +# Xapian (see: http://xapian.org/). # # See the section "External Indexing and Searching" for details. # The default value is: NO. @@ -1616,11 +1627,11 @@ EXTERNAL_SEARCH = NO # # Doxygen ships with an example indexer (doxyindexer) and search engine # (doxysearch.cgi) which are based on the open source search engine library -# Xapian (see: https://xapian.org/). See the section "External Indexing and +# Xapian (see: http://xapian.org/). See the section "External Indexing and # Searching" for details. # This tag requires that the tag SEARCHENGINE is set to YES. -SEARCHENGINE_URL = +SEARCHENGINE_URL = # When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed # search data is written to a file for indexing by an external tool. With the @@ -1636,7 +1647,7 @@ SEARCHDATA_FILE = searchdata.xml # projects and redirect the results back to the right project. # This tag requires that the tag SEARCHENGINE is set to YES. -EXTERNAL_SEARCH_ID = +EXTERNAL_SEARCH_ID = # The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen # projects other than the one defined by this configuration file, but that are @@ -1646,7 +1657,7 @@ EXTERNAL_SEARCH_ID = # EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ... # This tag requires that the tag SEARCHENGINE is set to YES. -EXTRA_SEARCH_MAPPINGS = +EXTRA_SEARCH_MAPPINGS = #--------------------------------------------------------------------------- # Configuration options related to the LaTeX output @@ -1710,7 +1721,7 @@ PAPER_TYPE = a4 # If left blank no extra packages will be included. # This tag requires that the tag GENERATE_LATEX is set to YES. -EXTRA_PACKAGES = +EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for the # generated LaTeX document. The header should contain everything until the first @@ -1726,7 +1737,7 @@ EXTRA_PACKAGES = # to HTML_HEADER. # This tag requires that the tag GENERATE_LATEX is set to YES. -LATEX_HEADER = +LATEX_HEADER = # The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the # generated LaTeX document. The footer should contain everything after the last @@ -1737,7 +1748,7 @@ LATEX_HEADER = # Note: Only use a user-defined footer if you know what you are doing! # This tag requires that the tag GENERATE_LATEX is set to YES. -LATEX_FOOTER = +LATEX_FOOTER = # The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined # LaTeX style sheets that are included after the standard style sheets created @@ -1748,7 +1759,7 @@ LATEX_FOOTER = # list). # This tag requires that the tag GENERATE_LATEX is set to YES. -LATEX_EXTRA_STYLESHEET = +LATEX_EXTRA_STYLESHEET = # The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the LATEX_OUTPUT output @@ -1756,7 +1767,7 @@ LATEX_EXTRA_STYLESHEET = # markers available. # This tag requires that the tag GENERATE_LATEX is set to YES. -LATEX_EXTRA_FILES = +LATEX_EXTRA_FILES = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is # prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will @@ -1803,7 +1814,7 @@ LATEX_SOURCE_CODE = NO # The LATEX_BIB_STYLE tag can be used to specify the style to use for the # bibliography, e.g. plainnat, or ieeetr. See -# https://en.wikipedia.org/wiki/BibTeX and \cite for more info. +# http://en.wikipedia.org/wiki/BibTeX and \cite for more info. # The default value is: plain. # This tag requires that the tag GENERATE_LATEX is set to YES. @@ -1864,14 +1875,14 @@ RTF_HYPERLINKS = NO # default style sheet that doxygen normally uses. # This tag requires that the tag GENERATE_RTF is set to YES. -RTF_STYLESHEET_FILE = +RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an RTF document. Syntax is # similar to doxygen's config file. A template extensions file can be generated # using doxygen -e rtf extensionFile. # This tag requires that the tag GENERATE_RTF is set to YES. -RTF_EXTENSIONS_FILE = +RTF_EXTENSIONS_FILE = # If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code # with syntax highlighting in the RTF output. @@ -1916,7 +1927,7 @@ MAN_EXTENSION = .3 # MAN_EXTENSION with the initial . removed. # This tag requires that the tag GENERATE_MAN is set to YES. -MAN_SUBDIR = +MAN_SUBDIR = # If the MAN_LINKS tag is set to YES and doxygen generates man output, then it # will generate one additional man file for each entity documented in the real @@ -1986,9 +1997,9 @@ DOCBOOK_PROGRAMLISTING = NO #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an -# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures -# the structure of the code including all documentation. Note that this feature -# is still experimental and incomplete at the moment. +# AutoGen Definitions (see http://autogen.sf.net) file that captures the +# structure of the code including all documentation. Note that this feature is +# still experimental and incomplete at the moment. # The default value is: NO. GENERATE_AUTOGEN_DEF = NO @@ -2029,7 +2040,7 @@ PERLMOD_PRETTY = YES # overwrite each other's variables. # This tag requires that the tag GENERATE_PERLMOD is set to YES. -PERLMOD_MAKEVAR_PREFIX = +PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor @@ -2070,7 +2081,7 @@ SEARCH_INCLUDES = YES # preprocessor. # This tag requires that the tag SEARCH_INCLUDES is set to YES. -INCLUDE_PATH = +INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the @@ -2078,7 +2089,7 @@ INCLUDE_PATH = # used. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. -INCLUDE_FILE_PATTERNS = +INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that are # defined before the preprocessor is started (similar to the -D option of e.g. @@ -2088,7 +2099,7 @@ INCLUDE_FILE_PATTERNS = # recursively expanded use the := operator instead of the = operator. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. -PREDEFINED = +PREDEFINED = # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this # tag can be used to specify a list of macro names that should be expanded. The @@ -2097,7 +2108,7 @@ PREDEFINED = # definition found in the source code. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. -EXPAND_AS_DEFINED = +EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will # remove all references to function-like macros that are alone on a line, have @@ -2126,13 +2137,13 @@ SKIP_FUNCTION_MACROS = YES # the path). If a tag file is not located in the directory in which doxygen is # run, you must also specify the path to the tagfile here. -TAGFILES = +TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create a # tag file that is based on the input files it reads. See section "Linking to # external documentation" for more information about the usage of tag files. -GENERATE_TAGFILE = +GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES, all external class will be listed in # the class index. If set to NO, only the inherited external classes will be @@ -2172,7 +2183,7 @@ PERL_PATH = /usr/bin/perl # powerful graphs. # The default value is: YES. -CLASS_DIAGRAMS = YES +CLASS_DIAGRAMS = NO # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see: @@ -2181,14 +2192,14 @@ CLASS_DIAGRAMS = YES # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. -MSCGEN_PATH = +MSCGEN_PATH = # You can include diagrams made with dia in doxygen documentation. Doxygen will # then run dia to produce the diagram and insert it in the documentation. The # DIA_PATH tag allows you to specify the directory where the dia binary resides. # If left empty dia is assumed to be found in the default search path. -DIA_PATH = +DIA_PATH = # If set to YES the inheritance and collaboration graphs will hide inheritance # and usage relations if the target is undocumented or is not a class. @@ -2201,9 +2212,9 @@ HIDE_UNDOC_RELATIONS = YES # http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent # Bell Labs. The other options in this section have no effect if this option is # set to NO -# The default value is: NO. +# The default value is: YES. -HAVE_DOT = NO +HAVE_DOT = YES # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed # to run in parallel. When set to 0 doxygen will base this on the number of @@ -2237,7 +2248,7 @@ DOT_FONTSIZE = 10 # the path where dot can find it using this tag. # This tag requires that the tag HAVE_DOT is set to YES. -DOT_FONTPATH = +DOT_FONTPATH = # If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for # each documented class showing the direct and indirect inheritance relations. @@ -2320,7 +2331,7 @@ INCLUDED_BY_GRAPH = YES # The default value is: NO. # This tag requires that the tag HAVE_DOT is set to YES. -CALL_GRAPH = NO +CALL_GRAPH = YES # If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller # dependency graph for every global function or class method. @@ -2332,7 +2343,7 @@ CALL_GRAPH = NO # The default value is: NO. # This tag requires that the tag HAVE_DOT is set to YES. -CALLER_GRAPH = NO +CALLER_GRAPH = YES # If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical # hierarchy of all classes instead of a textual one. @@ -2357,7 +2368,9 @@ DIRECTORY_GRAPH = YES # Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order # to make the SVG files visible in IE 9+ (other browsers do not have this # requirement). -# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo, +# Possible values are: png, png:cairo, png:cairo:cairo, png:cairo:gd, png:gd, +# png:gd:gd, jpg, jpg:cairo, jpg:cairo:gd, jpg:gd, jpg:gd:gd, gif, gif:cairo, +# gif:cairo:gd, gif:gd, gif:gd:gd, svg, png:gd, png:gd:gd, png:cairo, # png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and # png:gdiplus:gdiplus. # The default value is: png. @@ -2375,32 +2388,32 @@ DOT_IMAGE_FORMAT = png # The default value is: NO. # This tag requires that the tag HAVE_DOT is set to YES. -INTERACTIVE_SVG = YES +INTERACTIVE_SVG = NO # The DOT_PATH tag can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. # This tag requires that the tag HAVE_DOT is set to YES. -DOT_PATH = +DOT_PATH = # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the \dotfile # command). # This tag requires that the tag HAVE_DOT is set to YES. -DOTFILE_DIRS = +DOTFILE_DIRS = # The MSCFILE_DIRS tag can be used to specify one or more directories that # contain msc files that are included in the documentation (see the \mscfile # command). -MSCFILE_DIRS = +MSCFILE_DIRS = # The DIAFILE_DIRS tag can be used to specify one or more directories that # contain dia files that are included in the documentation (see the \diafile # command). -DIAFILE_DIRS = +DIAFILE_DIRS = # When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the # path where java can find the plantuml.jar file. If left blank, it is assumed @@ -2408,17 +2421,17 @@ DIAFILE_DIRS = # generate a warning when it encounters a \startuml command in this case and # will not generate output for the diagram. -PLANTUML_JAR_PATH = +PLANTUML_JAR_PATH = # When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a # configuration file for plantuml. -PLANTUML_CFG_FILE = +PLANTUML_CFG_FILE = # When using plantuml, the specified paths are searched for files specified by # the !include statement in a plantuml block. -PLANTUML_INCLUDE_PATH = +PLANTUML_INCLUDE_PATH = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes # that will be shown in the graph. If the number of nodes in a graph becomes diff --git a/FindBoost.cmake b/FindBoost.cmake deleted file mode 100644 index 6158c7c0839c48f7e6d21c7f95bd0b57ce63ba8e..0000000000000000000000000000000000000000 --- a/FindBoost.cmake +++ /dev/null @@ -1,184 +0,0 @@ -# This file is NOT an original FindBoost.cmake module provided by KitWare! -# -# It's a simplified version whose only purpose is to be used in a software -# library lib4neuro and it does NOT provide a full funcionality of the original, -# as it only works with the system-layout-named libraries (e.g. libboost_system.so). - -# Optional user-defined variables -# (can be passed directly to CMake or exported as environmental variables -# -# * BOOST_LIBRARYDIR - The path to the folder containing Boost libraries -# * BOOST_INCLUDEDIR - The path to the folder containing Boost header files - -# "External" used variables -# -# * DEPENDENCIES_LINK_TYPE - TODO - -# "Output" variables set after running this script -# * Boost_FOUND - TODO -# * Boost_INCLUDE_DIRS - TODO -# * Boost_LIBRARY_DIRS - TODO -# * Boost_LIBRARIES - TODO - -# Module usage -# TODO - -message("FindBoost starting...") - -# Check if needed Boost components were specified -if(NOT Boost_FIND_COMPONENTS) - message(FATAL_ERROR "No Boost components were specified! Please, set them correctly with flag COMPONENTS (see Module Usage section in this script).") -else() - message("Required Boost components: ${Boost_FIND_COMPONENTS}") -endif() - -# Look for a standard boost header file. -set(Boost_INCLUDE_DIRS "Boost_INCLUDE_DIRS-NOTFOUND") -find_path( - Boost_INCLUDE_DIRS - - NAMES - config.hpp - - HINTS - ${BOOST_INCLUDEDIR} - $ENV{BOOST_INCLUDEDIR} - ${CMAKE_CURRENT_LIST_DIR}/external_dependencies/boost - - PATHS - /usr/include - - PATH_SUFFIXES - boost - include -) - -# Add path without "boost" include sub-directory to include path, -# as Boost headers are supposed to be included like -# #include<boost/...> according to the documentation -set(TMP "") -#if(WIN32) -# string(REPLACE "\\boost\\boost" "\\boost" TMP ${Boost_INCLUDE_DIRS}) -# list(APPEND Boost_INCLUDE_DIRS ${TMP}) -#else() - string(REPLACE "/boost/boost" "/boost" TMP ${Boost_INCLUDE_DIRS}) - list(APPEND Boost_INCLUDE_DIRS ${TMP}) -#endif() - -message("Boost_INCLUDE_DIRS: ${Boost_INCLUDE_DIRS}") - -if(NOT Boost_INCLUDE_DIRS) - message(FATAL_ERROR "Boost include directory was not found! Please, set variable BOOST_INCLUDEDIR to the correct path.") -endif() - -# Create a list of requested Boost libraries with "system" names -if(NOT DEPENDENCIES_LINK_TYPE) - message(FATAL_ERROR "Variable DEPENDENCIES_LINK_TYPE is not set! Set it to 'static' or 'shared'.") -endif() - -set(LIB_SUFFIX "a") # suffix for Linux static libraries -if("${DEPENDENCIES_LINK_TYPE}" STREQUAL "shared" AND WIN32) - set(LIB_SUFFIX "dll") -elseif("${DEPENDENCIES_LINK_TYPE}" STREQUAL "static" AND WIN32) - set(LIB_SUFFIX "lib") -elseif("${DEPENDENCIES_LINK_TYPE}" STREQUAL "shared") - set(LIB_SUFFIX "so") -endif() - -set(LIB_PREFIX "lib") -#if(WIN32) -# set(LIB_PREFIX "") -#endif() - -set(REQUESTED_BOOST_LIBS "") -foreach(COMPONENT ${Boost_FIND_COMPONENTS}) - list(APPEND REQUESTED_BOOST_LIBS "${LIB_PREFIX}boost_${COMPONENT}.${LIB_SUFFIX}") -endforeach() - -# Look for libraries specified by COMPONENTS flag -set(Boost_LIBRARY_DIRS "Boost_LIBRARY_DIRS-NOTFOUND") -message("$ENV{BOOST_LIBRARYDIR}") -find_path( - Boost_LIBRARY_DIRS - - NAMES - ${REQUESTED_BOOST_LIBS} - - HINTS - ${BOOST_LIBRARYDIR} - $ENV{BOOST_LIBRARYDIR} - ${CMAKE_CURRENT_LIST_DIR}/external_dependencies/boost - ${CMAKE_CURRENT_LIST_DIR}/external_dependencies/boost/stage - ${CMAKE_CURRENT_LIST_DIR}/external_dependencies/boost/stage/lib - - - PATHS - /usr/lib/boost - /usr/lib/x86_64-linux-gnu - - PATH_SUFFIXES - lib -) - -if(NOT Boost_LIBRARY_DIRS) - message(FATAL_ERROR "Boost library directory was not found! Please, set variable BOOST_LIBRARYDIR to the correct path.") -endif() - - # Construct list of libraries' names and make them -# targets, so they may be linked -set(Boost_LIBRARIES "") -foreach(LIBNAME ${REQUESTED_BOOST_LIBS}) - message("Looking for ${LIBNAME}...") - - set(${LIBNAME} "${LIBNAME}-NOTFOUND") - find_library( - ${LIBNAME} - - NAMES - ${LIBNAME} - - PATHS - ${Boost_LIBRARY_DIRS} - - PATH_SUFFIXES - stage/lib - lib - - NO_DEFAULT_PATH - ) - - message("${LIBNAME} ${${LIBNAME}}") - - # Check, if the Boost component was found - if("${${LIBNAME}}" STREQUAL "${LIBNAME}-NOTFOUND") - message(FATAL_ERROR "Boost library ${LIBNAME} was NOT found!\ - Please, set variable BOOST_LIBRARYDIR to the correct path and check the library names\ - format in your Boost installation.") - else() - message("${LIBNAME} was found: ${${LIBNAME}}") - endif() - - list(APPEND Boost_LIBRARIES ${${LIBNAME}}) -endforeach() - - -if(NOT Boost_LIBRARY_DIRS) - message(FATAL_ERROR "Boost library directory was not found! Please, set variable BOOST_LIBRARYDIR to the correct path.") -endif() - -# Set Boost_FOUND -INCLUDE(FindPackageHandleStandardArgs) -FIND_PACKAGE_HANDLE_STANDARD_ARGS( - Boost - - FAIL_MESSAGE - "Boost was NOT found!" - - REQUIRED_VARS - Boost_INCLUDE_DIRS - Boost_LIBRARY_DIRS -) - -message("Boost_INCLUDE_DIRS: ${Boost_INCLUDE_DIRS}") -message("Boost_LIBRARY_DIRS: ${Boost_LIBRARY_DIRS}") -message("Boost_LIBRARIES: ${Boost_LIBRARIES}") diff --git a/build.bat b/build.bat index 92ef4a7d97759412ddf9eaf360bdca490ad833d9..464dd89820982cd13f1507d8b6c6193a1a90ff44 100644 --- a/build.bat +++ b/build.bat @@ -1,34 +1,10 @@ @echo off -rem call VsDevCmd.bat +cls -title Build lib4neuro project +del CMakeCache.txt -set DEPENDENCIES_LINK_TYPE=static - -rem Build type (Release/Debug) -set BUILD_TYPE=Debug -rem Should we rebuild BOOST? (yes/no) -set REBUILD_BOOST=yes - -rem Should we build the examples? (yes/no) -set BUILD_EXAMPLES=yes - -rem Should we build the unit-tests? (yes/no) -set BUILD_TESTS=yes +set CLEAN_AFTER=no -rem Should we build the lib4neuro library? (yes) -set BUILD_LIB=yes - -rem C++ compiler -set CXX_COMPILER=cl -set C_COMPILER=cl - -rem Makefile generator -rem For the complete list type "cmake --help" -rem Example: "MSYS Makefiles", "MinGW Makefiles", "NMake Makefiles" -set MAKEFILE_GENERATOR="Visual Studio 15 2017 Win64" - -call clean.bat -cmake -G %MAKEFILE_GENERATOR% -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON -DCMAKE_BUILD_TYPE=%BUILD_TYPE% -DCMAKE_CXX_COMPILER=%CXX_COMPILER% -DCMAKE_C_COMPILER=%C_COMPILER% -DBOOST_ROOT=%BOOST_ROOT% -DBOOST_LIBRARYDIR=%BOOST_LIBRARYDIR% -DBOOST_INCLUDEDIR=%BOOST_INCLUDEDIR% -DBUILD_TESTS=%BUILD_TESTS% -DBUILD_EXAMPLES=%BUILD_EXAMPLES% -DBUILD_LIB=%BUILD_LIB% -DLIB4NEURO_DIR=build\lib . -cmake --build . --config %BUILD_TYPE% && (echo (Build complete.); echo (For examples have a look at the folder build/examples.)) || (echo "Build finished with errors!") +set DEPENDENCIES_LINK_TYPE=static +call build_scripts\windows\win_VS_build_x64_release.bat diff --git a/build.sh b/build.sh index cf78459333c4084d32605ca6f30adf5760fa44ac..4bdebc439a872593f8864841b9bef480839a3b16 100755 --- a/build.sh +++ b/build.sh @@ -1,36 +1,9 @@ #!/bin/sh -#------------#------------------------------------------------------------ -# Parameters # -#------------# +export CLEAN_AFTER=no -# -# Modular build parameters -# -BUILD_TESTS=yes -BUILD_EXAMPLES=yes -BUILD_LIB=yes -DEPENDENCIES_LINK_TYPE=shared # shared/static +rm -f build/CMakeCache.txt -# Build type (Release/Debug) -BUILD_TYPE=Debug - -# C++ compiler -CXX_COMPILER=g++ -C_COMPILER=gcc - -if [ -z "$BUILD_TYPE" ] || [ -z "$CXX_COMPILER" ]; then - (>&2 echo "Set, please, both BUILD_TYPE and CXX_COMPILER variables in the 'build.sh' script.") - exit 2 -fi - -$(pwd)/clean.sh - -# -# For ExprTk download -# -git submodule init -git submodule update --remote - -cmake -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DCMAKE_CXX_COMPILER=${CXX_COMPILER} -DCMAKE_C_COMPILER=${C_COMPILER} -DBUILD_TESTS=${BUILD_TESTS} -DBUILD_EXAMPLES=${BUILD_EXAMPLES} -DBUILD_LIB=${BUILD_LIB} -DLIB4NEURO_DIR=$(pwd)/build/lib -DDEPENDENCIES_LINK_TYPE=${DEPENDENCIES_LINK_TYPE} . -cmake --build . --config ${BUILD_TYPE} -- -j${N_CORES} && (tput setaf 2; echo "Build complete."; echo "For examples have a look at the folder build/bin/examples."; tput sgr 0; ) || (tput setaf 1; echo "Build finished with errors!"; tput sgr 0; exit 1;) +export DEPENDENCIES_LINK_TYPE=static +build_scripts/linux/linux_gcc_build_x64_debug_local.sh +cd ../.. diff --git a/build_docs.sh b/build_docs.sh old mode 100755 new mode 100644 diff --git a/build_scripts/compile_anselm.sh b/build_scripts/compile_anselm.sh new file mode 100644 index 0000000000000000000000000000000000000000..8706619f9e62dd0d65b7be4eb4895aca120c659c --- /dev/null +++ b/build_scripts/compile_anselm.sh @@ -0,0 +1,12 @@ +#!/usr/bin/sh + +ml CMake/3.13.1 +ml GCC/7.1.0-2.28 +ml Boost +ml Armadillo +ml OpenBLAS +ml ScaLAPACK + +./clean.sh + +./build.sh \ No newline at end of file diff --git a/build_scripts/linux/build_boost_gcc.sh b/build_scripts/linux/build_boost_gcc.sh index 54953b558130b0d130f960cc20b6e737c99e3502..7f1cc6181cb8bd161f4e28c48c7eeced5840c591 100755 --- a/build_scripts/linux/build_boost_gcc.sh +++ b/build_scripts/linux/build_boost_gcc.sh @@ -1,14 +1,17 @@ -#!/bin/bash +#!/bin/sh +RED='\033[0;31m' CYAN='\033[0;36m' +YELLOW='\033[1;33m' +GREEN='\033[0;32m' +WHITE='\033[1;37m' NC='\033[0m' # No Color -echo "Building '${CYAN}BOOST${NC}' for ${WHITE}Debug${NC}" -cd ../.. - -cd external_dependencies/boost +echo "Building '${CYAN}BOOST${NC}' for ${WHITE}Debug${NC}(${LINK_TYPE})" + +cd ../../external_dependencies/boost -./b2 cxxflags="-fPIC" --layout=system variant=debug link=${DEPENDENCIES_LINK_TYPE} address-model=64 --with-system --with-serialization --with-random +./b2 -q cxxflags="-fPIC" --layout=system variant=debug link=${LINK_TYPE} address-model=64 --with-system --with-serialization --with-random cd ../../build_scripts/linux diff --git a/build_scripts/linux/clean_dependencies.sh b/build_scripts/linux/clean_dependencies.sh index 3b4ead63bb41002d099ab633e7c62349c5ee8cb7..1bbae3c794a4ee4ddee92fab1fcbcce8b7385abe 100755 --- a/build_scripts/linux/clean_dependencies.sh +++ b/build_scripts/linux/clean_dependencies.sh @@ -1,4 +1,5 @@ #!/bin/sh -rm ../../external_dependencies/boost/* -rm ../../external_dependencies/exprtk/* +rm -rf ../../external_dependencies/boost/* +rm -rf ../../external_dependencies/exprtk/* +rm -rf ../../external_dependencies/turtle/* diff --git a/build_scripts/linux/download_dependencies.sh b/build_scripts/linux/download_dependencies.sh index a6d938d1641b74bca747507ed357b5c17548a3f5..1723af15d7c6e210e2e108100759c2d5dbd7663e 100755 --- a/build_scripts/linux/download_dependencies.sh +++ b/build_scripts/linux/download_dependencies.sh @@ -31,5 +31,5 @@ then cd ../../build_scripts/linux - echo "${GREEN}External dependencies download \& bootstrapping finished${NC}" + echo "${GREEN}External dependencies download & bootstrapping finished${NC}" fi diff --git a/build_scripts/linux/linux_clean_after_examples.sh b/build_scripts/linux/linux_clean_after_examples.sh new file mode 100755 index 0000000000000000000000000000000000000000..20b13b9999e339dbc10a03a5ec4d27af77074885 --- /dev/null +++ b/build_scripts/linux/linux_clean_after_examples.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +cd ../../build + +rm -rf CMakeFiles +rm -r *.* +rm -r Makefile +rm -rf examples + +cd ../build_scripts/linux \ No newline at end of file diff --git a/build_scripts/linux/linux_clean_after_lib.sh b/build_scripts/linux/linux_clean_after_lib.sh new file mode 100755 index 0000000000000000000000000000000000000000..006f13587e0c5a478c6f3f9d6244065ecc6275e4 --- /dev/null +++ b/build_scripts/linux/linux_clean_after_lib.sh @@ -0,0 +1,8 @@ +#!/bin/sh +cd ../../build + +rm -rf CMakeFiles +rm -rf *.* +rm -rf Makefile + +cd ../build_scripts/linux diff --git a/build_scripts/linux/linux_clean_after_tests.sh b/build_scripts/linux/linux_clean_after_tests.sh new file mode 100755 index 0000000000000000000000000000000000000000..7e3fa6534a396a856b721777e860199ea6afeaf0 --- /dev/null +++ b/build_scripts/linux/linux_clean_after_tests.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +cd ../../build + +rm -rf CMakeFiles +rm -f *.* +rm -f Makefile + +cd unit-tests + + rm -rf CMakeFiles + rm -f cmake_install.cmake + rm -f Makefile + +cd .. + +cd ../build_scripts/linux diff --git a/build_scripts/linux/linux_clean_examples.sh b/build_scripts/linux/linux_clean_examples.sh index d3de032b8e0ad0153d54e23950ae095ed0b97c6f..84705c3ba09c579bff120cccb83faa73fc30f958 100755 --- a/build_scripts/linux/linux_clean_examples.sh +++ b/build_scripts/linux/linux_clean_examples.sh @@ -2,6 +2,6 @@ cd ../.. -rm -r build/examples +rm -rf build/bin/examples cd build_scripts/linux \ No newline at end of file diff --git a/build_scripts/linux/linux_clean_garbage.sh b/build_scripts/linux/linux_clean_garbage.sh index aaf60d5345630d0d85928cf7703c2b21ac119af9..5081bb42e60f61403eefc72f2d83562009202368 100755 --- a/build_scripts/linux/linux_clean_garbage.sh +++ b/build_scripts/linux/linux_clean_garbage.sh @@ -2,6 +2,9 @@ cd ../.. +rm -rf *.cbp +rm -rf *.log + rm -rf Makefile rm -rf docs/* rm -f src/*TestRunner* @@ -9,12 +12,13 @@ rm -f src/*.o src/*.mod rm -f src/funit.tmp src/*_fun.f90 rm -f CMakeCache.txt rm -f cmake_install.cmake src/cmake_install.cmake -rm -rf CMakeFiles src/CMakeFiles src/examples/CMakeFiles src/tests/CMakeFiles build/CMakeFiles build/examples/CMakeFiles build/unit-tests/CMakeFiles -rm -f build/Makefile build/examples/Makefile build/unit-tests/Makefile build/cmake_install.cmake build/examples/cmake_install.cmake build/unit-tests/cmake_install.cmake +rm -rf CMakeFiles src/CMakeFiles src/examples/CMakeFiles src/tests/CMakeFiles + +#build/CMakeFiles build/examples/CMakeFiles build/unit-tests/CMakeFiles +#rm -f build/Makefile build/examples/Makefile build/unit-tests/Makefile build/cmake_install.cmake build/examples/cmake_install.cmake build/unit-tests/cmake_install.cmake #mv build/examples/bin/* build/examples #mv build/unit-tests/bin/* build/unit-tests - -rm -rf build/examples/bin build/unit-tests/bin +#rm -rf build/examples/bin build/unit-tests/bin cd build_scripts/linux diff --git a/build_scripts/linux/linux_clean_lib.sh b/build_scripts/linux/linux_clean_lib.sh index 64955a285e23a8677561f18547a95a0eaec7c139..3db10e3d043a14f83ee984b60b078e47e59fea73 100755 --- a/build_scripts/linux/linux_clean_lib.sh +++ b/build_scripts/linux/linux_clean_lib.sh @@ -2,6 +2,6 @@ cd ../.. -rm -r build/lib +rm -rf build/lib cd build_scripts/linux \ No newline at end of file diff --git a/build_scripts/linux/linux_gcc_build_x64_debug_local.sh b/build_scripts/linux/linux_gcc_build_x64_debug_local.sh index 7af32a96e1023cf369809daad0ea1d36e2a213b9..48f8f7a1585a4d5640c6f1df22e8decbbadfb079 100755 --- a/build_scripts/linux/linux_gcc_build_x64_debug_local.sh +++ b/build_scripts/linux/linux_gcc_build_x64_debug_local.sh @@ -2,8 +2,9 @@ clear -# Should we rebuild BOOST? (yes/no) -REBUILD_BOOST=yes +export BOOST_ROOT=${PWD}../../external_dependencies/boost +export LIB4NEURO_INCLUDE_DIR=${PWD}/../../include + # Should we build the examples? (yes/no) BUILD_EXAMPLES=yes @@ -11,6 +12,8 @@ BUILD_EXAMPLES=yes # Should we build the unit-tests? (yes/no) BUILD_TESTS=yes +# Should we rebuild BOOST? (yes/no) +#REBUILD_BOOST=yes # Should we build the lib4neuro library? (yes) BUILD_LIB=yes @@ -19,87 +22,109 @@ CXX_COMPILER="g++" C_COMPILER="gcc" #**********************DO NOT CHANGE BEYOND THIS LINE**************************************** +if [ -z ${DEPENDENCIES_LINK_TYPE} ]; then DEPENDENCIES_LINK_TYPE=static; fi +if [ -z ${CLEAN_AFTER} ]; then CLEAN_AFTER=yes; fi + +export LINK_TYPE=static +if [ ${DEPENDENCIES_LINK_TYPE} = "shared" ] +then + export LINK_TYPE=shared +fi + +CLEAN_AF=yes +if [ ${CLEAN_AFTER} = "no" ] +then + CLEAN_AF=no +fi + + +BUILD_ERROR_OCCURED=0 RED='\033[0;31m' CYAN='\033[0;36m' YELLOW='\033[1;33m' GREEN='\033[0;32m' WHITE='\033[1;37m' -NC='\033[0m' # No Color -echo "Building the '${CYAN}lib4neuro${NC}' project for ${WHITE}Debug${NC}" +NC='\033[m' # No Color +echo -e "Building the ${CYAN}lib4neuro${NC} project for ${WHITE}Debug${NC}" BUILD_SOMETHING=no BUILD_SOMETHING_LIB=no -if [ $BUILD_LIB = "yes" ] +if [ ${BUILD_LIB} = "yes" ] then - echo "${CYAN}lib4neuro${NC} build type: ${WHITE}Debug${NC}" - echo "${CYAN}lib4neuro${NC} build architecture: ${WHITE}x64${NC}" - echo "${CYAN}lib4neuro${NC} will be built in '${YELLOW}build/lib/${NC}'" + echo -e "${CYAN}lib4neuro${NC} build type: ${WHITE}Debug${NC}" + echo -e "${CYAN}lib4neuro${NC} build architecture: ${WHITE}x64${NC}" + echo -e "${CYAN}lib4neuro${NC} will be built in '${YELLOW}build/lib/${NC}'" BUILD_SOMETHING=yes BUILD_SOMETHING_LIB=yes fi if [ $BUILD_TESTS = "yes" ] then - echo "${CYAN}lib4neuro${NC} unit tests will be built in '${YELLOW}build/unit-tests${NC}'" + echo -e "${CYAN}lib4neuro${NC} unit tests will be built in '${YELLOW}build/unit-tests${NC}'" BUILD_SOMETHING=yes BUILD_SOMETHING_LIB=yes fi -if [ $BUILD_EXAMPLES = "yes" ] +if [ ${BUILD_EXAMPLES} = "yes" ] then - echo "${CYAN}lib4neuro${NC} examples will be built in '${YELLOW}build/examples${NC}'" - BUILD_SOMETHING=yes - BUILD_SOMETHING_LIB=yes -fi - -if [ $REBUILD_BOOST = "yes" ] -then - echo "The required '${CYAN}BOOST${NC}' library will be recompiled in the directory '${YELLOW}external_dependencies/boost${NC}'" - rm -rf ../../external_dependencies/boost/stage/lib/* + echo -e "${CYAN}lib4neuro${NC} examples will be built in '${YELLOW}build/examples${NC}'" BUILD_SOMETHING=yes BUILD_SOMETHING_LIB=yes fi +#if [ ${REBUILD_BOOST} = "yes" ] +#then +# echo -e "The required '${CYAN}BOOST${NC}' library will be recompiled in the directory '${YELLOW}external_dependencies/boost${NC}'" +# rm -rf ../../external_dependencies/boost/stage +# rm -rf ../../external_dependencies/boost/bin.v2 +# BUILD_SOMETHING=yes +#fi # Boost rebuild -if [ $REBUILD_BOOST = "yes" ] -then - ./build_boost_gcc.sh -fi +#if [ ${REBUILD_BOOST} = "yes" ] +#then +# ./build_boost_gcc.sh || BUILD_ERROR_OCCURED=1 +#fi -if [ $BUILD_SOMETHING_LIB = "yes" ] +# Should we build the lib4neuro library? (yes) +if [ ${BUILD_SOMETHING_LIB} = "yes" -a $BUILD_ERROR_OCCURED = "0" ] then - if [ $BUILD_LIB = "yes" ] + if [ ${BUILD_LIB} = "yes" ] then ./linux_clean_lib.sh fi - if [ $BUILD_EXAMPLES = "yes" ] + if [ ${BUILD_EXAMPLES} = "yes" ] then ./linux_clean_examples.sh fi - if [ $BUILD_TESTS = "yes" ] + if [ ${BUILD_TESTS} = "yes" ] then ./linux_clean_tests.sh - fi + fi + + echo -e "Building the '${CYAN}lib4neuro${NC}' project for ${WHITE}Debug${NC} (preparing makefiles)" - echo "Building the '${CYAN}lib4neuro${NC}' project for ${WHITE}Debug${NC} (preparing makefiles)" - + cmake -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_COMPILER=${CXX_COMPILER} -DCMAKE_C_COMPILER=${C_COMPILER} -DBOOST_LIBRARYDIR=${BOOST_LIBRARYDIR} -DBOOST_INCLUDEDIR=${BOOST_INCLUDEDIR} -DBUILD_TESTS=${BUILD_TESTS} -DBUILD_EXAMPLES=${BUILD_EXAMPLES} -DBUILD_LIB=${BUILD_LIB} -DDEPENDENCIES_LINK_TYPE=${LINK_TYPE} -S . -B build || { echo -e "${RED}Makefile preparation finished with errors${NC}!"; BUILD_ERROR_OCCURED=1; } + if [ ${BUILD_ERROR_OCCURED} = "0" ] + then + echo -e "Building the '${CYAN}lib4neuro${NC}' project for ${WHITE}Debug${NC} (building)" - cd ../.. + . $(pwd)/set_env_n_cores - cmake -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_COMPILER=${CXX_COMPILER} -DCMAKE_C_COMPILER=${C_COMPILER} -DBOOST_LIBRARYDIR=${BOOST_LIBRARYDIR} -DBOOST_INCLUDEDIR=${BOOST_INCLUDEDIR} -DBUILD_TESTS=${BUILD_TESTS} -DBUILD_EXAMPLES=${BUILD_EXAMPLES} -DBUILD_LIB=${BUILD_LIB} -DLIB4NEURO_DIR=${PWD}/build/lib -DDEPENDENCIES_LINK_TYPE=${DEPENDENCIES_LINK_TYPE} . - - echo "Building the '${CYAN}lib4neuro${NC}' project for ${WHITE}Debug${NC} (building)" - ( cmake --build . --config Debug -- -j${N_CORES} ) && ( echo "${GREEN}Build complete${NC}." ) || ( echo "${RED}Build finished with errors${NC}!"; exit 1; ) + cmake --build build --config Debug -j ${N_CORES} && echo -e "${GREEN}Build complete${NC}." || { echo -e "${RED}Build finished with errors${NC}!"; BUILD_ERROR_OCCURED=1; } + fi - cd build_scripts/linux - ./linux_clean_garbage.sh fi +if [ ${BUILD_ERROR_OCCURED} = "1" ] +then + echo -e "${RED}Build encountered some errors!${NC}" + exit 1 +fi diff --git a/build_scripts/linux/linux_gcc_build_x64_debug_system.sh b/build_scripts/linux/linux_gcc_build_x64_debug_system.sh index 20f7c02a5ecad62972d3cff694ec79de6e3ccddc..144ecb2ca44afbf778106ac6fd6af4b13105514b 100755 --- a/build_scripts/linux/linux_gcc_build_x64_debug_system.sh +++ b/build_scripts/linux/linux_gcc_build_x64_debug_system.sh @@ -2,8 +2,9 @@ clear -# Should we rebuild BOOST? (yes/no) -REBUILD_BOOST=no +export BOOST_ROOT=${PWD}../../external_dependencies/boost +export LIB4NEURO_INCLUDE_DIR=${PWD}/../../include + # Should we build the examples? (yes/no) BUILD_EXAMPLES=yes @@ -19,6 +20,22 @@ CXX_COMPILER="g++" C_COMPILER="gcc" #**********************DO NOT CHANGE BEYOND THIS LINE**************************************** +if [ -z ${DEPENDENCIES_LINK_TYPE} ]; then DEPENDENCIES_LINK_TYPE=static; fi +if [ -z ${CLEAN_AFTER} ]; then CLEAN_AFTER=yes; fi + +LINK_TYPE=static +if [ ${DEPENDENCIES_LINK_TYPE} = "shared" ] +then + LINK_TYPE=shared +fi + +CLEAN_AF=yes +if [ ${CLEAN_AFTER} = "no" ] +then + CLEAN_AF=no +fi + +BUILD_ERROR_OCCURED=0 RED='\033[0;31m' CYAN='\033[0;36m' @@ -26,15 +43,13 @@ YELLOW='\033[1;33m' GREEN='\033[0;32m' WHITE='\033[1;37m' NC='\033[0m' # No Color - - -echo "Building the '${CYAN}lib4neuro${NC}' project for ${WHITE}Debug${NC}" +echo -e "Building the '${CYAN}lib4neuro${NC}' project for ${WHITE}Debug${NC}" BUILD_SOMETHING=no BUILD_SOMETHING_LIB=no -if [ $BUILD_LIB = "yes" ] +if [ ${BUILD_LIB} = "yes" ] then echo "${CYAN}lib4neuro${NC} build type: ${WHITE}Debug${NC}" echo "${CYAN}lib4neuro${NC} build architecture: ${WHITE}x64${NC}" @@ -43,65 +58,56 @@ then BUILD_SOMETHING_LIB=yes fi -if [ $BUILD_TESTS = "yes" ] -then - echo "${CYAN}lib4neuro${NC} unit tests will be built in '${YELLOW}build/unit-tests${NC}'" - BUILD_SOMETHING=yes - BUILD_SOMETHING_LIB=yes -fi - -if [ $BUILD_EXAMPLES = "yes" ] +if [ ${BUILD_TESTS} = "yes" ] then - echo "${CYAN}lib4neuro${NC} examples will be built in '${YELLOW}build/examples${NC}'" + echo -e "${CYAN}lib4neuro${NC} unit tests will be built in '${YELLOW}build/unit-tests${NC}'" BUILD_SOMETHING=yes BUILD_SOMETHING_LIB=yes fi -if [ $REBUILD_BOOST = "yes" ] +if [ ${BUILD_EXAMPLES} = "yes" ] then - echo "The required '${CYAN}BOOST${NC}' library will be recompiled in the directory '${YELLOW}external_dependencies/boost${NC}'" + echo -e "${CYAN}lib4neuro${NC} examples will be built in '${YELLOW}build/examples${NC}'" BUILD_SOMETHING=yes BUILD_SOMETHING_LIB=yes fi - -# Clean locally stored external dependencies rm -rf ../../external_dependencies/boost/* rm -rf ../../external_dependencies/exprtk/* -if [ $BUILD_SOMETHING_LIB = "yes" ] +# Should we build the lib4neuro library? (yes) +if [ ${BUILD_SOMETHING_LIB} = "yes" -a ${BUILD_ERROR_OCCURED} = "0" ] then - if [ $BUILD_LIB = "yes" ] + if [ ${BUILD_LIB} = "yes" ] then ./linux_clean_lib.sh fi - if [ $BUILD_EXAMPLES = "yes" ] + if [ ${BUILD_EXAMPLES} = "yes" ] then ./linux_clean_examples.sh fi - if [ $BUILD_TESTS = "yes" ] + if [ ${BUILD_TESTS} = "yes" ] then ./linux_clean_tests.sh fi - clear - echo "Building the '${CYAN}lib4neuro${NC}' project for ${WHITE}Debug${NC} (preparing makefiles)" - - - cd ../.. - - cmake -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_COMPILER=${CXX_COMPILER} -DCMAKE_C_COMPILER=${C_COMPILER} -DBOOST_LIBRARYDIR=${BOOST_LIBRARYDIR} -DBOOST_INCLUDEDIR=${BOOST_INCLUDEDIR} -DBUILD_TESTS=${BUILD_TESTS} -DBUILD_EXAMPLES=${BUILD_EXAMPLES} -DBUILD_LIB=${BUILD_LIB} -DLIB4NEURO_DIR=${PWD}/build/lib -DDEPENDENCIES_LINK_TYPE=${DEPENDENCIES_LINK_TYPE} . + echo -e "Building the '${CYAN}lib4neuro${NC}' project for ${WHITE}Debug${NC} (preparing makefiles)" - echo "Building the '${CYAN}lib4neuro${NC}' project for ${WHITE}Debug${NC} (building)" - ( cmake --build . --config Debug -- -j${N_CORES} ) && ( echo "${GREEN}Build complete${NC}." ) || ( echo "${RED}Build finished with errors${NC}!"; exit 1; ) + cmake -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_COMPILER=${CXX_COMPILER} -DCMAKE_C_COMPILER=${C_COMPILER} -DBOOST_LIBRARYDIR=${BOOST_LIBRARYDIR} -DBOOST_INCLUDEDIR=${BOOST_INCLUDEDIR} -DBUILD_TESTS=${BUILD_TESTS} -DBUILD_EXAMPLES=${BUILD_EXAMPLES} -DBUILD_LIB=${BUILD_LIB} -DDEPENDENCIES_LINK_TYPE=${LINK_TYPE} -S . -B build || { echo -e "${RED}Makefile preparation finished with errors${NC}!"; BUILD_ERROR_OCCURED=1; } - cd build_scripts/linux + if [ ${BUILD_ERROR_OCCURED} -eq 0 ] + then + echo -e "Building the '${CYAN}lib4neuro${NC}' project for ${WHITE}Debug${NC} (building)" + cmake --build build --config Debug -- -j ${N_CORES} && echo -e "${GREEN}Build complete${NC}." || { echo -e "${RED}Build finished with errors${NC}!"; BUILD_ERROR_OCCURED=1; } + fi - ./linux_clean_garbage.sh fi - - +if [ ${BUILD_ERROR_OCCURED} -eq 1 ] +then + echo -e "${RED}Build encountered some errors!${NC}" + exit 1 +fi diff --git a/build_scripts/linux/linux_run_tests.sh b/build_scripts/linux/linux_run_tests.sh index c6464edd85a92886bf69e6c115bac19746370176..70f3e7f8e738a777e6b6ad66a47ed6c421f25e11 100755 --- a/build_scripts/linux/linux_run_tests.sh +++ b/build_scripts/linux/linux_run_tests.sh @@ -6,7 +6,7 @@ cd ../.. # UNIT TESTS # ############## for f in build/unit-tests/*_test; do - ${f} || exit -1 + ${f} || exit 1 done -cd build_scripts/linux \ No newline at end of file +cd build_scripts/linux diff --git a/build_scripts/windows/win_VS2015_build_x64_release.bat b/build_scripts/windows/win_VS2015_build_x64_release.bat new file mode 100644 index 0000000000000000000000000000000000000000..9bb339c602ec41378236787cc2b70d1b6711dda9 --- /dev/null +++ b/build_scripts/windows/win_VS2015_build_x64_release.bat @@ -0,0 +1,27 @@ +@echo off +title Building the 'lib4neuro' project for Release + +set BUILD_SOMETHING_LIB=yes +set BUILD_LIB=yes +set BUILD_TESTS=yes +set BUILD_EXAMPLES=yes + +rem call VsDevCmd.bat + +title Building the 'lib4neuro' project for Release [preparing makefiles] + +rem C++ compiler (Requires Visual Studio 2015) +set CXX_COMPILER=cl +set C_COMPILER=cl + +set MAKEFILE_GENERATOR="Visual Studio 14 2015 Win64" + +cmake -G %MAKEFILE_GENERATOR% -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER=%CXX_COMPILER% -DCMAKE_C_COMPILER=%C_COMPILER% -DBOOST_ROOT=%BOOST_ROOT% -DBOOST_LIBRARYDIR=%BOOST_LIBRARYDIR% -DBOOST_INCLUDEDIR=%BOOST_INCLUDEDIR% -DBUILD_TESTS=%BUILD_TESTS% -DBUILD_EXAMPLES=%BUILD_EXAMPLES% -DBUILD_LIB=%BUILD_LIB% -DLIB4NEURO_DIR=build\lib -DDEPENDENCIES_LINK_TYPE=%LINK_TYPE% -DLIB4NEURO_INCLUDE_DIR=%LIB4NEURO_INCLUDE_DIR% -S . -B build + +title Building the 'lib4neuro' project for Release [building] + +call set_env_n_cores.bat + +if "%BUILD_SOMETHING_LIB%" == "yes" ( + cmake --build build -j %N_CORES% --config Release && echo "Build complete." +) diff --git a/build_scripts/windows/win_VS_build_x64_debug.bat b/build_scripts/windows/win_VS_build_x64_debug.bat index 8aabc35ee86ce68d6c7e9c7b366ce4f709e11884..d1ff690a276e53dab6091051b7915426836db394 100644 --- a/build_scripts/windows/win_VS_build_x64_debug.bat +++ b/build_scripts/windows/win_VS_build_x64_debug.bat @@ -1,86 +1,13 @@ @echo off title Building the 'lib4neuro' project for Debug -cls - -set DEPENDENCIES_LINK_TYPE=static -set BOOST_LIBRARYDIR=C:\local\boost_1_68_0\stage\lib - -set "REBUILD_BOOST=" -set "BUILD_EXAMPLES=" -set "BUILD_TESTS=" -set "BUILD_LIB=" -set "BUILD_SOMETHING=" -set "BUILD_SOMETHING_LIB=" - -rem call VsDevCmd.bat - -rem Should we rebuild BOOST? (yes/no) -set REBUILD_BOOST=no - -rem Should we build the examples? (yes/no) -set BUILD_EXAMPLES=yes - -rem Should we build the unit-tests? (yes/no) -set BUILD_TESTS=yes - -rem Should we build the lib4neuro library? (yes/no) +set BUILD_SOMETHING_LIB=yes set BUILD_LIB=yes +set BUILD_TESTS=yes +set BUILD_EXAMPLES=yes -rem **********************DO NOT CHANGE BEYOND THIS LINE**************************************** - -set BUILD_SOMETHING=no -set BUILD_SOMETHING_LIB=no - -IF "%BUILD_LIB%"=="yes" ( - echo Lib4neuro build type: Debug - echo Lib4neuro build architecture: x64 - echo Lib4neuro will be built in 'build\lib\' - set BUILD_SOMETHING=yes - set BUILD_SOMETHING_LIB=yes - call win_clean_lib.bat -) - -IF "%BUILD_TESTS%"=="yes" ( - echo Lib4neuro unit tests will be built in 'build\unit-tests' - set BUILD_SOMETHING=yes - set BUILD_SOMETHING_LIB=yes - call win_clean_tests.bat -) - -IF "%BUILD_EXAMPLES%"=="yes" ( - echo Lib4neuro examples will be built in 'build\examples' - set BUILD_SOMETHING=yes - set BUILD_SOMETHING_LIB=yes - call win_clean_examples.bat -) - -IF "%REBUILD_BOOST%"=="yes" ( - echo The required BOOST library will be recompiled in the directory 'external_dependencies\boost' - set BUILD_SOMETHING=yes -) - -IF "%BUILD_SOMETHING%"=="yes" ( - rem pause -) - - -rem Boost rebuild -IF "%REBUILD_BOOST%"=="yes" ( - title Rebuilding 'BOOST' for Debug - cd ..\.. - - rmdir /s /q external_dependencies\boost\stage 2>NUL - rmdir /s /q external_dependencies\boost\bin.v2 2>NUL - - cd external_dependencies\boost - - .\b2 --layout=system variant=debug link=%DEPENDENCIES_LINK_TYPE% address-model=64 --with-system --with-serialization --with-random --with-test || goto error_occured_boost - - cd ..\..\build_scripts\windows -) +rem call VsDevCmd.bat -IF "%BUILD_SOMETHING_LIB%"=="yes" ( title Building the 'lib4neuro' project for Debug [preparing makefiles] rem C++ compiler (Requires Visual Studio 2017) @@ -88,31 +15,12 @@ IF "%BUILD_SOMETHING_LIB%"=="yes" ( set C_COMPILER=cl set MAKEFILE_GENERATOR="Visual Studio 15 2017 Win64" - - cd ..\.. - cmake -G "Visual Studio 15 2017 Win64" -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_COMPILER=%CXX_COMPILER% -DCMAKE_C_COMPILER=%C_COMPILER% -DBOOST_ROOT=%BOOST_ROOT% -DBOOST_LIBRARYDIR=%BOOST_LIBRARYDIR% -DBOOST_INCLUDEDIR=%BOOST_INCLUDEDIR% -DBUILD_TESTS=%BUILD_TESTS% -DBUILD_EXAMPLES=%BUILD_EXAMPLES% -DBUILD_LIB=%BUILD_LIB% -DLIB4NEURO_DIR=build\lib -DDEPENDENCIES_LINK_TYPE=%DEPENDENCIES_LINK_TYPE% . || goto error_occured_lib - + + cmake -G "Visual Studio 15 2017 Win64" -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_COMPILER=%CXX_COMPILER% -DCMAKE_C_COMPILER=%C_COMPILER% -DBOOST_ROOT=%BOOST_ROOT% -DBOOST_LIBRARYDIR=%BOOST_LIBRARYDIR% -DBOOST_INCLUDEDIR=%BOOST_INCLUDEDIR% -DBUILD_TESTS=%BUILD_TESTS% -DBUILD_EXAMPLES=%BUILD_EXAMPLES% -DBUILD_LIB=%BUILD_LIB% -DLIB4NEURO_DIR=build\lib -DDEPENDENCIES_LINK_TYPE=%LINK_TYPE% -DLIB4NEURO_INCLUDE_DIR=%LIB4NEURO_INCLUDE_DIR% . title Building the 'lib4neuro' project for Debug [building] - cmake --build . --config Debug && echo "Build complete." || goto error_occured_lib - - cd build_scripts\windows + + call set_env_n_cores.bat + +if "%BUILD_SOMETHING_LIB%" == "yes" ( + cmake --build . -j %N_CORES% --config Debug && echo "Build complete." ) -goto final_goto - -:error_occured_lib -cd build_scripts\windows -goto final_goto - -:error_occured_boost -cd ..\..\build_scripts\windows -goto final_goto - - - - - -:final_goto -IF "%BUILD_LIB%"=="yes" call win_clean_after_lib.bat -IF "%BUILD_EXAMPLES%"=="yes" call win_clean_after_examples.bat -IF "%BUILD_TESTS%"=="yes" call win_clean_after_tests.bat -IF "%BUILD_SOMETHING_LIB%"=="yes" call win_clean_garbage.bat \ No newline at end of file diff --git a/build_scripts/windows/win_VS_build_x64_release.bat b/build_scripts/windows/win_VS_build_x64_release.bat index 4882937706fa5222f8481336624aa94958a0c561..46d141f7b36a69aa5649483e10fb5c9187df4ec3 100644 --- a/build_scripts/windows/win_VS_build_x64_release.bat +++ b/build_scripts/windows/win_VS_build_x64_release.bat @@ -1,149 +1,26 @@ @echo off title Building the 'lib4neuro' project for Release -cls - -set "REBUILD_BOOST=" -set "BUILD_EXAMPLES=" -set "BUILD_TESTS=" -set "BUILD_LIB=" -set "BUILD_SOMETHING=" -set "BUILD_SOMETHING_LIB=" - -rem call VsDevCmd.bat - -rem Should we rebuild BOOST? (yes/no) -set REBUILD_BOOST=yes - -rem Should we build the examples? (yes/no) -set BUILD_EXAMPLES=yes - -rem Should we build the unit-tests? (yes/no) -set BUILD_TESTS=yes - -rem Should we build the lib4neuro library? (yes) +set BUILD_SOMETHING_LIB=yes set BUILD_LIB=yes +set BUILD_TESTS=yes +set BUILD_EXAMPLES=yes -rem **********************DO NOT CHANGE BEYOND THIS LINE**************************************** - -set BUILD_SOMETHING=no -set BUILD_SOMETHING_LIB=no - -IF "%BUILD_LIB%"=="yes" ( - echo Lib4neuro build type: Release - echo Lib4neuro build architecture: x64 - echo Lib4neuro will be built in 'build\lib\' - set BUILD_SOMETHING=yes - set BUILD_SOMETHING_LIB=yes -) - -IF "%BUILD_TESTS%"=="yes" ( - echo Lib4neuro unit tests will be built in 'build\unit-tests' - set BUILD_SOMETHING=yes - set BUILD_SOMETHING_LIB=yes -) - -IF "%BUILD_EXAMPLES%"=="yes" ( - echo Lib4neuro examples will be built in 'build\examples' - set BUILD_SOMETHING=yes - set BUILD_SOMETHING_LIB=yes -) - -IF "%REBUILD_BOOST%"=="yes" ( - echo The required BOOST library will be recompiled in the directory 'external_dependencies\boost' - set BUILD_SOMETHING=yes -) - -IF "%BUILD_SOMETHING%"=="yes" ( - rem pause -) - - -rem Boost rebuild -IF "%REBUILD_BOOST%"=="yes" ( - title Rebuilding 'BOOST' for Release - cd ..\.. - - rmdir /s /q external_dependencies\boost\stage 2>NUL - rmdir /s /q external_dependencies\boost\bin.v2 2>NUL - - cd external_dependencies\boost - - .\b2 --layout=system variant=release link=static address-model=64 --with-system --with-serialization --with-random - - cd ..\..\build_scripts\windows -) - -IF "%BUILD_SOMETHING_LIB%"=="yes" ( - - IF "%BUILD_LIB%"=="yes" ( - call win_clean_lib.bat - ) +rem call VsDevCmd.bat - IF "%BUILD_EXAMPLES%"=="yes" ( - call win_clean_examples.bat - ) - - IF "%BUILD_TESTS%"=="yes" ( - call win_clean_tests.bat - ) - - title Building the 'lib4neuro' project for Release (preparing makefiles) + title Building the 'lib4neuro' project for Release [preparing makefiles] rem C++ compiler (Requires Visual Studio 2017) set CXX_COMPILER=cl set C_COMPILER=cl set MAKEFILE_GENERATOR="Visual Studio 15 2017 Win64" - - cd ..\.. - cmake -G "Visual Studio 15 2017 Win64" -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER=%CXX_COMPILER% -DCMAKE_C_COMPILER=%C_COMPILER% -DBOOST_ROOT=%BOOST_ROOT% -DBOOST_LIBRARYDIR=%BOOST_LIBRARYDIR% -DBOOST_INCLUDEDIR=%BOOST_INCLUDEDIR% -DBUILD_TESTS=%BUILD_TESTS% -DBUILD_EXAMPLES=%BUILD_EXAMPLES% -DBUILD_LIB=%BUILD_LIB% -DLIB4NEURO_DIR=build\lib . - - title Building the 'lib4neuro' project for Release (building) - (cmake --build . --config Release > build.log) && (echo "Build complete.") || (echo "Build finished with errors!") - - cd build_scripts\windows - - IF "%BUILD_LIB%"=="yes" ( - cd ..\.. - - rem Moving LIB files around to have a neater structure - xcopy /y build\bin\Release\lib4neuro.dll build\lib 2>NUL - xcopy /y build\lib\Release\lib4neuro.lib build\lib 2>NUL - - rmdir /s /q "build\lib\Release" 2> NUL - - cd build_scripts\windows - ) - - IF "%BUILD_EXAMPLES%"=="yes" ( - cd ..\.. - - rem Moving EXAMPLE files around to have a neater structure - mkdir build\tmp - xcopy /y build\examples\bin\Release\*.exe build\tmp 2>NUL - rmdir /s /q "build\examples" 2> NUL - move build\tmp build\examples - - xcopy /y build\lib\*.dll build\examples 2>NUL - - cd build_scripts\windows - ) - - IF "%BUILD_TESTS%"=="yes" ( - cd ..\.. - - rem Moving EXAMPLE files around to have a neater structure - mkdir build\tmp - xcopy /y build\unit-tests\bin\Release\*.exe build\tmp 2>NUL - rmdir /s /q "build\unit-tests" 2> NUL - move build\tmp build\unit-tests - - xcopy /y build\lib\*.dll build\unit-tests 2>NUL - - cd build_scripts\windows - ) - - call win_clean_garbage.bat + + cmake -G %MAKEFILE_GENERATOR% -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER=%CXX_COMPILER% -DCMAKE_C_COMPILER=%C_COMPILER% -DBOOST_ROOT=%BOOST_ROOT% -DBOOST_LIBRARYDIR=%BOOST_LIBRARYDIR% -DBOOST_INCLUDEDIR=%BOOST_INCLUDEDIR% -DBUILD_TESTS=%BUILD_TESTS% -DBUILD_EXAMPLES=%BUILD_EXAMPLES% -DBUILD_LIB=%BUILD_LIB% -DLIB4NEURO_DIR=build\lib -DDEPENDENCIES_LINK_TYPE=%LINK_TYPE% -DLIB4NEURO_INCLUDE_DIR=%LIB4NEURO_INCLUDE_DIR% -S . -B build + title Building the 'lib4neuro' project for Release [building] + + call set_env_n_cores.bat + +if "%BUILD_SOMETHING_LIB%" == "yes" ( + cmake --build build -j %N_CORES% --config Release && echo "Build complete." ) - diff --git a/build_scripts/windows/win_clean_after_examples.bat b/build_scripts/windows/win_clean_after_examples.bat index 7fd3c151e2656d7d2a1741c0c7386049a4f02fd1..520ba5bc233d9bf2d29465b3028db6e3f556df97 100644 --- a/build_scripts/windows/win_clean_after_examples.bat +++ b/build_scripts/windows/win_clean_after_examples.bat @@ -4,13 +4,16 @@ cd ..\.. rem Moving EXAMPLE files around to have a neater structure echo Cleaning up after examples + + rmdir /s /q "build\lib\Debug" 2> NUL + rmdir /s /q "build\examples" 2> NUL mkdir build\tmp xcopy /y build\bin\examples\Debug\*.exe build\tmp 2>NUL - rmdir /s /q "build\examples" 2> NUL - rmdir /s /q "build\lib\Debug" 2> NUL - move build\tmp build\examples + rmdir /s /q "build\bin\examples" 2> NUL + + move build\tmp build\bin\examples - xcopy /y build\lib\*.dll build\examples 2>NUL + xcopy /y build\lib\*.dll build\bin\examples 2>NUL cd build_scripts\windows \ No newline at end of file diff --git a/build_scripts/windows/win_clean_after_lib.bat b/build_scripts/windows/win_clean_after_lib.bat index f481d973260a4f29900909de110b55b00c83673d..f796392840a70059a493febc9f8d6061fe01029e 100644 --- a/build_scripts/windows/win_clean_after_lib.bat +++ b/build_scripts/windows/win_clean_after_lib.bat @@ -1,20 +1,19 @@ -@echo off +rem @echo off cd ..\.. rem Moving LIB files around to have a neater structure echo Cleaning up after library -xcopy /y build\bin\Debug\lib4neuro.dll build\lib 2>NUL -xcopy /y build\lib\Debug\lib4neuro.lib build\lib 2>NUL -xcopy /y build\lib\Debug\lib4neuro.pdb build\lib 2>NUL +xcopy /y build\bin\Debug\*.dll build\lib 2>NUL +xcopy /y build\bin\Debug\*.pdb build\lib 2>NUL -xcopy /y build\bin\Debug\boost_unit_test.dll build\lib 2>NUL -xcopy /y build\lib\Debug\boost_unit_test.lib build\lib 2>NUL -xcopy /y build\lib\Debug\boost_unit_test.pdb build\lib 2>NUL +xcopy /y build\lib\Debug\*.lib build\lib 2>NUL +xcopy /y build\lib\Debug\*.pdb build\lib 2>NUL -rmdir /s /q "build\lib\Debug" 2> NUL -rmdir /s /q "build\libboost_unit_test.dir" 2> NUL rmdir /s /q "build\boost_unit_test.dir" 2> NUL -rmdir /s /q "build\exprtk.dir" 2> NUL +rmdir /s /q "build\exprtk_wrap.dir" 2> NUL +rmdir /s /q "build\lib4neuro.dir" 2> NUL +rmdir /s /q "build\lib\Debug" 2> NUL +rmdir /s /q "build\bin\Debug" 2> NUL cd build_scripts\windows \ No newline at end of file diff --git a/build_scripts/windows/win_clean_after_tests.bat b/build_scripts/windows/win_clean_after_tests.bat index 92996d42a77fdb6cfc58438bbead4d09b15c0e86..5cf729fcbecdf4f4b9c12628478d79f0062bbef1 100644 --- a/build_scripts/windows/win_clean_after_tests.bat +++ b/build_scripts/windows/win_clean_after_tests.bat @@ -4,11 +4,12 @@ cd ..\.. rem Moving UNIT-TESTS files around to have a neater structure echo Cleaning up after unit-tests + + rmdir /s /q "build\lib\Debug" 2> NUL mkdir build\tmp xcopy /y build\unit-tests\Debug\*.exe build\tmp 2>NUL rmdir /s /q "build\unit-tests" 2> NUL - rmdir /s /q "build\lib\Debug" 2> NUL move build\tmp build\unit-tests xcopy /y build\lib\*.dll build\unit-tests 2>NUL diff --git a/build_scripts/windows/win_clean_examples.bat b/build_scripts/windows/win_clean_examples.bat index 29eb8b100c11811b48c47bcfa3fee37debd91245..5c8615f6d37721a50daa45de56ea254e85269065 100644 --- a/build_scripts/windows/win_clean_examples.bat +++ b/build_scripts/windows/win_clean_examples.bat @@ -1,4 +1,4 @@ -@echo off +rem @echo off title Cleaning up 'examples' echo Cleaning up before examples build @@ -6,5 +6,6 @@ echo Cleaning up before examples build cd ..\.. rmdir /s /q "build\examples" 2>NUL +rmdir /s /q "build\bin\examples" 2>NUL cd build_scripts\windows \ No newline at end of file diff --git a/build_scripts/windows/win_clean_garbage.bat b/build_scripts/windows/win_clean_garbage.bat index 43d1f9b77ccdd13bf27ae64233f549e60de78912..49e0ea9bd95a86752702ba3d314fb2f34ce0527e 100644 --- a/build_scripts/windows/win_clean_garbage.bat +++ b/build_scripts/windows/win_clean_garbage.bat @@ -30,9 +30,6 @@ rmdir /s /q "src\examples\CMakeFiles" 2>NUL rmdir /s /q "src\tests\CMakeFiles" 2>NUL rmdir /s /q "build\CMakeFiles" 2>NUL -rmdir /s /q "build\lib4neuro.dir" 2>NUL -rmdir /s /q "build\libexprtk.dir" 2>NUL -rmdir /s /q "build\bin" 2>NUL del /q "build\*vcxproj*" 2>NUL del /q "build\*cmake*" 2>NUL diff --git a/build_scripts/windows/win_clean_lib.bat b/build_scripts/windows/win_clean_lib.bat index 9fc021d87d08ee2d57d67543cff4ad80f172772f..c313bd1ea77b1e4c496b866a4dedc80ea8af4b9d 100644 --- a/build_scripts/windows/win_clean_lib.bat +++ b/build_scripts/windows/win_clean_lib.bat @@ -8,7 +8,5 @@ cd ..\.. rmdir /s /q "build\lib" 2>NUL rmdir /s /q "build\bin\Debug" 2>NUL rmdir /s /q "build\bin\Release" 2>NUL -rmdir /s /q "build\libboost_unit_test.dir" 2>NUL - cd build_scripts\windows \ No newline at end of file diff --git a/build_scripts/windows/win_download_dependencies.bat b/build_scripts/windows/win_download_dependencies.bat index fcc257c9e0945aa54fcdd16d7469f60c318e6d0f..5b5512920f287a85e0b454cb3fef521d887d3f38 100644 --- a/build_scripts/windows/win_download_dependencies.bat +++ b/build_scripts/windows/win_download_dependencies.bat @@ -20,8 +20,8 @@ rem Dependencies download IF "%DOWNLOAD_DEP%"=="yes" ( cd ..\.. - - rmdir /s /q "external_dependencies" 2>NUL + + rmdir /s /q external_dependencies 2>NUL git submodule init git submodule update --remote diff --git a/build_scripts/windows/win_run_tests.bat b/build_scripts/windows/win_run_tests.bat index b458df1b9236b4ed4343d68f583282be2c85083d..5dddc02a751f0914aaa3905bf8442804442723c1 100644 --- a/build_scripts/windows/win_run_tests.bat +++ b/build_scripts/windows/win_run_tests.bat @@ -3,6 +3,6 @@ title Running 'lib4neuro' unit-tests cd ..\..\build\unit-tests rem Runs all the available Unit-Tests -for /r %%v in ("*_test.exe") do call %%v +for /r %%v in ("*_test.exe") do %%v echo %v% -cd ..\..\build_scripts\windows \ No newline at end of file +cd ..\..\build_scripts\windows diff --git a/ci_run_tests.sh b/ci_run_tests.sh old mode 100755 new mode 100644 index dfc202791eb6650bd81be3269dba2a8b12149955..7c2490c0d867cfb2c7a15f2221f2360d0335a932 --- a/ci_run_tests.sh +++ b/ci_run_tests.sh @@ -3,7 +3,8 @@ ############## # UNIT TESTS # ############## -for f in build/unit-tests/bin/*_test; do - ${f} || exit -1 +for f in build/unit-tests/*_test; do + echo "Test ${f} starting..." + ${f} || exit 1 done diff --git a/clean.bat b/clean.bat index 3a38f7ba7480439bfdf5219ce748f890c2aa612d..1cb95d9f6a7b7812a14ee124a1e1ecc0be270982 100644 --- a/clean.bat +++ b/clean.bat @@ -18,3 +18,5 @@ rmdir /s /q CMakeFiles 2>NUL rmdir /s /q src/CMakeFiles 2>NUL rmdir /s /q src/examples/CMakeFiles 2>NUL rmdir /s /q src/tests/CMakeFiles 2>NUL +rem rmdir /s /q external_dependencies 2>NUL +rmdir /s /q _deps diff --git a/clean.sh b/clean.sh index 3f6c2d69384a9368fd53f590b515d28a074206e5..3a0e8d6fe8123e85a8511410c6b100e402ed9ae9 100755 --- a/clean.sh +++ b/clean.sh @@ -9,3 +9,5 @@ rm -f src/funit.tmp src/*_fun.f90 rm -f CMakeCache.txt rm -f cmake_install.cmake src/cmake_install.cmake rm -rf CMakeFiles src/CMakeFiles src/examples/CMakeFiles src/tests/CMakeFiles +rm -rf external_dependencies/* +rm -rf _deps diff --git a/cmake/DownloadArmadillo.cmake b/cmake/DownloadArmadillo.cmake new file mode 100644 index 0000000000000000000000000000000000000000..1f7c59c0b63e020974e223b055dc2b958617f9c4 --- /dev/null +++ b/cmake/DownloadArmadillo.cmake @@ -0,0 +1,27 @@ +MESSAGE("DownloadArmadillo starting...") + +SET(ARMADILLO_LOCAL_PATH ${ROOT_DIR}/external_dependencies/armadillo) + +INCLUDE(FetchContent) + +###################### +# Download Armadillo # +###################### +FETCHCONTENT_DECLARE( + armadillo + SOURCE_DIR ${ARMADILLO_LOCAL_PATH} + GIT_REPOSITORY https://gitlab.com/conradsnicta/armadillo-code.git + GIT_TAG 9.300.x #TODO do some general solution! +) + +SET(FETCHCONTENT_QUIET FALSE) + +FETCHCONTENT_POPULATE(armadillo) + +FIND_PACKAGE(Armadillo) + +IF(NOT ARMADILLO_FOUND) + MESSAGE(FATAL_ERROR "Armadillo was not downloaded successfully!") +ENDIF() + +SET(ENV{ARMADILLO_LOCAL_PATH} ${ARMADILLO_LOCAL_PATH}) diff --git a/cmake/DownloadBoost.cmake b/cmake/DownloadBoost.cmake new file mode 100644 index 0000000000000000000000000000000000000000..a5c76383e9e652ca8667dbe9241d410aaf8a2fb3 --- /dev/null +++ b/cmake/DownloadBoost.cmake @@ -0,0 +1,99 @@ +MESSAGE("DownloadBoost starting...") + +SET(BOOST_LOCAL_PATH ${ROOT_DIR}/external_dependencies/boost) + +INCLUDE(FetchContent) + +################## +# Download Boost # +################## +SET(WINAPI_BOOST_LIB "") +IF(WIN32) + SET(WINAPI_BOOST_LIB libs/winapi) +ENDIF() + +FETCHCONTENT_DECLARE( + boost + SOURCE_DIR ${BOOST_LOCAL_PATH} + GIT_REPOSITORY https://github.com/boostorg/boost.git + GIT_SUBMODULES tools/build tools/boost_install + libs/system libs/random libs/serialization + libs/config libs/headers libs/assert libs/core + libs/integer libs/type_traits libs/mpl + libs/preprocessor libs/throw_exception + libs/utility libs/static_assert libs/smart_ptr + libs/predef libs/move libs/io libs/iterator + libs/detail libs/spirit libs/optional + libs/function libs/type_index libs/bind + libs/container_hash libs/array libs/test + libs/timer libs/exception libs/algorithm + libs/range libs/numeric libs/format + libs/lexical_cast libs/concept_check + libs/container libs/math libs/function_types + libs/typeof ${WINAPI_BOOST_LIB} +) + +SET(FETCHCONTENT_QUIET FALSE) + +FETCHCONTENT_POPULATE(boost) + +############### +# Build Boost # +############### +SET(BOOTSTRAP_CMD sh bootstrap.sh) +SET(B2_CMD ./b2 -j${N_CORES}) +IF(WIN32) + SET(BOOTSTRAP_CMD bootstrap.bat) + SET(B2_CMD b2 -j${N_CORES}) +ENDIF() + +EXECUTE_PROCESS( + COMMAND ${BOOTSTRAP_CMD} + WORKING_DIRECTORY ${BOOST_LOCAL_PATH} + RESULT_VARIABLE rv +) +IF(NOT rv STREQUAL "0") + MESSAGE("Boost build: bootstrap: ${rv}") +ENDIF() + +EXECUTE_PROCESS( + COMMAND ${B2_CMD} headers + WORKING_DIRECTORY ${BOOST_LOCAL_PATH} + RESULT_VARIABLE rv +) +IF(NOT rv STREQUAL "0") + MESSAGE("Boost build: b2 headers: ${rv}") +ENDIF() + +SET(VARIANT release) +IF(${CMAKE_BUILD_TYPE} STREQUAL "Debug") + SET(VARIANT debug) +ENDIF() + +set(PIC_CODE "") +if(NOT WIN32) + set(PIC_CODE cxxflags=-fPIC) +endif() + +EXECUTE_PROCESS( + COMMAND ${B2_CMD} -q ${PIC_CODE} ${BOOST_TOOLSET} --layout=system variant=${VARIANT} link=${DEPENDENCIES_LINK_TYPE} address-model=64 architecture=x86 --with-system --with-serialization --with-random + WORKING_DIRECTORY ${BOOST_LOCAL_PATH} + RESULT_VARIABLE rv +) +IF(NOT rv STREQUAL "0") + MESSAGE("Boost build: b2: ${rv}") +ENDIF() + +FIND_PACKAGE( + Boost + + COMPONENTS + system + serialization + random +) + +IF(NOT Boost_FOUND) + MESSAGE(FATAL_ERROR "Boost was not downloaded successfully!") +ENDIF() + diff --git a/cmake/DownloadExprtk.cmake b/cmake/DownloadExprtk.cmake new file mode 100644 index 0000000000000000000000000000000000000000..a7d9c1df41d1991e93368ac35ea036c6f3f4a049 --- /dev/null +++ b/cmake/DownloadExprtk.cmake @@ -0,0 +1,25 @@ +MESSAGE("DownloadExprtk starting...") + +SET(EXPRTK_LOCAL_PATH ${ROOT_DIR}/external_dependencies/exprtk) + +INCLUDE(FetchContent) + +################### +# Download exprtk # +################### +FETCHCONTENT_DECLARE( + exprtk + SOURCE_DIR ${EXPRTK_LOCAL_PATH} + GIT_REPOSITORY https://github.com/ArashPartow/exprtk.git +) + +SET(FETCHCONTENT_QUIET FALSE) + +FETCHCONTENT_POPULATE(exprtk) + +FIND_PACKAGE(Exprtk) + +IF(NOT EXPRTK_FOUND) + MESSAGE(FATAL_ERROR "Exprtk was not downloaded successfully!") +ENDIF() + diff --git a/cmake/DownloadFromURL.cmake b/cmake/DownloadFromURL.cmake new file mode 100644 index 0000000000000000000000000000000000000000..62c2d7cd2f154563aaba714cb09671b1d2c30bf9 --- /dev/null +++ b/cmake/DownloadFromURL.cmake @@ -0,0 +1,3 @@ +MESSAGE(URL_FOR_DOWNLOAD: ${URL_FOR_DOWNLOAD}) +MESSAGE(FILE_TO_SAVE: ${FILE_TO_SAVE}) +FILE(DOWNLOAD ${URL_FOR_DOWNLOAD} ${FILE_TO_SAVE}) diff --git a/cmake/DownloadLapacke.cmake b/cmake/DownloadLapacke.cmake new file mode 100644 index 0000000000000000000000000000000000000000..338829b3c330b271866d8ac1de1f2555e92c44a8 --- /dev/null +++ b/cmake/DownloadLapacke.cmake @@ -0,0 +1,42 @@ +MESSAGE("DownloadLapacke starting...") + +SET(LAPACKE_LOCAL_PATH ${ROOT_DIR}/external_dependencies/Lapacke) + +INCLUDE(FetchContent) + +#################### +# Download Lapacke # +#################### +SET(LAPACKE_FILES ${LAPACKE_LOCAL_PATH}/lapacke.h ${LAPACKE_LOCAL_PATH}/liblapacke.dll ${LAPACKE_LOCAL_PATH}/liblapacke.lib) + +ADD_CUSTOM_COMMAND( + OUTPUT ${LAPACKE_LOCAL_PATH}/lapacke.h + COMMAND ${CMAKE_COMMAND} -DURL_FOR_DOWNLOAD=https://icl.cs.utk.edu/lapack-for-windows/include/lapacke.h -DFILE_TO_SAVE=${LAPACKE_LOCAL_PATH}/lapacke.h -P ${CMAKE_CURRENT_LIST_DIR}/DownloadFromUrl.cmake + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} + VERBATIM + COMMAND_EXPAND_LISTS +) + +ADD_CUSTOM_COMMAND( + OUTPUT ${LAPACKE_LOCAL_PATH}/liblapacke.dll + COMMAND ${CMAKE_COMMAND} -DURL_FOR_DOWNLOAD=https://icl.cs.utk.edu/lapack-for-windows/libraries/VisualStudio/3.7.0/Dynamic-MINGW/Win64/liblapacke.dll -DFILE_TO_SAVE=${LAPACKE_LOCAL_PATH}/liblapacke.dll -P ${CMAKE_CURRENT_LIST_DIR}/DownloadFromUrl.cmake + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} + VERBATIM + COMMAND_EXPAND_LISTS +) + +ADD_CUSTOM_COMMAND( + OUTPUT ${LAPACKE_LOCAL_PATH}/liblapacke.lib + COMMAND ${CMAKE_COMMAND} -DURL_FOR_DOWNLOAD=https://icl.cs.utk.edu/lapack-for-windows/libraries/VisualStudio/3.7.0/Dynamic-MINGW/Win64/liblapacke.lib -DFILE_TO_SAVE=${LAPACKE_LOCAL_PATH}/liblapacke.lib -P ${CMAKE_CURRENT_LIST_DIR}/DownloadFromUrl.cmake + WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} + VERBATIM + COMMAND_EXPAND_LISTS +) + +SET(ENV{LAPACKE_HEADERS} ${LAPACKE_LOCAL_PATH}/lapacke.h) +LIST(APPEND LAPACKE_LIBRARIES "${LAPACKE_LOCAL_PATH}/liblapacke.lib") +#"${LAPACKE_LOCAL_PATH}/liblapacke.dll" +SET(ENV{LAPACKE_LIBRARIES} ${LAPACKE_LIBRARIES}) +OPTION(ENV{LAPACKE_LOCAL} ON) + +ADD_CUSTOM_TARGET(lapacke_build DEPENDS ${LAPACKE_FILES}) diff --git a/cmake/DownloadOpenBLAS.cmake b/cmake/DownloadOpenBLAS.cmake new file mode 100644 index 0000000000000000000000000000000000000000..e011dd08d77973ee1e50b35edd72d8d42f20ab00 --- /dev/null +++ b/cmake/DownloadOpenBLAS.cmake @@ -0,0 +1,63 @@ +MESSAGE("DownloadOpenBLAS starting...") + +SET(OPENBLAS_LOCAL_PATH ${ROOT_DIR}/external_dependencies/OpenBLAS) + +INCLUDE(FetchContent) + +##################### +# Download OpenBLAS # +##################### +IF(WIN32) + FETCHCONTENT_DECLARE( + OpenBLAS + SOURCE_DIR ${OPENBLAS_LOCAL_PATH} + URL https://github.com/JuliaLinearAlgebra/OpenBLASBuilder/releases/download/v0.3.0-3/OpenBLAS.v0.3.0.x86_64-w64-mingw32-gcc8.tar.gz + ) + + # See messages during 'git clone' + SET(FETCHCONTENT_QUIET FALSE) + + IF(NOT OpenBLAS_POPULATED) + FETCHCONTENT_POPULATE(OpenBLAS) + ENDIF() + +ELSE() + FETCHCONTENT_DECLARE( + OpenBLAS + SOURCE_DIR ${OPENBLAS_LOCAL_PATH} + GIT_REPOSITORY https://github.com/xianyi/OpenBLAS.git + ) + + # See messages during 'git clone' + SET(FETCHCONTENT_QUIET FALSE) + + IF(NOT OpenBLAS_POPULATED) + FETCHCONTENT_POPULATE(OpenBLAS) + ENDIF() +ENDIF() + +IF(NOT WIN32) + ################################### + # Build OpenBLAS (only for Linux) # + ################################### + EXECUTE_PROCESS( + COMMAND ${CMAKE_COMMAND} -j ${N_CORES} . + WORKING_DIRECTORY ${OPENBLAS_LOCAL_PATH} + RESULT_VARIABLE rv + ) + IF(NOT rv STREQUAL "0") + MESSAGE("OpenBLAS build: cmake .: ${rv}") + ENDIF() + + # Build library + EXECUTE_PROCESS( + COMMAND ${CMAKE_COMMAND} --build . --config Release -j ${N_CORES} + WORKING_DIRECTORY ${OPENBLAS_LOCAL_PATH} + RESULT_VARIABLE rv + ) + IF(NOT rv STREQUAL "0") + MESSAGE("OpenBLAS build: cmake --build: ${rv}") + ENDIF() +ENDIF() + +FIND_PACKAGE(OpenBLAS) diff --git a/cmake/DownloadTurtle.cmake b/cmake/DownloadTurtle.cmake new file mode 100644 index 0000000000000000000000000000000000000000..eff09cbeb980a62a91af79d5753ac7809660c227 --- /dev/null +++ b/cmake/DownloadTurtle.cmake @@ -0,0 +1,25 @@ +MESSAGE("DownloadTurtle starting...") + +SET(TURTLE_LOCAL_PATH ${ROOT_DIR}/external_dependencies/turtle) + +INCLUDE(FetchContent) + +################### +# Download exprtk # +################### +FETCHCONTENT_DECLARE( + turtle + SOURCE_DIR ${TURTLE_LOCAL_PATH} + GIT_REPOSITORY https://github.com/mat007/turtle.git +) + +SET(FETCHCONTENT_QUIET FALSE) + +FETCHCONTENT_POPULATE(turtle) + +FIND_PACKAGE(Turtle) + +IF(NOT TURTLE_FOUND) + MESSAGE(FATAL_ERROR "Turtle was not downloaded successfully!") +ENDIF() + diff --git a/cmake/FindArmadillo.cmake b/cmake/FindArmadillo.cmake new file mode 100644 index 0000000000000000000000000000000000000000..89e559c0aa93c50fc17ed8492d30122819f2ca8a --- /dev/null +++ b/cmake/FindArmadillo.cmake @@ -0,0 +1,37 @@ +MESSAGE("FindArmadillo starting...") + +# Find headers +FIND_PATH( + ARMADILLO_INCLUDE_DIR + + NAMES + armadillo + + HINTS + $ENV{ARMADILLO_INCLUDE_DIR} + ${ARMADILLO_INCLUDE_DIR} + ${ROOT_DIR}/external_dependencies/armadillo/ + + PATHS + /usr + /home + /opt + + PATH_SUFFIXES + include + armadillo + include/armadillo + local +) + +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS( + armadillo + "Armadillo was NOT found!" + ARMADILLO_INCLUDE_DIR) + +IF(armadillo_FOUND) + MESSAGE(STATUS "Armadillo headers found.") + MESSAGE(STATUS "ARMADILLO_INCLUDE_DIR: ${ARMADILLO_INCLUDE_DIR}") + SET(ARMADILLO_ROOT ${ARMADILLO_INCLUDE_DIR}/..) +ENDIF() diff --git a/cmake/FindBoost.cmake b/cmake/FindBoost.cmake new file mode 100644 index 0000000000000000000000000000000000000000..bd7c8d6d75904db1bec762f2013ced1450d94e18 --- /dev/null +++ b/cmake/FindBoost.cmake @@ -0,0 +1,180 @@ +# This file is NOT an original FindBoost.cmake module provided by KitWare! +# +# It's a simplified version whose only purpose is to be used in a software +# library lib4neuro and it does NOT provide a full funcionality of the original, +# as it only works with the system-layout-named libraries (e.g. libboost_system.so). + +# Optional user-defined variables +# (can be passed directly to CMake or exported as environmental variables +# +# * BOOST_LIBRARYDIR - The path to the folder containing Boost libraries +# * BOOST_INCLUDEDIR - The path to the folder containing Boost header files + +# "External" used variables +# +# * DEPENDENCIES_LINK_TYPE - TODO + +# "Output" variables set after running this script +# * Boost_FOUND - TODO +# * Boost_INCLUDE_DIRS - TODO +# * Boost_LIBRARY_DIRS - TODO +# * Boost_LIBRARIES - TODO + +# Module usage +# TODO + +MESSAGE("FindBoost starting...") + +# Check if needed Boost components were specified +IF(NOT Boost_FIND_COMPONENTS) + MESSAGE(FATAL_ERROR "No Boost components were specified! Please, set them correctly with flag COMPONENTS (see Module Usage section in this script).") +ELSE() + MESSAGE("Required Boost components: ${Boost_FIND_COMPONENTS}") +ENDIF() + +# Look for a standard boost header file. +SET(Boost_INCLUDE_DIRS "Boost_INCLUDE_DIRS-NOTFOUND") +FIND_PATH( + Boost_INCLUDE_DIRS + + NAMES + config.hpp + + HINTS + $ENV{BOOST_ROOT} + $ENV{BOOST_ROOT}/boost + ${BOOST_INCLUDEDIR} + $ENV{BOOST_INCLUDEDIR} + ${ROOT_DIR}/external_dependencies/boost + ${ROOT_DIR}/external_dependencies/boost/src/boost + + PATHS + /usr/include + + PATH_SUFFIXES + boost + include +) + +# Add path without "boost" include sub-directory to include path, +# as Boost headers are supposed to be included like +# #include<boost/...> according to the documentation +SET(TMP "") +STRING(REPLACE "/boost/boost" "/boost" TMP ${Boost_INCLUDE_DIRS}) +LIST(APPEND Boost_INCLUDE_DIRS ${TMP}) + +IF(NOT Boost_INCLUDE_DIRS) + # message(FATAL_ERROR "Boost include directory was not found! Please, set variable BOOST_INCLUDEDIR to the correct path.") +ELSE() + MESSAGE("Boost_INCLUDE_DIRS: ${Boost_INCLUDE_DIRS}") +ENDIF() + +# Create a list of requested Boost libraries with "system" names +IF(NOT DEPENDENCIES_LINK_TYPE) + MESSAGE(FATAL_ERROR "Variable DEPENDENCIES_LINK_TYPE is not set! Set it to 'static' or 'shared'.") +ENDIF() + +SET(REQUESTED_BOOST_LIBS "") +FOREACH(COMPONENT ${Boost_FIND_COMPONENTS}) + LIST(APPEND REQUESTED_BOOST_LIBS "${LIB_PREFIX}boost_${COMPONENT}.${LIB_SUFFIX}") +ENDFOREACH() + +MESSAGE("REQUESTED_BOOST_LIBS: ${REQUESTED_BOOST_LIBS}") + +# Look for libraries specified by COMPONENTS flag +SET(Boost_LIBRARY_DIRS "Boost_LIBRARY_DIRS-NOTFOUND") +FIND_PATH( + Boost_LIBRARY_DIRS + + NAMES + ${REQUESTED_BOOST_LIBS} + + HINTS + $ENV{BOOST_ROOT} + $ENV{BOOST_ROOT}/stage + $ENV{BOOST_ROOT}/stage/lib + ${BOOST_LIBRARYDIR} + $ENV{BOOST_LIBRARYDIR} + ${ROOT_DIR}/external_dependencies/boost + ${ROOT_DIR}/external_dependencies/boost/src/boost + ${ROOT_DIR}/external_dependencies/boost/stage + ${ROOT_DIR}/external_dependencies/boost/stage/lib + + + PATHS + /usr/lib/boost + /usr/lib/x86_64-linux-gnu + + PATH_SUFFIXES + lib +) + +IF(NOT Boost_LIBRARY_DIRS) + #message(FATAL_ERROR "Boost library directory was not found! Please, set variable BOOST_LIBRARYDIR to the correct path.") +ELSE() + MESSAGE("Boost_LIBRARY_DIRS: ${Boost_LIBRARY_DIRS}") + + # Construct list of libraries' names and make them + # targets, so they may be linked + SET(Boost_LIBRARIES "") + FOREACH(LIBNAME ${REQUESTED_BOOST_LIBS}) + MESSAGE("Looking for ${LIBNAME}...") + + SET(${LIBNAME} "${LIBNAME}-NOTFOUND") + FIND_LIBRARY( + ${LIBNAME} + + NAMES + ${LIBNAME} + + PATHS + ${Boost_LIBRARY_DIRS} + + PATH_SUFFIXES + stage/lib + lib + + NO_DEFAULT_PATH + ) + + # Check, if the Boost component was found + IF("${${LIBNAME}}" STREQUAL "${LIBNAME}-NOTFOUND") + #message(FATAL_ERROR "Boost library ${LIBNAME} was NOT found!\ + # Please, set variable BOOST_LIBRARYDIR to the correct path and check the library names\ + # format in your Boost installation.") + ELSE() + MESSAGE("${LIBNAME} was found: ${${LIBNAME}}") + + # Add every found library as an IMPORTED target + STRING(TOUPPER ${DEPENDENCIES_LINK_TYPE} TMP) + STRING(REGEX REPLACE "^lib" "" TARGET_NAME ${LIBNAME}) + STRING(REGEX REPLACE "\\.[a-z]*$" "" TARGET_NAME ${TARGET_NAME}) + ADD_LIBRARY(${TARGET_NAME} ${TMP} IMPORTED) + SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES IMPORTED_LOCATION ${${LIBNAME}}) + MESSAGE("Created IMPORTED library target: ${TARGET_NAME}") + ENDIF() + + LIST(APPEND Boost_LIBRARIES ${${LIBNAME}}) + ENDFOREACH() + +ENDIF() + + +# Set Boost_FOUND +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS( + Boost + + FAIL_MESSAGE + "Boost was NOT found!" + + REQUIRED_VARS + Boost_INCLUDE_DIRS + Boost_LIBRARY_DIRS +) + +IF(Boost_FOUND) + MESSAGE("Boost_INCLUDE_DIRS: ${Boost_INCLUDE_DIRS}") + MESSAGE("Boost_LIBRARY_DIRS: ${Boost_LIBRARY_DIRS}") + MESSAGE("Boost_LIBRARIES: ${Boost_LIBRARIES}") +ENDIF() diff --git a/Findexprtk.cmake b/cmake/FindExprtk.cmake similarity index 71% rename from Findexprtk.cmake rename to cmake/FindExprtk.cmake index 4b84c47d4e3b2e2a718f03b2751a385c76e8a7d9..05ed480929eab69ade6ebc6079d0d750bd88b024 100644 --- a/Findexprtk.cmake +++ b/cmake/FindExprtk.cmake @@ -9,25 +9,30 @@ # ################################################################################ +MESSAGE("FindExprtk starting...") + # Find headers and libraries FIND_PATH( EXPRTK_INCLUDE_DIR NAMES - exprtk.hpp + exprtk.hpp HINTS - $ENV{EXPRTK_INCLUDE_DIR} - $ENV{EXPRTK_ROOT} - ${EXPRTK_ROOT} - ${EXPRTK_INCLUDE_DIR} - external_dependencies/exprtk + $ENV{EXPRTK_INCLUDE_DIR} + $ENV{EXPRTK_ROOT} + ${EXPRTK_ROOT} + ${EXPRTK_INCLUDE_DIR} + ${ROOT_DIR}/external_dependencies/exprtk PATHS - /home - /usr/local - /usr - /opt/local + /home + /usr/local + /usr + /opt/local + PATH_SUFFIXES - include + include + exprtk + include/exprtk ) # Set EXPRTK_FOUND honoring the QUIET and REQUIRED arguments INCLUDE(FindPackageHandleStandardArgs) @@ -40,8 +45,7 @@ FIND_PACKAGE_HANDLE_STANDARD_ARGS( IF(EXPRTK_FOUND) # Include dirs SET(EXPRTK_INCLUDE_DIRS ${EXPRTK_INCLUDE_DIR}) -ELSE() - MESSAGE(FATAL_ERROR "Set, please, the environmental variable EXPRTK_INCLUDE_DIR to the folder, where 'exprtk.hpp' is located...") + MESSAGE("Exprtk was successfully found.") ENDIF(EXPRTK_FOUND) # Advanced options for not cluttering the cmake UIs: diff --git a/cmake/FindOpenBLAS.cmake b/cmake/FindOpenBLAS.cmake new file mode 100644 index 0000000000000000000000000000000000000000..b7691e8a1c55cfcff7576ad9bcb664b7a55675c5 --- /dev/null +++ b/cmake/FindOpenBLAS.cmake @@ -0,0 +1,60 @@ +MESSAGE("FindOpenBLAS starting...") + +FIND_PATH( + OpenBLAS_INCLUDE_DIR + + NAMES + cblas.h + + HINTS + ${OpenBLAS_INCLUDE_DIRECTORY} + $ENV{OpenBLAS_INCLUDE_DIRECTORY} + ${ROOT_DIR}/external_dependencies/OpenBLAS + /usr + + PATH_SUFFIXES + include + include/x86_64-linux-gnu + include/OpenBLAS +) + +FIND_LIBRARY( + OpenBLAS_LIBRARIES + + NAMES + openblas + + HINTS + ${OpenBLAS_LIBRARY_DIRECTORY} + $ENV{OpenBLAS_LIBRARY_DIRECTORY} + ${ROOT_DIR}/external_dependencies/OpenBLAS/ + /usr + + PATH_SUFFIXES + bin + lib + lib/x86_64-linux-gnu + local + include/OpenBLAS/lib +) + +# Set OpenBLAS_Found +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS( + OpenBLAS + + FAIL_MESSAGE + "OpenBLAS was NOT found!" + + REQUIRED_VARS + OpenBLAS_INCLUDE_DIR + OpenBLAS_LIBRARIES +) + +IF(OpenBLAS_FOUND) + MESSAGE(STATUS "OpenBLAS was found.") + MESSAGE(STATUS "OpenBLAS_LIBRARIES: ${OpenBLAS_LIBRARIES}") + MESSAGE(STATUS "OpenBLAS_INCLUDE_DIR: ${OpenBLAS_INCLUDE_DIR}") +ELSE() + MESSAGE(STATUS "Could not find OpenBLAS") +ENDIF() diff --git a/cmake/FindTurtle.cmake b/cmake/FindTurtle.cmake new file mode 100644 index 0000000000000000000000000000000000000000..b8e6956ad0831191ff7b790aad01164804c305de --- /dev/null +++ b/cmake/FindTurtle.cmake @@ -0,0 +1,49 @@ +MESSAGE("FindTurtle starting...") + +# Find headers and libraries +FIND_PATH( + TURTLE_INCLUDE_DIR + + NAMES + mock.hpp + + HINTS + $ENV{TURTLE_INCLUDE_DIR} + ${TURTLE_INCLUDE_DIR} + ${ROOT_DIR}/external_dependencies/turtle/ + + PATHS + /usr + /home + /opt + + PATH_SUFFIXES + include + turtle + include/turtle/include/turtle + include/turtle + local +) +# Set TURTLE_FOUND honoring the QUIET and REQUIRED arguments +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS( + turtle + "Turtle was NOT found!" + TURTLE_INCLUDE_DIR) + +# Output variables +IF(TURTLE_FOUND) + # Include dirs + SET(TURTLE_INCLUDE_DIRS ${TURTLE_INCLUDE_DIR}) + MESSAGE("Turtle was successfully found.") +ELSE() + # MESSAGE(FATAL_ERROR "Set, please, the environmental variable TURTLE_INCLUDE_DIR to the folder, where 'mock.hpp' is located...") +ENDIF(TURTLE_FOUND) + +# Add path only to the 'include' folder +SET(TMP "") +STRING(REGEX REPLACE "/turtle$" "" TMP ${TURTLE_INCLUDE_DIR}) +LIST(APPEND TURTLE_INCLUDE_DIR ${TMP}) + +# Advanced options for not cluttering the cmake UIs: +MARK_AS_ADVANCED(TURTLE_INCLUDE_DIR) diff --git a/external_dependencies/boost b/external_dependencies/boost deleted file mode 160000 index 507ad00a0ad1ce6e8833f2f529fa1e9150c446c0..0000000000000000000000000000000000000000 --- a/external_dependencies/boost +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 507ad00a0ad1ce6e8833f2f529fa1e9150c446c0 diff --git a/external_dependencies/exprtk b/external_dependencies/exprtk deleted file mode 160000 index 9a8474e7a259fa5348658a651cd19af216749674..0000000000000000000000000000000000000000 --- a/external_dependencies/exprtk +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 9a8474e7a259fa5348658a651cd19af216749674 diff --git a/include/4neuro.h b/include/4neuro.h index 12e1c482b983fe66dcad286b0d63330190573da9..1bbae34d99372a4f6f916c16cd4c2f943ecb22b7 100644 --- a/include/4neuro.h +++ b/include/4neuro.h @@ -1,27 +1,32 @@ -// -// Created by martin on 7/16/18. -// - #ifndef INC_4NEURO_4NEURO_H #define INC_4NEURO_4NEURO_H //TODO make only public interface visible #include "../src/DataSet/DataSet.h" -#include "../src/ErrorFunction/ErrorFunctions.h" -#include "../src/LearningMethods/ParticleSwarm.h" -#include "../src/NetConnection/ConnectionFunctionGeneral.h" -#include "../src/NetConnection/ConnectionFunctionIdentity.h" #include "../src/Network/NeuralNetwork.h" #include "../src/Network/NeuralNetworkSum.h" #include "../src/Neuron/Neuron.h" +#include "../src/Neuron/NeuronConstant.h" #include "../src/Neuron/NeuronBinary.h" #include "../src/Neuron/NeuronLinear.h" #include "../src/Neuron/NeuronLogistic.h" +#include "../src/Neuron/NeuronBiased.h" #include "../src/Solvers/DESolver.h" +#include "../src/ErrorFunction/ErrorFunctions.h" +#include "../src/LearningMethods/ParticleSwarm.h" +#include "../src/LearningMethods/GradientDescent.h" +#include "../src/LearningMethods/GradientDescentBB.h" +#include "../src/LearningMethods/GradientDescentSingleItem.h" +#include "../src/LearningMethods/LearningSequence.h" +#include "../src/LearningMethods/LevenbergMarquardt.h" +#include "../src/LearningMethods/RandomSolution.h" +#include "../src/CSVReader/CSVReader.h" +#include "../src/CrossValidator/CrossValidator.h" #include "../src/constants.h" -#include "../src/settings.h" -#include "../src/message.h" +// Abbreviate lib4neuro namespace to l4n +namespace l4n = lib4neuro; + #endif //INC_4NEURO_4NEURO_H diff --git a/release-api/delete_release.sh b/release-api/delete_release.sh new file mode 100644 index 0000000000000000000000000000000000000000..4bb59c347d9642f82fe465616df9e61833899c69 --- /dev/null +++ b/release-api/delete_release.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +PROJECT_ID=$1 +TAG_NAME=$2 +PRIVATE_TOKEN=$3 + +if [ "$3" == "" ]; then + echo "Missing parameter! Parameters are PROJECT_ID, TAG_NAME and PRIVATE_TOKEN."; + exit 1; +fi + +curl --request DELETE --header "Private-Token: ${PRIVATE_TOKEN}" "https://code.it4i.cz/api/v4/projects/${PROJECT_ID}/releases/${TAG_NAME}" + +echo + diff --git a/release-api/upload_release.sh b/release-api/upload_release.sh new file mode 100644 index 0000000000000000000000000000000000000000..29ba60ad6e4d8b4b24d32a62fd61502acab3282b --- /dev/null +++ b/release-api/upload_release.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +RELEASE_NAME="$1" +TAG_NAME="$2" +PROJECT_ID="$3" +DESCRIPTION_FILE_PATH="$4" +PRIVATE_TOKEN="$5" + +if [ "$5" == "" ]; then + echo "Missing parameter! Parameters are RELEASE_NAME, TAG_NAME, PROJECT_ID, DESCRIPTION_FILE_PATH and PRIVATE_TOKEN."; + exit 1; +fi + +DESCRIPTION='' + +# Load data from file +while read -r line; do + DESCRIPTION="${DESCRIPTION}${line}\n"; +done < "${DESCRIPTION_FILE_PATH}" + +curl --request POST\ + --header 'Content-Type: application/json'\ + --header "Private-Token: ${PRIVATE_TOKEN}"\ + --data-binary "{\"name\": \"${RELEASE_NAME}\", \"tag_name\": \"${TAG_NAME}\", \"description\": \"${DESCRIPTION}\"}"\ + "https://code.it4i.cz/api/v4/projects/${PROJECT_ID}/releases" + +echo diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4fd5ccc16c4c98fdf196b6ec0b4e0eb1e7abc04b..de3c43c6dd97a8548060b180aa84299950af33bd 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,78 +1,190 @@ -if ("${BUILD_TESTS}" STREQUAL "yes") - add_subdirectory(tests unit-tests) -endif () - -if ("${BUILD_EXAMPLES}" STREQUAL "yes") - add_subdirectory(examples examples) -endif () - -if ("${BUILD_LIB}" STREQUAL "yes") - add_library(lib4neuro SHARED - Neuron/Neuron.cpp - Neuron/NeuronBinary.cpp - Neuron/NeuronConstant.cpp - Neuron/NeuronLinear.cpp - Neuron/NeuronLogistic.cpp - Network/NeuralNetwork.cpp - Network/NeuralNetworkSum.cpp - NetConnection/ConnectionFunctionGeneral.cpp - NetConnection/ConnectionFunctionIdentity.cpp - LearningMethods/ParticleSwarm.cpp - DataSet/DataSet.cpp - ErrorFunction/ErrorFunctions.cpp - Solvers/DESolver.cpp - General/ExprtkWrapper.cpp - ) - - target_link_libraries( +# Handle undefined parameters +IF(NOT BUILD_LIB) + IF(ENV{BUILD_LIB}) + SET(BUILD_LIB $ENV{BUILD_LIB}) + ELSE() + SET(BUILD_LIB "yes") + ENDIF() +ENDIF() + +IF(NOT BUILD_EXAMPLES) + IF(ENV{BUILD_EXAMPLES}) + SET(BUILD_EXAMPLES $ENV{BUILD_EXAMPLES}) + ELSE() + SET(BUILD_EXAMPLES "no") + ENDIF() +ENDIF() + +IF(NOT BUILD_TESTS) + IF(ENV{BUILD_TESTS}) + SET(BUILD_TESTS $ENV{BUILD_TESTS}) + ELSE() + SET(BUILD_TESTS "no") + ENDIF() +ENDIF() + +IF("${BUILD_LIB}" STREQUAL "yes") + + SET(LIBRARIES_OUTPUT_DIR ${PROJECT_BINARY_DIR}/lib) + + SET(LIB_TYPE "STATIC") + IF(DEPENDENCIES_LINK_TYPE STREQUAL "shared") + SET(LIB_TYPE "SHARED") + ENDIF() + + ADD_LIBRARY( + exprtk_wrap + + ${LIB_TYPE} + + General/ExprtkWrapper.cpp + ) + + SET_TARGET_PROPERTIES( + exprtk_wrap + + PROPERTIES + ARCHIVE_OUTPUT_DIRECTORY $<1:${LIBRARIES_OUTPUT_DIR}> + LIBRARY_OUTPUT_DIRECTORY $<1:${LIBRARIES_OUTPUT_DIR}> + RUNTIME_OUTPUT_DIRECTORY $<1:${LIBRARIES_OUTPUT_DIR}> + ) + + TARGET_INCLUDE_DIRECTORIES( + exprtk_wrap + + PRIVATE + ${EXPRTK_INCLUDE_DIR} + ${Boost_INCLUDE_DIRS} + ${SRC_DIR} + ) + + TARGET_LINK_LIBRARIES( + exprtk_wrap + + PRIVATE + ${Boost_LIBRARIES} + ) + + IF(NOT OpenBLAS_LIBRARIES) + SET(OpenBLAS_LIBRARIES "") + ENDIF() + + IF(NOT BLAS_LIBRARIES) + SET(BLAS_LIBRARIES "") + ENDIF() + + IF(NOT LAPACK_LIBRARIES) + SET(LAPACK_LIBRARIES "") + ENDIF() + + ADD_LIBRARY( + lib4neuro + + ${LIB_TYPE} + + Neuron/Neuron.cpp + Neuron/NeuronBinary.cpp + Neuron/NeuronConstant.cpp + Neuron/NeuronLinear.cpp + Neuron/NeuronLogistic.cpp + Network/NeuralNetwork.cpp + Network/NeuralNetworkSum.cpp + NetConnection/ConnectionFunctionGeneral.cpp + NetConnection/ConnectionFunctionIdentity.cpp + LearningMethods/LearningMethods.cpp + LearningMethods/ParticleSwarm.cpp + LearningMethods/GradientDescent.cpp + LearningMethods/LevenbergMarquardt.cpp + LearningMethods/GradientDescentBB.cpp + DataSet/DataSet.cpp + ErrorFunction/ErrorFunctions.cpp + Solvers/DESolver.cpp + CSVReader/CSVReader.cpp + CrossValidator/CrossValidator.cpp + NormalizationStrategy/NormalizationStrategy.cpp + LearningMethods/GradientDescentSingleItem.cpp + LearningMethods/LearningSequence.cpp + LearningMethods/RandomSolution.cpp + NetConnection/ConnectionFunctionConstant.cpp + Neuron/NeuronBiased.cpp + ) + + # Detect Threading library + SET(THREADS_PREFER_PTHREAD_FLAG ON) + FIND_PACKAGE(Threads REQUIRED) + + # GFortran linking + SET(GFORT "") + IF(OpenBLAS_FOUND) + MESSAGE("Linking GFortran because of OpenBLAS...") + SET(GFORT gfortran) + ENDIF() + + TARGET_LINK_LIBRARIES( lib4neuro PRIVATE - ${Boost_LIBRARIES} + exprtk_wrap + Threads::Threads + ${Boost_LIBRARIES} + ${CXX_FILESYSTEM_LIB} + ${OpenBLAS_LIBRARIES} + ${BLAS_LIBRARIES} + ${LAPACK_LIBRARIES} + ${GFORT} ) - target_include_directories( + TARGET_INCLUDE_DIRECTORIES( lib4neuro - #TODO Boost_INCLUDE_DIRS should be PRIVATE - rewrite code accordingly! PUBLIC - ${ROOT_DIR}/include - ${EXPRTK_INCLUDE_DIR} - ${Boost_INCLUDE_DIRS} + ${ROOT_DIR}/include PRIVATE - ${SRC_DIR} - -# ${Boost_INCLUDE_DIRS} + ${EXPRTK_INCLUDE_DIR} + ${SRC_DIR} + ${Boost_INCLUDE_DIRS} + ${ARMADILLO_INCLUDE_DIR} ) - set_target_properties( + SET_TARGET_PROPERTIES( lib4neuro PROPERTIES - ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib" - LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib" - RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin" + ARCHIVE_OUTPUT_DIRECTORY $<1:${LIBRARIES_OUTPUT_DIR}> + LIBRARY_OUTPUT_DIRECTORY $<1:${LIBRARIES_OUTPUT_DIR}> + RUNTIME_OUTPUT_DIRECTORY $<1:${LIBRARIES_OUTPUT_DIR}> + ) + + IF("${DEPENDENCIES_LINK_TYPE}" STREQUAL "static") + ADD_LIBRARY(boost_unit_test STATIC boost_test_lib_dummy.cpp) + ELSEIF("${DEPENDENCIES_LINK_TYPE}" STREQUAL "shared") + ADD_LIBRARY(boost_unit_test SHARED boost_test_lib_dummy.cpp) + ENDIF() + + SET_TARGET_PROPERTIES( + boost_unit_test + + PROPERTIES + ARCHIVE_OUTPUT_DIRECTORY $<1:${LIBRARIES_OUTPUT_DIR}> + LIBRARY_OUTPUT_DIRECTORY $<1:${LIBRARIES_OUTPUT_DIR}> + RUNTIME_OUTPUT_DIRECTORY $<1:${LIBRARIES_OUTPUT_DIR}> ) - set(PREFIX "") -# if(WIN32) -# set(PREFIX "lib") -# endif() - if(WIN32 AND "${DEPENDENCIES_LINK_TYPE}" STREQUAL "shared") - message(FATAL_ERROR "Only static linking of external dependencies is supported for Windows systems now!") - elseif("${DEPENDENCIES_LINK_TYPE}" STREQUAL "static") - add_library(${PREFIX}boost_unit_test STATIC boost_test_lib_dummy.cpp) - elseif("${DEPENDENCIES_LINK_TYPE}" STREQUAL "shared") - add_library(${PREFIX}boost_unit_test SHARED boost_test_lib_dummy.cpp) - endif() - - target_include_directories( + TARGET_INCLUDE_DIRECTORIES( ${PREFIX}boost_unit_test PRIVATE - ${EXPRTK_INCLUDE_DIR} - ${Boost_INCLUDE_DIRS} + ${Boost_INCLUDE_DIRS} ) -endif () +ENDIF() + +IF("${BUILD_TESTS}" STREQUAL "yes") + ADD_SUBDIRECTORY(tests) +ENDIF() + +IF("${BUILD_EXAMPLES}" STREQUAL "yes") + ADD_SUBDIRECTORY(examples) +ENDIF() + diff --git a/src/CSVReader/CSVReader.cpp b/src/CSVReader/CSVReader.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7eb47b2ecfafa11bdd8f09164810caf2786cadf3 --- /dev/null +++ b/src/CSVReader/CSVReader.cpp @@ -0,0 +1,138 @@ +#include <string> +#include <fstream> +#include <sstream> +#include <regex> +#include <algorithm> +#include <memory> +#include <boost/lexical_cast.hpp> +#include <boost/algorithm/string/erase.hpp> + +#include "CSVReader.h" +#include "exceptions.h" + +bool is_file_accessible(std::string file_path) { + return std::ifstream(file_path).good(); +} + +namespace lib4neuro { + CSVReader::CSVReader(std::string file_path, + std::string delimiter, + bool ignore_first_line) { + if (!is_file_accessible(file_path)) { + THROW_RUNTIME_ERROR("The file path \'" + file_path + "\' specified in CSVReader is not accessible!"); + } + + this->file_path = file_path; + this->delimiter = delimiter; + this->ignore_first_line = ignore_first_line; + this->header_included = ignore_first_line; + } + + void CSVReader::read() { + std::ifstream ifs(this->file_path); + std::string line; + + if (this->ignore_first_line) { + std::getline(ifs, + line); + } + + /* Read single line from the file */ + while (std::getline(ifs, + line)) { + + /* Ignore empty line */ + if (line == "") { + continue; + } + + /* Separate elements of the line according to the delimiter */ + size_t last = 0; + size_t next = 0; + std::vector<std::string> separated_line; + while ((next = line.find(this->delimiter, + last)) != std::string::npos) { + separated_line.emplace_back(line.substr(last, + next - last)); + last = next + 1; + } + separated_line.emplace_back(line.substr(last)); + + /* Store the elements from the line to the vector with data */ + this->data.emplace_back(separated_line); + } + + ifs.close(); + } + + std::vector<std::vector<std::string>>* CSVReader::get_data() { + return &this->data; + } + + void CSVReader::print_data() { + for (auto line : this->data) { + for (auto e : line) { + std::cout << e << " "; + } + std::cout << std::endl; + } + } + + std::shared_ptr<DataSet> CSVReader::get_data_set(std::vector<unsigned int>* input_col_indices, + std::vector<unsigned int>* output_col_indices) { + + std::vector<std::pair<std::vector<double>, std::vector<double>>> data_set_contents; + + if (this->data.empty()) { + THROW_LOGIC_ERROR("DataSet can not be created as there were no data read beforehand! Did you forget to call " + "the method 'read()'?"); + } + + for (auto line : this->data) { + //TODO check empty values in data + std::vector<double> input; + for (auto ind : *input_col_indices) { + std::string s; + + try { + /* Remove remaining spaces */ + s = line.at(ind); + boost::algorithm::erase_all(s, + " "); + + /* Strip BOM */ + // TODO solve in another way - work properly with different encodings! + boost::algorithm::erase_all(s, + "\uEFBBBF"); // UTF-8 + boost::algorithm::erase_all(s, + "\uFEFF"); // UTF-16 + + /* Check, if the string is a number */ + auto tmp = boost::lexical_cast<double>(s); + + /* Add loaded number to the vector of inputs */ + input.push_back(tmp); + + } + catch (const std::out_of_range& e) { + THROW_OUT_OF_RANGE_ERROR("Non-existing index specified (" + std::to_string(ind) + ")!"); + + } + catch (const boost::bad_lexical_cast& e) { + THROW_RUNTIME_ERROR( + "Value \"" + s + "\" is not numerical and so it cannot be used in Data Set!"); + } + } + + std::vector<double> output; + for (auto ind : *output_col_indices) { + output.emplace_back(std::stod(line.at(ind))); + } + + data_set_contents.emplace_back(std::make_pair(input, + output)); + } + + return std::make_shared<DataSet>(DataSet(&data_set_contents)); + } +} diff --git a/src/CSVReader/CSVReader.h b/src/CSVReader/CSVReader.h new file mode 100644 index 0000000000000000000000000000000000000000..69877fe888d97d8020fc612b0eac27cc3c98cb79 --- /dev/null +++ b/src/CSVReader/CSVReader.h @@ -0,0 +1,84 @@ + +#ifndef LIB4NEURO_CSVREADER_H +#define LIB4NEURO_CSVREADER_H + +#include <string> + +#include "../settings.h" +#include "../DataSet/DataSet.h" + +namespace lib4neuro { + + /** + * + */ + class CSVReader { + //TODO make more efficient - possibly with external library? + + private: + + /** + * + */ + std::string file_path; + + /** + * + */ + bool header_included; + + /** + * + */ + std::string delimiter; + + /** + * + */ + bool ignore_first_line; + + /** + * + */ + std::vector<std::vector<std::string>> data; + + public: + + /** + * + * @param file_path + * @param delimiter + * @param ignore_first_line + */ + LIB4NEURO_API CSVReader(std::string file_path, + std::string delimiter = ",", + bool ignore_first_line = false); + + /** + * + */ + LIB4NEURO_API void read(); + + /** + * + * @return + */ + LIB4NEURO_API std::vector<std::vector<std::string>>* get_data(); + + /** + * + * @param input_col_indices + * @param output_col_indices + * @return + */ + LIB4NEURO_API std::shared_ptr<DataSet> get_data_set(std::vector<unsigned int>* input_col_indices, + std::vector<unsigned int>* output_col_indices); + + /** + * + */ + LIB4NEURO_API void print_data(); + }; +} + +#endif //LIB4NEURO_CSVREADER_H diff --git a/src/CrossValidator/CrossValidator.cpp b/src/CrossValidator/CrossValidator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5ba768f53ee84a76f6d12d94a6449217f0e218d6 --- /dev/null +++ b/src/CrossValidator/CrossValidator.cpp @@ -0,0 +1,74 @@ + +#include "CrossValidator.h" +#include "message.h" + +namespace lib4neuro { + LIB4NEURO_API CrossValidator::CrossValidator(LearningMethod* optimizer, + ErrorFunction* ef) { + this->optimizer = optimizer; + this->ef = ef; + } + + LIB4NEURO_API void CrossValidator::run_k_fold_test(unsigned int k, + unsigned int tests_number, + std::ofstream* results_file_path) { + //TODO do not duplicate code - write in a more elegant way + //NeuralNetwork* net = this->ef->get_network_instance(); + + double cv_err_sum = 0; + + for (unsigned int i = 0; i < tests_number; i++) { + COUT_INFO("Cross-validation run " << i + 1 << std::endl); + *results_file_path << "Cross-validation run " << i + 1 << std::endl; + + this->ef->divide_data_train_test(1.0 / k); + *results_file_path << "Number of train data points: " << this->ef->get_n_data_set() + << std::endl; + *results_file_path << "Number of test data points: " << this->ef->get_n_test_data_set() + << std::endl; + this->ef->randomize_parameters(1.0 / ef->get_dimension()); + this->optimizer->optimize(*this->ef, + results_file_path); + + /* Error evaluation and writing */ + double err = this->ef->eval_on_test_data(results_file_path); + cv_err_sum += err; + COUT_INFO("CV error (run " << i + 1 << "): " << err << std::endl << std::endl); + + this->ef->return_full_data_set_for_training(); + } + + COUT_INFO("CV error mean: " << cv_err_sum / tests_number << std::endl); + *results_file_path << "CV error mean: " << cv_err_sum / tests_number << std::endl; + } + + LIB4NEURO_API void CrossValidator::run_k_fold_test(unsigned int k, + unsigned int tests_number, + std::string results_file_path) { + double cv_err_sum = 0; + + for (unsigned int i = 0; i < tests_number; i++) { + COUT_INFO("Cross-validation run " << i + 1 << std::endl); + + this->ef->divide_data_train_test(1.0 / k); + COUT_DEBUG("Number of train data points: " << this->ef->get_n_data_set() << std::endl); + COUT_DEBUG("Number of test data points: " << this->ef->get_n_test_data_set() << std::endl); + this->ef->randomize_parameters(1.0 / ef->get_dimension()); + this->optimizer->optimize(*this->ef); + + /* Error evaluation and writing */ + double err; + if (results_file_path == "") { + err = this->ef->eval_on_test_data(); + } else { + err = this->ef->eval_on_test_data(results_file_path + "_cv" + std::to_string(i) + ".dat"); + } + cv_err_sum += err; + COUT_INFO("CV error (run " << i + 1 << "): " << err << std::endl << std::endl); + + this->ef->return_full_data_set_for_training(); + } + + COUT_INFO("CV error mean: " << cv_err_sum / tests_number << std::endl); + } +} diff --git a/src/CrossValidator/CrossValidator.h b/src/CrossValidator/CrossValidator.h new file mode 100644 index 0000000000000000000000000000000000000000..13b34650cc074e18df05bf42ae95aacc2a40a287 --- /dev/null +++ b/src/CrossValidator/CrossValidator.h @@ -0,0 +1,60 @@ + +#ifndef LIB4NEURO_CROSSVALIDATOR_H +#define LIB4NEURO_CROSSVALIDATOR_H + +#include "../settings.h" +#include "../DataSet/DataSet.h" +#include "../LearningMethods/LearningMethod.h" + +namespace lib4neuro { + + /** + * + */ + class CrossValidator { + private: + + /** + * + */ + LearningMethod* optimizer; + + /** + * + */ + ErrorFunction* ef; + + public: + + /** + * + * @param optimizer + * @param data_set + */ + LIB4NEURO_API CrossValidator(LearningMethod* optimizer, + ErrorFunction* ef); + + /** + * + * @param k + * @param test_number + * @param results_file_path + */ + LIB4NEURO_API void + run_k_fold_test(unsigned int k, + unsigned int test_number, + std::string results_file_path = ""); + + /** + * + * @param k + * @param tests_number + * @param results_file_path + */ + LIB4NEURO_API void run_k_fold_test(unsigned int k, + unsigned int tests_number, + std::ofstream* results_file_path); + }; +} + +#endif //LIB4NEURO_CROSSVALIDATOR_H diff --git a/src/DataSet/DataSet.cpp b/src/DataSet/DataSet.cpp index 6f22cd5af01a18a2e7b1fd0d7739f56840be3ec8..a87d9ab435aad4337e6293aa11f530376c6edde3 100644 --- a/src/DataSet/DataSet.cpp +++ b/src/DataSet/DataSet.cpp @@ -1,176 +1,490 @@ -// -// Created by martin on 7/13/18. -// -#include "DataSet.h" +#include <algorithm> +#include <boost/serialization/export.hpp> -InvalidDimension::InvalidDimension() : std::runtime_error("Invalid dimension specified!") {}; +#include "DataSetSerialization.h" +#include "exceptions.h" -InvalidDimension::InvalidDimension(std::string msg) : std::runtime_error(msg.c_str()) {}; +BOOST_CLASS_EXPORT_IMPLEMENT(lib4neuro::DataSet); -DataSet::DataSet(std::string file_path) { - std::ifstream ifs (file_path); - boost::archive::text_iarchive ia(ifs); - ia >> *this; - ifs.close(); -} +namespace lib4neuro { -DataSet::DataSet(std::vector<std::pair<std::vector<double>, std::vector<double>>> *data_ptr) { - this->n_elements = data_ptr->size(); - this->data = *data_ptr; + DataSet::DataSet() { + this->n_elements = 0; + this->input_dim = 0; + this->output_dim = 0; + this->normalization_strategy = std::make_shared<DoubleUnitStrategy>(DoubleUnitStrategy()); + } - this->input_dim = this->data[0].first.size(); - this->output_dim = this->data[0].second.size(); + DataSet::DataSet(std::string file_path) { + std::ifstream ifs(file_path); + if (ifs.is_open()) { + try { + boost::archive::text_iarchive ia(ifs); + ia >> *this; + } + catch (boost::archive::archive_exception& e) { + THROW_RUNTIME_ERROR( + "Serialized archive error: '" + e.what() + "'! Please, check if your file is really " + "the serialized DataSet."); + } + ifs.close(); + } else { + THROW_RUNTIME_ERROR("File " + file_path + " couldn't be open!"); + } - //TODO check the complete data set for input/output dimensions -} + this->normalization_strategy = std::make_shared<DoubleUnitStrategy>(DoubleUnitStrategy()); -DataSet::DataSet(double lower_bound, double upper_bound, unsigned int size, double output) { - std::vector<std::pair<std::vector<double>, std::vector<double>>> new_data_vec; - this->data = new_data_vec; - this->n_elements = 0; - this->input_dim = 1; - this->output_dim = 1; + } - this->add_isotropic_data(lower_bound, upper_bound, size, output); -} + DataSet::DataSet(std::vector<std::pair<std::vector<double>, std::vector<double>>>* data_ptr, + NormalizationStrategy* ns) { + this->data.clear(); + this->n_elements = data_ptr->size(); + this->data = *data_ptr; + this->input_dim = this->data[0].first.size(); + this->output_dim = this->data[0].second.size(); + + if (ns) { + std::shared_ptr<NormalizationStrategy> ns_tmp; + ns_tmp.reset(ns); + this->normalization_strategy = ns_tmp; + } -DataSet::DataSet(std::vector<double> &bounds, unsigned int no_elems_in_one_dim, std::vector<double> (*output_func)(std::vector<double>&), unsigned int output_dim) { - std::vector<std::pair<std::vector<double>, std::vector<double>>> new_data_vec; - this->data = new_data_vec; - this->input_dim = bounds.size()/2; - this->output_dim = output_dim; - this->n_elements = 0; + //TODO check the complete data set for input/output dimensions + } - this->add_isotropic_data(bounds, no_elems_in_one_dim, output_func); -} + DataSet::DataSet(double lower_bound, + double upper_bound, + unsigned int size, + double output, + NormalizationStrategy* ns) { + std::vector<std::pair<std::vector<double>, std::vector<double>>> new_data_vec; + this->data = new_data_vec; + this->n_elements = 0; + this->input_dim = 1; + this->output_dim = 1; + + if (ns) { + std::shared_ptr<NormalizationStrategy> ns_tmp(ns); + this->normalization_strategy = ns_tmp; + } + this->add_isotropic_data(lower_bound, + upper_bound, + size, + output); + } + + DataSet::DataSet(std::vector<double>& bounds, + unsigned int no_elems_in_one_dim, + std::vector<double> (* output_func)(std::vector<double>&), + unsigned int output_dim, + NormalizationStrategy* ns) { + std::vector<std::pair<std::vector<double>, std::vector<double>>> new_data_vec; + this->data = new_data_vec; + this->input_dim = bounds.size() / 2; + this->output_dim = output_dim; + this->n_elements = 0; + + if (ns) { + std::shared_ptr<NormalizationStrategy> ns_tmp; + ns_tmp.reset(ns); + this->normalization_strategy = ns_tmp; + } -void DataSet::add_data_pair(std::vector<double> &inputs, std::vector<double> &outputs) { - if(inputs.size() != this->input_dim) { - throw InvalidDimension("Bad input dimension."); - } else if(outputs.size() != this->output_dim) { - throw InvalidDimension("Bad output dimension."); + this->add_isotropic_data(bounds, + no_elems_in_one_dim, + output_func); } - this->n_elements++; - this->data.emplace_back(std::make_pair(inputs, outputs)); -} + DataSet::~DataSet() { -void DataSet::add_isotropic_data(double lower_bound, double upper_bound, unsigned int size, double output) { - if(this->input_dim != 1 || this->output_dim != 1) { - throw InvalidDimension("Cannot add data with dimensionality 1:1 when the data set " - "is of different dimensionality!"); } - double frac = (upper_bound - lower_bound) / (size - 1); - std::vector<double> inp, out; + void DataSet::shift_outputs_to_zero() { - out = {output}; + auto first_elem = this->data.at(0).second; - for(unsigned int i = 0; i < size; ++i){ - inp = {frac*i}; - this->data.emplace_back(std::make_pair(inp, out)); + for(size_t j = 0; j < this->data.size(); ++j){ + for(size_t i = 0; i < this->get_output_dim(); ++i){ + this->data.at(j).second[i] -= first_elem[i]; + } + } } - this->n_elements += size; -} + void DataSet::add_data_pair(std::vector<double>& inputs, + std::vector<double>& outputs) { + if (this->n_elements == 0 && this->input_dim == 0 && this->output_dim == 0) { + this->input_dim = inputs.size(); + this->output_dim = outputs.size(); + } + + if (inputs.size() != this->input_dim) { + THROW_RUNTIME_ERROR("Bad input dimension."); + } else if (outputs.size() != this->output_dim) { + THROW_RUNTIME_ERROR("Bad output dimension."); + } + + this->n_elements++; + this->data.emplace_back(std::make_pair(inputs, + outputs)); + } + + void DataSet::add_isotropic_data(double lower_bound, + double upper_bound, + unsigned int size, + double output) { + + if (this->input_dim != 1 || this->output_dim != 1) { + THROW_RUNTIME_ERROR("Cannot add data with dimensionality 1:1 when the data set " + "is of different dimensionality!"); + } -void DataSet::add_isotropic_data(std::vector<double> &bounds, unsigned int no_elems_in_one_dim, std::vector<double> (*output_func)(std::vector<double>&)) { - // TODO add check of dataset dimensions + double frac; + if (size < 1) { + THROW_INVALID_ARGUMENT_ERROR("Size of added data has to be >=1 !"); + } else if (size == 1) { + frac = 1; + } else { + frac = (upper_bound - lower_bound) / (size - 1); + } + + std::vector<double> inp, out; - std::vector<std::vector<double>> grid; - std::vector<double> tmp; - double frac; + out = {output}; - for(unsigned int i = 0; i < bounds.size(); i += 2) { - frac = (bounds[i] + bounds[i+1]) / (no_elems_in_one_dim - 1); - tmp.clear(); - for(double j = bounds[i]; j <= bounds[i+1]; j += frac) { - tmp.emplace_back(j); + for (unsigned int i = 0; i < size; ++i) { + inp = {frac * i}; + this->data.emplace_back(std::make_pair(inp, + out)); } - grid.emplace_back(tmp); + this->n_elements += size; } - grid = this->cartesian_product(&grid); + void DataSet::add_isotropic_data(std::vector<double>& bounds, + unsigned int no_elems_in_one_dim, + std::vector<double> (* output_func)(std::vector<double>&)) { + // TODO add check of dataset dimensions - for(auto vec : grid) { - this->n_elements++; - this->data.emplace_back(std::make_pair(vec, output_func(vec))); + std::vector<std::vector<double>> grid; + std::vector<double> tmp; + double frac; + if (no_elems_in_one_dim < 1) { + THROW_INVALID_ARGUMENT_ERROR("Number of elements in one dimension has to be >=1 !"); + } + + for (unsigned int i = 0; i < bounds.size(); i += 2) { + if (no_elems_in_one_dim == 1) { + frac = 1; + } else { + frac = (bounds[i] - bounds[i + 1]) / (no_elems_in_one_dim - 1); + } + + tmp.clear(); + for (double j = bounds[i]; j <= bounds[i + 1]; j += frac) { + tmp.emplace_back(j); + } + + grid.emplace_back(tmp); + } + + grid = this->cartesian_product(&grid); + + for (auto vec : grid) { + this->n_elements++; + this->data.emplace_back(std::make_pair(vec, + output_func(vec))); + } } -} -std::vector<std::pair<std::vector<double>, std::vector<double>>>* DataSet::get_data() { - return &(this->data); -} + std::vector<std::pair<std::vector<double>, std::vector<double>>>* DataSet::get_data() { + return &(this->data); + } -size_t DataSet::get_n_elements() { - return this->n_elements; -} + size_t DataSet::get_n_elements() { + return this->n_elements; + } -size_t DataSet::get_input_dim() { - return this->input_dim; -} + size_t DataSet::get_input_dim() { + return this->input_dim; + } -size_t DataSet::get_output_dim() { - return this->output_dim; -} + size_t DataSet::get_output_dim() { + return this->output_dim; + } -void DataSet::print_data() { - if (n_elements) { - for (auto p : this->data) { - /* INPUT */ - for (auto v : std::get<0>(p)) { - std::cout << v << " "; - } + void DataSet::print_data() { + if (n_elements) { + for (auto p : this->data) { + /* INPUT */ + for (auto v : std::get<0>(p)) { + std::cout << v << " "; + } - std::cout << "-> "; + std::cout << "-> "; - /* OUTPUT */ - for (auto v : std::get<1>(p)) { - std::cout << v << " "; + /* OUTPUT */ + for (auto v : std::get<1>(p)) { + std::cout << v << " "; + } + + std::cout << std::endl; } + } + } - std::cout << std::endl; + void DataSet::store_text(std::string file_path) { + std::ofstream ofs(file_path); + + if (!ofs.is_open()) { + THROW_RUNTIME_ERROR("File " + file_path + " couldn't be open!"); + } else { + boost::archive::text_oarchive oa(ofs); + oa << *this; + ofs.close(); } } -} -void DataSet::store_text(std::string &file_path) { - //TODO check if stream was successfully opened - std::ofstream ofs(file_path); - boost::archive::text_oarchive oa(ofs); - oa << *this; - ofs.close(); -} + void DataSet::store_data_text(std::ofstream* file_path) { + for (auto e : this->data) { + /* First part of the pair */ + for (unsigned int i = 0; i < e.first.size() - 1; i++) { + *file_path << this->get_denormalized_value(e.first.at(i)) << ","; + } + *file_path << this->get_denormalized_value(e.first.back()) << " "; + + /* Second part of the pair */ + for (unsigned int i = 0; i < e.second.size() - 1; i++) { + *file_path << this->get_denormalized_value(e.second.at(i)) << ","; + } + *file_path << this->get_denormalized_value(e.second.back()) << std::endl; + } + } -template <class T> -std::vector<std::vector<T>> DataSet::cartesian_product(const std::vector<std::vector<T>>* v) { - std::vector<std::vector<double>> v_combined_old, v_combined, v_tmp; - std::vector<double> tmp; + void DataSet::store_data_text(std::string file_path) { + std::ofstream ofs(file_path); - for(const auto& e : v->at(0)) { - tmp = {e}; - v_combined.emplace_back(tmp); + if (!ofs.is_open()) { + THROW_RUNTIME_ERROR("File " + file_path + " couldn't be open!"); + } else { + this->store_data_text(&ofs); + ofs.close(); + } } - for(unsigned int i = 1; i < v->size(); i++) { // Iterate through remaining vectors of 'v' - v_combined_old = v_combined; - v_combined.clear(); + template<class T> + std::vector<std::vector<T>> DataSet::cartesian_product(const std::vector<std::vector<T>>* v) { + std::vector<std::vector<double>> v_combined_old, v_combined, v_tmp; + std::vector<double> tmp; - for(const auto& e : v->at(i)) { - for(const auto& vec : v_combined_old) { - tmp = vec; - tmp.emplace_back(e); + for (const auto& e : v->at(0)) { + tmp = {e}; + v_combined.emplace_back(tmp); + } - /* Add only unique elements */ - if(std::find(v_combined.begin(), v_combined.end(), tmp) == v_combined.end()) { - v_combined.emplace_back(tmp); + for (unsigned int i = 1; i < v->size(); i++) { // Iterate through remaining vectors of 'v' + v_combined_old = v_combined; + v_combined.clear(); + + for (const auto& e : v->at(i)) { + for (const auto& vec : v_combined_old) { + tmp = vec; + tmp.emplace_back(e); + + /* Add only unique elements */ + if (std::find(v_combined.begin(), + v_combined.end(), + tmp) == v_combined.end()) { + v_combined.emplace_back(tmp); + } } } } + + return v_combined; + } + + void DataSet::normalize() { + this->normalized = false; + if (!this->normalization_strategy) { + THROW_INVALID_ARGUMENT_ERROR("There is no normalization strategy given for this data set, so it can not be " + "normalized!"); + } + + /* Find maximum and minimum values */ + if (this->max_min_inp_val.empty()) { + this->max_min_inp_val.emplace_back(this->data.at(0).first.at(0)); + this->max_min_inp_val.emplace_back(this->data.at(0).first.at(0)); + } + + double tmp, tmp2; + for (auto pair : this->data) { + /* Finding maximum */ + //TODO make more efficiently + tmp = *std::max_element(pair.first.begin(), + pair.first.end()); + tmp2 = *std::max_element(pair.second.begin(), + pair.second.end()); + + tmp = std::max(tmp, + tmp2); + + /* Testing for a new maxima */ + if (tmp > this->max_min_inp_val.at(0)) { + this->max_min_inp_val.at(0) = tmp; + } + + /* Finding minimum */ + tmp = *std::min_element(pair.first.begin(), + pair.first.end()); + tmp2 = *std::min_element(pair.second.begin(), + pair.second.end()); + + tmp = std::min(tmp, + tmp2); + + /* Testing for a new minima */ + if (tmp < this->max_min_inp_val.at(1)) { + this->max_min_inp_val.at(1) = tmp; + } + } + + /* Normalize every number in the data set */ + for (auto& pair : this->data) { + for (auto& v : pair.first) { + v = this->normalization_strategy->normalize(v, + this->max_min_inp_val.at(0), + this->max_min_inp_val.at(1)); + } + + for (auto& v : pair.second) { + v = this->normalization_strategy->normalize(v, + this->max_min_inp_val.at(0), + this->max_min_inp_val.at(1)); + } + } + + this->normalized = true; + + } + + double DataSet::get_normalized_value(double val) { + if (!this->normalized || !this->normalization_strategy) { + return val; + } + return this->normalization_strategy->normalize(val, + this->max_min_inp_val.at(0), + this->max_min_inp_val.at(1)); + } + + double DataSet::get_denormalized_value(double val) { + if (!this->normalized || !this->normalization_strategy) { + return val; + } + return this->normalization_strategy->de_normalize(val); } - return v_combined; -} \ No newline at end of file + void DataSet::get_input(std::vector<double>& d, + size_t idx) { + assert(d.size() == this->data[idx].first.size()); + for (size_t j = 0; j < this->data[idx].first.size(); ++j) { + d[j] = this->data[idx].first[j]; + } + } + + void DataSet::get_output(std::vector<double>& d, + size_t idx) { + assert(d.size() == this->data[idx].second.size()); + for (size_t j = 0; j < this->data[idx].second.size(); ++j) { + d[j] = this->data[idx].second[j]; + } + } + + void DataSet::de_normalize() { + std::vector<double> tmp_inp(this->data.at(0).first.size()); + std::vector<double> tmp_out(this->data.at(0).second.size()); + + for (auto& pair: this->data) { + for (size_t i = 0; i < pair.first.size(); i++) { + tmp_inp.at(i) = this->normalization_strategy->de_normalize(pair.first.at(i)); + } + pair.first = tmp_inp; + } + + for (auto& pair: this->data) { + for (size_t i = 0; i < pair.second.size(); i++) { + tmp_out.at(i) = this->normalization_strategy->de_normalize(pair.second.at(i)); + } + pair.second = tmp_out; + } + + /* Remove found max and minimal values, because of is_normalized() method */ + this->max_min_inp_val.clear(); + } + + void DataSet::de_normalize_single(std::vector<double>& d1, + std::vector<double>& d2) { + assert(d1.size() == d2.size()); + for (size_t j = 0; j < d1.size(); ++j) { + d2[j] = this->normalization_strategy->de_normalize(d1[j]); + } + } + + NormalizationStrategy* DataSet::get_normalization_strategy() { + return this->normalization_strategy.get(); + } + + void DataSet::set_normalization_strategy(NormalizationStrategy* ns) { + if (ns) { + this->normalization_strategy.reset(ns); + } + } + + bool DataSet::is_normalized() { + return !this->max_min_inp_val.empty(); + } + + double DataSet::get_max_inp_val() { + return this->max_min_inp_val.at(0); + } + + double DataSet::get_min_inp_val() { + return this->max_min_inp_val.at(1); + } + + /** + * Method returning random amount of data pairs between 1-max + */ + std::vector<std::pair<std::vector<double>, std::vector<double>>> DataSet::get_random_data_batch(size_t max) { + if (max <= 0) { + return this->data; + } else { + std::vector<std::pair<std::vector<double>, std::vector<double>>> newData; + srand(time(NULL)); //TODO use Mersen twister from Boost + + size_t n_chosen = rand() % std::min(max, + this->data.size()) + 1; + n_chosen = max; + std::vector<size_t> chosens; + size_t chosen; + + for (size_t i = 0; i < n_chosen; i++) { + chosen = rand() % this->data.size(); + auto it = std::find(chosens.begin(), + chosens.end(), + chosen); + + if (it != chosens.end()) { + i--; + } else { + newData.push_back(this->data.at(chosen)); + chosens.push_back(chosen); + } + } + + return newData; + } + } +} diff --git a/src/DataSet/DataSet.h b/src/DataSet/DataSet.h index 92418b6ec6d5297b76cf66c7230ba9c9061472ce..aa850d8d9b3eb16ab91c691d241f395712f2a05c 100644 --- a/src/DataSet/DataSet.h +++ b/src/DataSet/DataSet.h @@ -1,204 +1,313 @@ -// -// Created by martin on 7/13/18. -// #ifndef INC_4NEURO_DATASET_H #define INC_4NEURO_DATASET_H -#include "../settings.h" - #include <iostream> #include <fstream> #include <utility> #include <vector> -#include <exception> #include <string> #include <functional> -#include <boost/serialization/base_object.hpp> -#include <boost/range/size_type.hpp> -#include <boost/serialization/vector.hpp> -#include <boost/serialization/utility.hpp> -#include <boost/archive/text_oarchive.hpp> -#include <boost/archive/text_iarchive.hpp> +#include <limits> +#include <memory> +#include "../settings.h" +#include "../NormalizationStrategy/NormalizationStrategy.h" -/** - * Class representing an error caused by an incorrect - * input/output dimension specification - */ -class InvalidDimension: public std::runtime_error { -public: +namespace lib4neuro { /** - * Constructor with the general error message + * Class representing data, which can be used for training + * and testing purposes. */ - LIB4NEURO_API InvalidDimension(); + class DataSet { - /** - * Constructor with specific error message - * @param msg Specific error message - */ - LIB4NEURO_API explicit InvalidDimension(std::string msg); -}; + private: -/** - * Class representing data, which can be used for training - * and testing purposes. - */ -class DataSet { - friend class boost::serialization::access; + /** + * Number of elements in the data set + */ + size_t n_elements = 0; -private: - /** - * Number of elements in the data set - */ - size_t n_elements; + /** + * Dimension of the input + */ + size_t input_dim = 0; - /** - * Dimension of the input - */ - size_t input_dim = 0; + /** + * Dimension of the output + */ + size_t output_dim = 0; - /** - * Dimension of the output - */ - size_t output_dim = 0; - /** - * Stored data in the format of pairs of corresponding - * input and output vectors - */ - std::vector<std::pair<std::vector<double>, std::vector<double>>> data; + bool normalized = false; - template <class T> - std::vector<std::vector<T>> cartesian_product(const std::vector<std::vector<T>>* v); + /** + * Maximum (index 0) and minimum (index 1) input value + */ + std::vector<double> max_min_inp_val; //TODO make more efficiently, than by vector! -protected: - /** - * Serialization function - * @tparam Archive Boost library template - * @param ar Boost parameter - filled automatically during serialization! - * @param version Boost parameter - filled automatically during serialization! - */ - template<class Archive> - void serialize(Archive & ar, const unsigned int version){ - ar & this->n_elements; - ar & this->input_dim; - ar & this->output_dim; - ar & this->data; - }; + /** + * Stored data in the format of pairs of corresponding + * input and output vectors + */ + std::vector<std::pair<std::vector<double>, std::vector<double>>> data; -public: + /** + * + * @tparam T + * @param v + * @return + */ + template<class T> + std::vector<std::vector<T>> cartesian_product(const std::vector<std::vector<T>>* v); - /** - * Constructor reading data from the file - * @param file_path Path to the file with stored data set - */ - LIB4NEURO_API DataSet(std::string file_path); + /** + * + */ + //TODO let user choose in the constructor! + std::shared_ptr<NormalizationStrategy> normalization_strategy; - /** - * Constructor accepting data vector - * @param data_ptr Pointer to the vector containing data - */ - LIB4NEURO_API DataSet(std::vector<std::pair<std::vector<double>, std::vector<double>>>* data_ptr); - /** - * Creates a new data set with input values equidistantly positioned - * over the certain interval and the output value - * being constant - * - * Both input and output are 1-dimensional - * - * @todo add bounds as vectors for multi-dimensional data-sets - * - * @param lower_bound Lower bound of the input data interval - * @param upper_bound Upper bound of the input data interval - * @param size Number of input-output pairs generated - * @param output Constant output value - */ - LIB4NEURO_API DataSet(double lower_bound, double upper_bound, unsigned int size, double output); + public: - /** - * - * @param bounds - * @param no_elems_in_one_dim - * @param output_func - * @param output_dim - */ - LIB4NEURO_API DataSet(std::vector<double> &bounds, unsigned int no_elems_in_one_dim, std::vector<double> (*output_func)(std::vector<double>&), unsigned int output_dim); + /** + * Struct used to access private properties from + * the serialization function + */ + struct access; - /** - * Getter for number of elements - * @return Number of elements in the data set - */ - LIB4NEURO_API size_t get_n_elements(); + /** + * Constructor for an empty DataSet + */ + LIB4NEURO_API DataSet(); - /** - * Returns the input dimension - * @return Input dimension - */ - LIB4NEURO_API size_t get_input_dim(); + /** + * Constructor reading data from the file + * @param file_path Path to the file with stored data set + */ + LIB4NEURO_API DataSet(std::string file_path); + /** + * Constructor accepting data vector + * @param data_ptr Pointer to the vector containing data + */ + LIB4NEURO_API DataSet(std::vector<std::pair<std::vector<double>, std::vector<double>>>* data_ptr, + NormalizationStrategy* ns = nullptr); - /** - * Return the output dimension - * @return Output dimension - */ - LIB4NEURO_API size_t get_output_dim(); + /** + * Creates a new data set with input values equidistantly positioned + * over the certain interval and the output value + * being constant + * + * Both input and output are 1-dimensional + * + * @todo add bounds as vectors for multi-dimensional data-sets + * + * @param lower_bound Lower bound of the input data interval + * @param upper_bound Upper bound of the input data interval + * @param size Number of input-output pairs generated + * @param output Constant output value + */ + LIB4NEURO_API DataSet(double lower_bound, + double upper_bound, + unsigned int size, + double output, + NormalizationStrategy* ns = nullptr); - /** - * Getter for the data structure - * @return Vector of data - */ - LIB4NEURO_API std::vector<std::pair<std::vector<double>, std::vector<double>>>* get_data(); + LIB4NEURO_API ~DataSet(); - /** - * Adds a new pair of data to the data set - * @param inputs Vector of input data - * @param outputs Vector of output data corresponding to the input data - */ - LIB4NEURO_API void add_data_pair(std::vector<double> &inputs, std::vector<double> &outputs); + /** + * + * @param bounds + * @param no_elems_in_one_dim + * @param output_func + * @param output_dim + */ + LIB4NEURO_API DataSet(std::vector<double>& bounds, + unsigned int no_elems_in_one_dim, + std::vector<double> (* output_func)(std::vector<double>&), + unsigned int output_dim, + NormalizationStrategy* ns = nullptr); - //TODO expand method to generate multiple data types - chebyshev etc. - /** - * Adds a new data with input values equidistantly positioned - * over the certain interval and the output value - * being constant - * - * Both input and output are 1-dimensional - * - * @param lower_bound Lower bound of the input data interval - * @param upper_bound Upper bound of the input data interval - * @param size Number of input-output pairs generated - * @param output Constant output value - */ - LIB4NEURO_API void add_isotropic_data(double lower_bound, double upper_bound, unsigned int size, double output); + /** + * + */ + LIB4NEURO_API void shift_outputs_to_zero(); - /** - * Adds a new data with input values equidistantly positioned - * over the certain interval and the output value - * being constant - * - * Input can have arbitrary many dimensions, - * output can be an arbitrary function - * - * @param bounds Odd values are lower bounds and even values are corresponding upper bounds - * @param size Number of input-output pairs generated - * @param output_func Function determining output value - */ - LIB4NEURO_API void add_isotropic_data(std::vector<double> &bounds, unsigned int no_elems_in_one_dim, std::vector<double> (*output_func)(std::vector<double>&)); + /** + * Getter for number of elements + * @return Number of elements in the data set + */ + LIB4NEURO_API size_t get_n_elements(); - //TODO Chebyshev - ch. interpolation points, i-th point = cos(i*alpha) from 0 to pi + /** + * Returns the input dimension + * @return Input dimension + */ + LIB4NEURO_API size_t get_input_dim(); - /** - * Prints the data set - */ - LIB4NEURO_API void print_data(); + /** + * Return the output dimension + * @return Output dimension + */ + LIB4NEURO_API size_t get_output_dim(); - /** - * Stores the DataSet object to the binary file - */ - LIB4NEURO_API void store_text(std::string &file_path); -}; + /** + * Getter for the data structure + * @return Vector of data + */ + LIB4NEURO_API std::vector<std::pair<std::vector<double>, std::vector<double>>>* get_data(); + + /** + * Adds a new pair of data to the data set + * @param inputs Vector of input data + * @param outputs Vector of output data corresponding to the input data + */ + LIB4NEURO_API void add_data_pair(std::vector<double>& inputs, + std::vector<double>& outputs); + + //TODO expand method to generate multiple data types - chebyshev etc. + /** + * Adds a new data with input values equidistantly positioned + * over the certain interval and the output value + * being constant + * + * Both input and output are 1-dimensional + * + * @param lower_bound Lower bound of the input data interval + * @param upper_bound Upper bound of the input data interval + * @param size Number of input-output pairs generated + * @param output Constant output value + */ + LIB4NEURO_API void add_isotropic_data(double lower_bound, + double upper_bound, + unsigned int size, + double output); + + /** + * Adds a new data with input values equidistantly positioned + * over the certain interval and the output value + * being constant + * + * Input can have arbitrary many dimensions, + * output can be an arbitrary function + * + * @param bounds Odd values are lower bounds and even values are corresponding upper bounds + * @param size Number of input-output pairs generated + * @param output_func Function determining output value + */ + LIB4NEURO_API void add_isotropic_data(std::vector<double>& bounds, + unsigned int no_elems_in_one_dim, + std::vector<double> (* output_func)(std::vector<double>&)); + + //TODO Chebyshev - ch. interpolation points, i-th point = cos(i*alpha) from 0 to pi + + /** + * Prints the data set + */ + LIB4NEURO_API void print_data(); + + /** + * Stores the DataSet object to the binary file + * + */ + LIB4NEURO_API void store_text(std::string file_path); + /** + * + * @param file_path + */ + LIB4NEURO_API void store_data_text(std::ofstream* file_path); + + /** + * Stores the data to the text file in a human readable format + * + * @param file_path + */ + LIB4NEURO_API void store_data_text(std::string file_path); + + /** + * Normalizes the data set + */ + LIB4NEURO_API void normalize(); + + /** + * returns the normalized value of @val + * @param val + * @return + */ + LIB4NEURO_API double get_normalized_value(double val); + + /** + * + * @param val + * @return + */ + LIB4NEURO_API double get_denormalized_value(double val); + + /** + * Denormalizes the data set + */ + LIB4NEURO_API void de_normalize(); + + /** + * stores the de-normalized vector @d1 into @d2 + * @param d1 + * @param d2 + */ + LIB4NEURO_API void de_normalize_single(std::vector<double>& d1, + std::vector<double>& d2); + + /** + * stores the @idx-th input in the vector @d + * @param d + * @param idx + */ + LIB4NEURO_API void get_input(std::vector<double>& d, + size_t idx); + + /** + * stores the @idx-th output in the vector @d + * @param d + * @param idx + */ + LIB4NEURO_API void get_output(std::vector<double>& d, + size_t idx); + + /** + * + * @return + */ + LIB4NEURO_API NormalizationStrategy* get_normalization_strategy(); + + LIB4NEURO_API void set_normalization_strategy(NormalizationStrategy* ns); + + /** + * + * @return + */ + LIB4NEURO_API bool is_normalized(); + + /** + * + * @return + */ + LIB4NEURO_API double get_max_inp_val(); + + /** + * + * @return + */ + LIB4NEURO_API double get_min_inp_val(); + + /** + * + * @param max + * @return + */ + LIB4NEURO_API std::vector<std::pair<std::vector<double>, std::vector<double>>> + get_random_data_batch(size_t max); + }; +} #endif //INC_4NEURO_DATASET_H diff --git a/src/DataSet/DataSetSerialization.h b/src/DataSet/DataSetSerialization.h new file mode 100644 index 0000000000000000000000000000000000000000..89a8a8beba57f9fd0ca647b126a83b4c7acc938d --- /dev/null +++ b/src/DataSet/DataSetSerialization.h @@ -0,0 +1,56 @@ + +#ifndef LIB4NEURO_DATASETSERIALIZATION_H +#define LIB4NEURO_DATASETSERIALIZATION_H + +#include <boost/serialization/base_object.hpp> +#include <boost/range/size_type.hpp> +#include <boost/serialization/vector.hpp> +#include <boost/serialization/utility.hpp> +#include <boost/archive/text_oarchive.hpp> +#include <boost/archive/text_iarchive.hpp> +#include <boost/serialization/export.hpp> +#include <boost/serialization/shared_ptr.hpp> + +#include "DataSet.h" + +BOOST_CLASS_EXPORT_KEY(lib4neuro::DataSet); + +namespace lib4neuro { + struct DataSet::access { + template<class Archive> + static void serialize(Archive& ar, + DataSet& ds, + const unsigned int version) { + ar & ds.n_elements; + ar & ds.input_dim; + ar & ds.output_dim; + ar & ds.data; + ar & ds.max_min_inp_val; + ar & ds.normalization_strategy; + } + }; +} + +namespace boost { + namespace serialization { + + /** + * Serialization function + * @tparam Archive Boost library template + * @param ar Boost parameter - filled automatically during serialization! + * @param ds DataSet instance + * @param version Boost parameter - filled automatically during serialization! + */ + template<class Archive> + void serialize(Archive& ar, + lib4neuro::DataSet& ds, + const unsigned int version) { + lib4neuro::DataSet::access::serialize(ar, + ds, + version); + } + + } // namespace serialization +} // namespace boost + +#endif //LIB4NEURO_DATASETSERIALIZATION_H diff --git a/src/ErrorFunction/ErrorFunctions.cpp b/src/ErrorFunction/ErrorFunctions.cpp index 54d3f04610d91b5688a6a76a59e633f0145a7764..45139360e8460a2ccd5e6b35b8a5885fb5494589 100644 --- a/src/ErrorFunction/ErrorFunctions.cpp +++ b/src/ErrorFunction/ErrorFunctions.cpp @@ -1,114 +1,827 @@ -// -// Created by martin on 7/15/18. -// #include <vector> +#include <cmath> +#include <sstream> +#include <boost/random/mersenne_twister.hpp> +#include <boost/random/uniform_int_distribution.hpp> #include "ErrorFunctions.h" +#include "exceptions.h" +#include "message.h" +namespace lib4neuro { -size_t ErrorFunction::get_dimension() { - return this->dimension; -} + size_t ErrorFunction::get_dimension() { + return this->dimension; + } -MSE::MSE(NeuralNetwork *net, DataSet *ds) { - this->net = net; - this->ds = ds; - this->dimension = net->get_n_weights() + net->get_n_biases(); -} + void MSE::divide_data_train_test(double percent_test) { + size_t ds_size = this->ds->get_n_elements(); -double MSE::eval(std::vector<double> *weights) { - unsigned int dim_out = this->ds->get_output_dim(); -// unsigned int dim_in = this->ds->get_input_dim(); - size_t n_elements = this->ds->get_n_elements(); - double error = 0.0, val; + /* Store the full data set */ + this->ds_full = this->ds; - std::vector<std::pair<std::vector<double>, std::vector<double>>>* data = this->ds->get_data(); + /* Choose random subset of the DataSet for training and the remaining part for validation */ + boost::random::mt19937 gen; + boost::random::uniform_int_distribution<> dist(0, + ds_size - 1); -// //TODO instead use something smarter -// this->net->copy_weights(weights); + size_t test_set_size = ceil(ds_size * percent_test); - std::vector<double> output( dim_out ); + std::vector<unsigned int> test_indices; + test_indices.reserve(test_set_size); + for (unsigned int i = 0; i < test_set_size; i++) { + test_indices.emplace_back(dist(gen)); + } + std::sort(test_indices.begin(), + test_indices.end(), + std::greater<unsigned int>()); - for(unsigned int i = 0; i < n_elements; ++i){ // Iterate through every element in the test set + std::vector<std::pair<std::vector<double>, std::vector<double>>> test_data, train_data; - this->net->eval_single(data->at(i).first, output, weights); // Compute the net output and store it into 'output' variable + /* Copy all the data to train_data */ + for (auto e : *this->ds_full->get_data()) { + train_data.emplace_back(e); + } + /* Move the testing data from train_data to test_data */ + for (auto ind : test_indices) { + test_data.emplace_back(train_data.at(ind)); + train_data.erase(train_data.begin() + ind); + } -// printf("errors: "); - for(unsigned int j = 0; j < dim_out; ++j) { // Compute difference for every element of the output vector + /* Re-initialize data set for training */ + this->ds = new DataSet(&train_data, + this->ds_full->get_normalization_strategy()); - val = output[j] - data->at(i).second[j]; - error += val * val; + /* Initialize test data */ + this->ds_test = new DataSet(&test_data, + this->ds_full->get_normalization_strategy()); + } -// printf("%f, ", val * val); + void MSE::return_full_data_set_for_training() { + if (this->ds_test) { + this->ds = this->ds_full; } -// printf("\n"); + } + + void MSE::get_jacobian_and_rhs(std::vector<std::vector<double>>& jacobian, + std::vector<double>& rhs) { +// size_t row_idx = 0; + std::vector<double> partial_error(this->get_n_outputs()); + rhs.resize(this->get_dimension()); + std::fill(rhs.begin(), + rhs.end(), + 0.0); + + std::vector<std::vector<double>> jac_loc; + for (auto item: *this->ds->get_data()) { + + this->nets[0]->get_jacobian(jac_loc, + item, + partial_error); + for (size_t ri = 0; ri < jac_loc.size(); ++ri) { + jacobian.push_back(jac_loc[ri]); + + for (size_t ci = 0; ci < this->get_dimension(); ++ci) { +// J.at(row_idx, +// ci) = jacobian[ri][ci]; + rhs.at(ci) += partial_error[ri] * jac_loc[ri][ci]; + } +// row_idx++; + } + } } -// printf("n_elements: %d\n", n_elements); - return error/n_elements; -} + MSE::MSE(NeuralNetwork* net, + DataSet* ds) { + this->nets.push_back(net); + this->ds = ds; + this->dimension = net->get_n_weights() + net->get_n_biases(); + } -ErrorSum::ErrorSum() { - this->summand = nullptr; - this->summand_coefficient = nullptr; - this->dimension = 0; -} + double MSE::eval_on_single_input(std::vector<double>* input, + std::vector<double>* output, + std::vector<double>* weights) { + std::vector<double> predicted_output(this->nets[0]->get_n_outputs()); + this->nets[0]->eval_single(*input, + predicted_output, + weights); + double result = 0; + double val; + + for (size_t i = 0; i < output->size(); i++) { + val = output->at(i) - predicted_output.at(i); + result += val * val; + } + + return std::sqrt(result); + } + + double MSE::eval_on_data_set(lib4neuro::DataSet* data_set, + std::ofstream* results_file_path, + std::vector<double>* weights, + bool verbose + ) { + size_t dim_in = data_set->get_input_dim(); + size_t dim_out = data_set->get_output_dim(); + double error = 0.0, val, output_norm = 0; + + std::vector<std::pair<std::vector<double>, std::vector<double>>>* data = data_set->get_data(); + size_t n_elements = data->size(); + + //TODO instead use something smarter + std::vector<std::vector<double>> outputs(data->size()); + std::vector<double> output(dim_out); + + if (verbose) { + COUT_DEBUG("Evaluation of the error function MSE on the given data-set" << std::endl); + COUT_DEBUG(R_ALIGN << "[Element index]" << " " + << R_ALIGN << "[Input]" << " " + << R_ALIGN << "[Real output]" << " " + << R_ALIGN << "[Predicted output]" << " " + << R_ALIGN << "[Absolute error]" << " " + << R_ALIGN << "[Relative error %]" + << std::endl); + } + + if (results_file_path) { + *results_file_path << R_ALIGN << "[Element index]" << " " + << R_ALIGN << "[Input]" << " " + << R_ALIGN << "[Real output]" << " " + << R_ALIGN << "[Predicted output]" << " " + << R_ALIGN << "[Abs. error]" << " " + << R_ALIGN << "[Rel. error %]" + << std::endl; + } + + for (size_t i = 0; i < data->size(); i++) { // Iterate through every element in the test set + /* Compute the net output and store it into 'output' variable */ + this->nets[0]->eval_single(data->at(i).first, + output, + weights); -ErrorSum::~ErrorSum(){ - if( this->summand ){ - delete this->summand; + outputs.at(i) = output; + } + + double denormalized_output; + double denormalized_real_input; + double denormalized_real_output; + + std::string separator = ""; + for (size_t i = 0; i < data->size(); i++) { + + /* Compute difference for every element of the output vector */ + std::stringstream ss_input; + for (size_t j = 0; j < dim_in; j++) { + denormalized_real_input = data_set->get_denormalized_value(data->at(i).first.at(j)); + ss_input << separator << denormalized_real_input; + separator = ","; + } + + std::stringstream ss_real_output; + std::stringstream ss_predicted_output; + + double loc_error = 0; + output_norm = 0; + separator = ""; + for (size_t j = 0; j < dim_out; ++j) { + denormalized_real_output = data_set->get_denormalized_value(data->at(i).second.at(j)); + denormalized_output = data_set->get_denormalized_value(outputs.at(i).at(j)); + + ss_real_output << separator << denormalized_real_output; + ss_predicted_output << separator << denormalized_output; + separator = ","; + + val = denormalized_output - denormalized_real_output; + loc_error += val * val; + error += loc_error; + + output_norm += denormalized_output * denormalized_output; + } + + std::stringstream ss_ind; + ss_ind << "[" << i << "]"; +#ifdef L4N_DEBUG + + if (verbose) { + COUT_DEBUG(R_ALIGN << ss_ind.str() << " " + << R_ALIGN << ss_input.str() << " " + << R_ALIGN << ss_real_output.str() << " " + << R_ALIGN << ss_predicted_output.str() << " " + << R_ALIGN << std::sqrt(loc_error) << " " + << R_ALIGN + << 200.0 * std::sqrt(loc_error) / (std::sqrt(loc_error) + std::sqrt(output_norm)) + << std::endl); + } + + +#endif + if (results_file_path) { + *results_file_path << R_ALIGN << ss_ind.str() << " " + << R_ALIGN << ss_input.str() << " " + << R_ALIGN << ss_real_output.str() << " " + << R_ALIGN << ss_predicted_output.str() << " " + << R_ALIGN << std::sqrt(loc_error) << " " + << R_ALIGN + << 200.0 * std::sqrt(loc_error) / (std::sqrt(loc_error) + std::sqrt(output_norm)) + << std::endl; + } + } + + double result = std::sqrt(error) / n_elements; + + if (verbose) { + COUT_DEBUG("MSE = " << result << std::endl); + } + + if (results_file_path) { + *results_file_path << "MSE = " << result << std::endl; + } + + return result; + } + + double MSE::eval_on_data_set(DataSet* data_set, + std::string results_file_path, + std::vector<double>* weights, + bool verbose) { + std::ofstream ofs(results_file_path); + if (ofs.is_open()) { + return this->eval_on_data_set(data_set, + &ofs, + weights, + + verbose); + ofs.close(); + } else { + THROW_RUNTIME_ERROR("File " + results_file_path + " couldn't be open!"); + } + + return -1.0; } - if( this->summand_coefficient ){ - delete this->summand_coefficient; + + double MSE::eval_on_data_set(DataSet* data_set, + std::vector<double>* weights, + bool verbose) { + return this->eval_on_data_set(data_set, + nullptr, + weights, + + verbose); } -} -double ErrorSum::eval(std::vector<double> *weights) { - double output = 0.0; - ErrorFunction *ef = nullptr; + double MSE::eval(std::vector<double>* weights, + bool denormalize_data, + bool verbose) { + return this->eval_on_data_set(this->ds, + nullptr, + weights, + verbose); + } + + double MSE::eval_on_test_data(std::vector<double>* weights, + bool verbose) { + return this->eval_on_data_set(this->ds_test, + weights, + verbose); + } + + double MSE::eval_on_test_data(std::string results_file_path, + std::vector<double>* weights, + bool verbose) { + return this->eval_on_data_set(this->ds_test, + results_file_path, + weights, + verbose); + } + + double MSE::eval_on_test_data(std::ofstream* results_file_path, + std::vector<double>* weights, + bool verbose) { + return this->eval_on_data_set(this->ds_test, + results_file_path, + weights, + + verbose); + } + + void + MSE::calculate_error_gradient(std::vector<double>& params, + std::vector<double>& grad, + double alpha, + size_t batch) { + + size_t dim_out = this->ds->get_output_dim(); + size_t n_elements = this->ds->get_n_elements(); + std::vector<std::pair<std::vector<double>, std::vector<double>>>* data = this->ds->get_data(); + + if (batch > 0) { + *data = this->ds->get_random_data_batch(batch); + n_elements = data->size(); + } + std::vector<double> error_derivative(dim_out); + + for (auto el: *data) { // Iterate through every element in the test set + + this->nets[0]->eval_single(el.first, + error_derivative, + ¶ms); // Compute the net output and store it into 'output' variable - for( unsigned int i = 0; i < this->summand->size(); ++i ){ - ef = this->summand->at( i ); + for (size_t j = 0; j < dim_out; ++j) { + error_derivative.at(j) = 2.0 * (error_derivative.at(j) - el.second.at(j)); //real - expected result + } - if( ef ){ - output += ef->eval( weights ) * this->summand_coefficient->at( i ); + this->nets[0]->add_to_gradient_single(el.first, + error_derivative, + alpha / n_elements, + grad); } } - return output; -} + double MSE::calculate_single_residual(std::vector<double>* input, + std::vector<double>* output, + std::vector<double>* parameters) { + + //TODO maybe move to the general ErrorFunction + //TODO check input vector sizes - they HAVE TO be allocated before calling this function + + return -this->eval_on_single_input(input, + output, + parameters); + } + + void MSE::calculate_residual_gradient(std::vector<double>* input, + std::vector<double>* output, + std::vector<double>* gradient, + double h) { + + //TODO check input vector sizes - they HAVE TO be allocated before calling this function + + size_t n_parameters = this->get_dimension(); + std::vector<double> parameters = this->get_parameters(); + + double delta; // Complete step size + double former_parameter_value; + double f_val1; // f(x + delta) + double f_val2; // f(x - delta) + + for (size_t i = 0; i < n_parameters; i++) { + delta = h * (1 + std::abs(parameters.at(i))); + former_parameter_value = parameters.at(i); + + if (delta != 0) { + /* Computation of f_val1 = f(x + delta) */ + parameters.at(i) = former_parameter_value + delta; + f_val1 = this->calculate_single_residual(input, + output, + ¶meters); + + /* Computation of f_val2 = f(x - delta) */ + parameters.at(i) = former_parameter_value - delta; + f_val2 = this->calculate_single_residual(input, + output, + ¶meters); + + gradient->at(i) = (f_val1 - f_val2) / (2 * delta); + } + + /* Restore parameter to the former value */ + parameters.at(i) = former_parameter_value; + } + } + + void MSE::calculate_error_gradient_single(std::vector<double>& error_vector, + std::vector<double>& gradient_vector) { + std::fill(gradient_vector.begin(), + gradient_vector.end(), + 0); + std::vector<double> dummy_input; + this->nets[0]->add_to_gradient_single(dummy_input, + error_vector, + 1.0, + gradient_vector); + } + + void + MSE::analyze_error_gradient(std::vector<double>& params, + std::vector<double>& grad, + double alpha, + size_t batch) { + + size_t dim_out = this->ds->get_output_dim(); + size_t n_elements = this->ds->get_n_elements(); + std::vector<std::pair<std::vector<double>, std::vector<double>>>* data = this->ds->get_data(); + + if (batch > 0) { + *data = this->ds->get_random_data_batch(batch); + n_elements = data->size(); + } + std::vector<double> error_derivative(dim_out); + + std::vector<double> grad_sum(grad.size()); + std::fill(grad_sum.begin(), + grad_sum.end(), + 0.0); + this->nets[0]->write_weights(); + this->nets[0]->write_biases(); + for (auto el: *data) { // Iterate through every element in the test set + + this->nets[0]->eval_single_debug(el.first, + error_derivative, + ¶ms); // Compute the net output and store it into 'output' variable + std::cout << "Input["; + for (auto v: el.first) { + std::cout << v << ", "; + } + std::cout << "]"; + + std::cout << " Desired Output["; + for (auto v: el.second) { + std::cout << v << ", "; + } + std::cout << "]"; + + std::cout << " Real Output["; + for (auto v: error_derivative) { + std::cout << v << ", "; + } + std::cout << "]"; + + for (size_t j = 0; j < dim_out; ++j) { + error_derivative.at(j) = 2.0 * (error_derivative.at(j) - el.second.at(j)); //real - expected result + } + std::cout << " Error derivative["; + for (auto v: error_derivative) { + std::cout << v << ", "; + } + std::cout << "]"; + + std::fill(grad.begin(), + grad.end(), + 0.0); + this->nets[0]->add_to_gradient_single_debug(el.first, + error_derivative, + 1.0, + grad); + for (size_t i = 0; i < grad.size(); ++i) { + grad_sum.at(i) += grad.at(i); + } + + std::cout << " Gradient["; + for (auto v: grad) { + std::cout << v << ", "; + } + std::cout << "]"; -void ErrorSum::add_error_function( ErrorFunction *F, double alpha ) { - if(!this->summand){ - this->summand = new std::vector<ErrorFunction*>(0); + std::cout << std::endl; + } + std::cout << " Total gradient["; + for (auto v: grad_sum) { + std::cout << v << ", "; + } + std::cout << "]" << std::endl << std::endl; } - this->summand->push_back( F ); - if(!this->summand_coefficient){ - this->summand_coefficient = new std::vector<double>(0); + double MSE::eval_single_item_by_idx(size_t i, + std::vector<double>* parameter_vector, + std::vector<double>& error_vector) { + double output = 0, val; + + this->nets[0]->eval_single(this->ds->get_data()->at(i).first, + error_vector, + parameter_vector); + + for (size_t j = 0; j < error_vector.size(); ++j) { // Compute difference for every element of the output vector + val = error_vector.at(j) - this->ds->get_data()->at(i).second.at(j); + output += val * val; + } + + for (size_t j = 0; j < error_vector.size(); ++j) { + error_vector.at(j) = + 2.0 * (error_vector.at(j) - this->ds->get_data()->at(i).second.at(j)); //real - expected result + } + + return sqrt(output); } - this->summand_coefficient->push_back( alpha ); - if(F){ - if(F->get_dimension() > this->dimension){ - this->dimension = F->get_dimension(); + + std::vector<double> MSE::get_parameters() { + std::vector<double> output(this->get_dimension()); + for (size_t i = 0; i < this->nets[0]->get_n_weights(); ++i) { + output[i] = this->nets[0]->get_parameter_ptr_weights()->at(i); } + for (size_t i = 0; i < this->nets[0]->get_n_biases(); ++i) { + output[i + this->nets[0]->get_n_weights()] = this->nets[0]->get_parameter_ptr_biases()->at(i); + } + return output; } -} -size_t ErrorSum::get_dimension() { -// if(!this->dimension) { -// size_t max = 0; -// for(auto e : *this->summand) { -// if(e->get_dimension() > max) { -// max = e->get_dimension(); -// } -// }; -// -// this->dimension = max; -// } - return this->dimension; -} \ No newline at end of file + void MSE::set_parameters(std::vector<double>& params) { + this->nets[0]->copy_parameter_space(¶ms); + } + + size_t MSE::get_n_data_set() { + return this->ds->get_n_elements(); + } + + size_t MSE::get_n_test_data_set() { + return this->ds_test->get_n_elements(); + } + + size_t MSE::get_n_outputs() { + return this->nets[0]->get_n_outputs(); + } + + void MSE::randomize_parameters(double scaling) { + this->nets[0]->randomize_parameters(); + this->nets[0]->scale_parameters(scaling); + } + + ErrorSum::ErrorSum() { + this->summand = nullptr; + this->dimension = 0; + } + + ErrorSum::~ErrorSum() { + if (this->summand) { + + for (auto el: *this->summand) { + if (el) { + delete el; + } + } + + delete this->summand; + } + } + + double ErrorSum::eval_on_test_data(std::vector<double>* weights, + bool verbose) { + //TODO take care of the case, when there are no test data + + double output = 0.0; + ErrorFunction* ef = nullptr; + + for (unsigned int i = 0; i < this->summand->size(); ++i) { + ef = this->summand->at(i); + + if (ef) { + output += ef->eval_on_test_data(weights) * this->summand_coefficient.at(i); + } + } + + return output; + } + + double ErrorSum::eval_on_test_data(std::string results_file_path, + std::vector<double>* weights, + bool verbose) { + THROW_NOT_IMPLEMENTED_ERROR(); + + return -1; + } + + double ErrorSum::eval_on_test_data(std::ofstream* results_file_path, + std::vector<double>* weights, + bool verbose) { + THROW_NOT_IMPLEMENTED_ERROR(); + return -1; + } + + double ErrorSum::eval_on_data_set(lib4neuro::DataSet* data_set, + std::vector<double>* weights, + bool verbose) { + THROW_NOT_IMPLEMENTED_ERROR(); + + return -1; + } + + double ErrorSum::eval_on_data_set(lib4neuro::DataSet* data_set, + std::string results_file_path, + std::vector<double>* weights, + bool verbose) { + THROW_NOT_IMPLEMENTED_ERROR(); + + return -1; + } + + double ErrorSum::eval_on_data_set(lib4neuro::DataSet* data_set, + std::ofstream* results_file_path, + std::vector<double>* weights, + bool verbose) { + THROW_NOT_IMPLEMENTED_ERROR(); + return -1; + } + + double ErrorSum::eval(std::vector<double>* weights, + bool denormalize_data, + bool verbose) { + double output = 0.0; + ErrorFunction* ef = nullptr; + + for (unsigned int i = 0; i < this->summand->size(); ++i) { + ef = this->summand->at(i); + + if (ef) { + output += ef->eval(weights) * this->summand_coefficient.at(i); + } + } + + return output; + } + + double ErrorSum::eval_single_item_by_idx(size_t i, + std::vector<double>* parameter_vector, + std::vector<double>& error_vector) { + double output = 0.0; + ErrorFunction* ef = nullptr; + std::fill(error_vector.begin(), + error_vector.end(), + 0); + + std::vector<double> error_vector_mem(error_vector.size()); + for (size_t j = 0; j < this->summand->size(); ++j) { + ef = this->summand->at(i); + + if (ef) { + output += ef->eval_single_item_by_idx(i, + parameter_vector, + error_vector_mem) * this->summand_coefficient.at(j); + + for (size_t k = 0; k < error_vector_mem.size(); ++k) { + error_vector[k] += error_vector_mem[k] * this->summand_coefficient.at(j); + } + } + } + + return output; + } + + void ErrorSum::calculate_error_gradient(std::vector<double>& params, + std::vector<double>& grad, + double alpha, + size_t batch) { + + ErrorFunction* ef = nullptr; + for (size_t i = 0; i < this->summand->size(); ++i) { + ef = this->summand->at(i); + + if (ef) { + ef->calculate_error_gradient(params, + grad, + this->summand_coefficient.at(i) * alpha, + batch); + } + } + } + + void ErrorSum::calculate_error_gradient_single(std::vector<double>& error_vector, + std::vector<double>& gradient_vector) { + COUT_INFO("ErrorSum::calculate_error_gradient_single NOT YET IMPLEMENTED!!!"); + } + + void ErrorSum::analyze_error_gradient(std::vector<double>& params, + std::vector<double>& grad, + double alpha, + size_t batch) { + + ErrorFunction* ef = nullptr; + for (size_t i = 0; i < this->summand->size(); ++i) { + ef = this->summand->at(i); + + if (ef) { + ef->calculate_error_gradient(params, + grad, + this->summand_coefficient.at(i) * alpha, + batch); + } + } + } + + void ErrorSum::add_error_function(ErrorFunction* F, + double alpha) { + if (!this->summand) { + this->summand = new std::vector<ErrorFunction*>(0); + } + this->summand->push_back(F); + + this->summand_coefficient.push_back(alpha); + + if (F) { + if (F->get_dimension() > this->dimension) { + this->dimension = F->get_dimension(); + } + } + } + + size_t ErrorSum::get_dimension() { + return this->dimension; + } + + std::vector<double> ErrorSum::get_parameters() { + return this->summand->at(0)->get_parameters(); + } + + void ErrorSum::set_parameters(std::vector<double>& params) { + //TODO may cause problems for general error sum... + for (auto n: *this->summand) { + n->set_parameters(params); + } + } + + + void ErrorSum::calculate_residual_gradient(std::vector<double>* input, + std::vector<double>* output, + std::vector<double>* gradient, + double h) { + THROW_NOT_IMPLEMENTED_ERROR(); + } + + double ErrorSum::calculate_single_residual(std::vector<double>* input, + std::vector<double>* output, + std::vector<double>* parameters) { + THROW_NOT_IMPLEMENTED_ERROR(); + + return 0; + } + + double ErrorSum::eval_on_single_input(std::vector<double>* input, + std::vector<double>* output, + std::vector<double>* weights) { + double o = 0.0; + + for (size_t i = 0; i < this->summand->size(); ++i) { + o += this->summand->at(i)->eval_on_single_input(input, + output, + weights) * this->summand_coefficient.at(i); + } + + return o; + } + + size_t ErrorSum::get_n_data_set() { + size_t o = 0; + + for (size_t i = 0; i < this->summand->size(); ++i) { + o += this->summand->at(i)->get_n_data_set(); + } + + return o; + } + + size_t ErrorSum::get_n_test_data_set() { + size_t o = 0; + + for (size_t i = 0; i < this->summand->size(); ++i) { + o += this->summand->at(i)->get_n_test_data_set(); + } + + return o; + } + + size_t ErrorSum::get_n_outputs() { + size_t o = 0; + + for (size_t i = 0; i < this->summand->size(); ++i) { + o += this->summand->at(i)->get_n_outputs(); + } + + return o; + } + + void ErrorSum::divide_data_train_test(double percent) { + for (auto n: *this->summand) { + n->divide_data_train_test(percent); + } + } + + void ErrorSum::return_full_data_set_for_training() { + for (auto n: *this->summand) { + n->return_full_data_set_for_training(); + } + } + + void ErrorSum::get_jacobian_and_rhs(std::vector<std::vector<double>>& jacobian, + std::vector<double>& rhs) { + for (auto n: *this->summand) { + std::vector<double> rhs_loc; + n->get_jacobian_and_rhs(jacobian, + rhs_loc); + + size_t curr_size = rhs.size(); + rhs.resize(curr_size + rhs_loc.size()); + for (size_t i = 0; i < rhs_loc.size(); ++i) { + rhs.at(i + curr_size) = rhs_loc.at(i); + } + } + } + + void ErrorSum::randomize_parameters(double scaling) { + for (auto n: *this->summand) { + n->randomize_parameters(scaling); + } + } + +} diff --git a/src/ErrorFunction/ErrorFunctions.h b/src/ErrorFunction/ErrorFunctions.h index 25719e6f03e72866da91376932fd0138f2b3df10..f15e10d2cfbfbfe0b282526a7ee5854b10a271bd 100644 --- a/src/ErrorFunction/ErrorFunctions.h +++ b/src/ErrorFunction/ErrorFunctions.h @@ -1,6 +1,3 @@ -// -// Created by martin on 7/15/18. -// #ifndef INC_4NEURO_ERRORFUNCTION_H #define INC_4NEURO_ERRORFUNCTION_H @@ -9,96 +6,695 @@ #include "../Network/NeuralNetwork.h" #include "../DataSet/DataSet.h" -#include "exprtk.hpp" - -enum ErrorFunctionType{ - ErrorFuncMSE -}; - - - -class ErrorFunction { -public: - - /** - * - * @param weights - * @return - */ - virtual double eval(std::vector<double>* weights = nullptr) = 0; - - /** - * - * @return - */ - LIB4NEURO_API virtual size_t get_dimension(); - -protected: - - /** - * - */ - size_t dimension = 0; -}; - -class MSE : public ErrorFunction { - -public: - /** - * Constructor for single neural network - * @param net - * @param ds - */ - LIB4NEURO_API MSE(NeuralNetwork* net, DataSet* ds); - - /** - * - * @param weights - * @return - */ - LIB4NEURO_API virtual double eval(std::vector<double>* weights = nullptr); - -private: - - NeuralNetwork* net; - DataSet* ds; -}; - -class ErrorSum : public ErrorFunction{ -public: - /** - * - */ - LIB4NEURO_API ErrorSum(); - - /** - * - */ - LIB4NEURO_API ~ErrorSum(); - - /** - * - * @param weights - * @return - */ - LIB4NEURO_API virtual double eval(std::vector<double>* weights = nullptr); - - /** - * - * @param F - */ - LIB4NEURO_API void add_error_function( ErrorFunction *F, double alpha = 1.0 ); - - /** - * - * @return - */ - LIB4NEURO_API size_t get_dimension() override; - -private: - std::vector<ErrorFunction*>* summand; - std::vector<double> *summand_coefficient; -}; +//TODO HEAVY refactoring needed! + +namespace lib4neuro { + + //TODO write smarter using ErrorFunction abstract class? + enum ErrorFunctionType { + ErrorFuncMSE + }; + + class ErrorFunction { + public: + + /** + * + * @param weights + * @return + */ + virtual double eval(std::vector<double>* weights = nullptr, + bool denormalize_data = false, + bool verbose = false) = 0; + + /** + * + * @param input + * @param output + * @param weights + * @return + */ + virtual double eval_on_single_input(std::vector<double>* input, + std::vector<double>* output, + std::vector<double>* weights = nullptr) = 0; + + /** + * + * @return + */ + LIB4NEURO_API virtual size_t get_dimension(); + + /** + * + * @param params + * @param grad + * @param alpha + * @param batch + */ + virtual void + calculate_error_gradient(std::vector<double>& params, + std::vector<double>& grad, + double alpha = 1.0, + size_t batch = 0) = 0; + + /** + * + * @param params + * @param grad + * @param alpha + * @param batch + */ + virtual void + analyze_error_gradient(std::vector<double>& params, + std::vector<double>& grad, + double alpha = 1.0, + size_t batch = 0) = 0; + + /** + * + * @return + */ + virtual std::vector<double> get_parameters() = 0; + + /** + * + * @return + */ + virtual size_t get_n_data_set() = 0; + + /** + * + * @return + */ + virtual size_t get_n_test_data_set() = 0; + + /** + * + * @return + */ + virtual size_t get_n_outputs() = 0; + + /** + * + * @param params + */ + virtual void set_parameters(std::vector<double>& params) = 0; + + /** + * + * @param percent_train + * @return + */ + virtual void divide_data_train_test(double percent_test) = 0; + + /** + * + */ + virtual void return_full_data_set_for_training() = 0; + + /** + * + * @param jacobian + * @param rhs + */ + virtual void get_jacobian_and_rhs(std::vector<std::vector<double>>& jacobian, + std::vector<double>& rhs) = 0; + + /** + * + */ + virtual double eval_on_test_data(std::vector<double>* weights = nullptr, + bool verbose = false) = 0; + + /** + * + * @param results_file_path + * @param weights + * @return + */ + virtual double eval_on_test_data(std::string results_file_path, + std::vector<double>* weights = nullptr, + bool verbose = false) = 0; + + /** + * + * @param results_file_path + * @param weights + * @return + */ + virtual double eval_on_test_data(std::ofstream* results_file_path, + std::vector<double>* weights = nullptr, + bool verbose = false) = 0; + + /** + * + * @param data_set + * @param weights + * @return + */ + virtual double eval_on_data_set(DataSet* data_set, + std::vector<double>* weights = nullptr, + bool verbose = false) = 0; + + /** + * + * @param data_set + * @param weights + * @param results_file_path + * @return + */ + virtual double + eval_on_data_set(DataSet* data_set, + std::string results_file_path, + std::vector<double>* weights = nullptr, + bool verbose = false) = 0; + + /** + * + * @param data_set + * @param results_file_path + * @param weights + * @return + */ + virtual double eval_on_data_set(DataSet* data_set, + std::ofstream* results_file_path = nullptr, + std::vector<double>* weights = nullptr, + bool verbose = false) = 0; + + /** + * + * @param i + * @param parameter_vector + * @param error_vector + * @return + */ + virtual double eval_single_item_by_idx(size_t i, + std::vector<double>* parameter_vector, + std::vector<double>& error_vector) = 0; + + /** + * + * @param error_vector + * @param gradient_vector + */ + virtual void calculate_error_gradient_single(std::vector<double>& error_vector, + std::vector<double>& gradient_vector) = 0; + + /** + * + * @param input + * @param output + * @param gradient + * @param h + */ + virtual void + calculate_residual_gradient(std::vector<double>* input, + std::vector<double>* output, + std::vector<double>* gradient, + double h = 1e-3) = 0; + + /** + * + * @param input + * @param output + * @param parameters + * @return + */ + virtual double + calculate_single_residual(std::vector<double>* input, + std::vector<double>* output, + std::vector<double>* parameters = nullptr) = 0; + + /** + * + * @param scaling + */ + virtual void randomize_parameters(double scaling) = 0; + + protected: + + /** + * + */ + size_t dimension = 0; + + /** + * + */ + std::vector<NeuralNetwork*> nets; + + /** + * + */ + DataSet* ds = nullptr; + + /** + * + */ + DataSet* ds_full = nullptr; + + /** + * + */ + DataSet* ds_test = nullptr; + }; + + + class MSE : public ErrorFunction { + + public: + /** + * Constructor for single neural network + * @param net + * @param ds + */ + LIB4NEURO_API MSE(NeuralNetwork* net, + DataSet* ds); + + /** + * + * @param percent_train + * @return + */ + LIB4NEURO_API virtual void divide_data_train_test(double percent_test) override; + + /** + * + */ + LIB4NEURO_API virtual void return_full_data_set_for_training() override; + + /** + * + * @param jacobian + * @param rhs + */ + LIB4NEURO_API virtual void get_jacobian_and_rhs(std::vector<std::vector<double>>& jacobian, + std::vector<double>& rhs) override; + /** + * + * @param weights + * @return + */ + LIB4NEURO_API double eval(std::vector<double>* weights = nullptr, + bool denormalize_data = false, + bool verbose = false) override; + + /** + * + * @param params + * @param grad + * @param alpha + * @param batch + */ + LIB4NEURO_API void + calculate_error_gradient(std::vector<double>& params, + std::vector<double>& grad, + double alpha = 1.0, + size_t batch = 0) override; + + /** + * + * @param params + * @param grad + * @param alpha + * @param batch + */ + LIB4NEURO_API void + analyze_error_gradient(std::vector<double>& params, + std::vector<double>& grad, + double alpha = 1.0, + size_t batch = 0) override; + + /** + * Evaluates the function f(x) = 0 - MSE(x) for a + * specified input x + * + * @param input + * @return + */ + LIB4NEURO_API + double calculate_single_residual(std::vector<double>* input, + std::vector<double>* output, + std::vector<double>* parameters) override; + + /** + * Compute gradient of the residual function f(x) = 0 - MSE(x) for a specific input x. + * The method uses the central difference method. + * + * @param[in] input Vector being a single input + * @param[out] gradient Resulting gradient + * @param[in] h Step used in the central difference + */ + LIB4NEURO_API void + calculate_residual_gradient(std::vector<double>* input, + std::vector<double>* output, + std::vector<double>* gradient, + double h = 1e-3) override; + + /** + * + * @param input + * @return + */ + LIB4NEURO_API double eval_on_single_input(std::vector<double>* input, + std::vector<double>* output, + std::vector<double>* weights = nullptr) override; + + /** + * + * @param weights + * @return + */ + LIB4NEURO_API double eval_on_test_data(std::vector<double>* weights = nullptr, + bool verbose = false) override; + + /** + * + * @param results_file_path + * @param weights + * @return + */ + LIB4NEURO_API double eval_on_test_data(std::string results_file_path = nullptr, + std::vector<double>* weights = nullptr, + bool verbose = false) override; + + /** + * + * @param results_file_path + * @param weights + * @return + */ + LIB4NEURO_API double eval_on_test_data(std::ofstream* results_file_path, + std::vector<double>* weights = nullptr, + bool verbose = false) override; + + /** + * + * @param data_set + * @param results_file_path + * @param weights + * @return + */ + LIB4NEURO_API double eval_on_data_set(DataSet* data_set, + std::ofstream* results_file_path, + std::vector<double>* weights = nullptr, + bool verbose = false) override; + + /** + * + * @param data_set + * @param weights + * @return + */ + LIB4NEURO_API double eval_on_data_set(DataSet* data_set, + std::vector<double>* weights = nullptr, + bool verbose = false) override; + + /** + * + * @param data_set + * @param results_file_path + * @param weights + * @return + */ + LIB4NEURO_API double eval_on_data_set(DataSet* data_set, + std::string results_file_path, + std::vector<double>* weights = nullptr, + bool verbose = false) override; + + /** + * + * @param i + * @param parameter_vector + * @param error_vector + * @return + */ + LIB4NEURO_API double eval_single_item_by_idx(size_t i, + std::vector<double>* parameter_vector, + std::vector<double>& error_vector) override; + + /** + * + * @param error_vector + * @param gradient_vector + */ + LIB4NEURO_API void calculate_error_gradient_single(std::vector<double>& error_vector, + std::vector<double>& gradient_vector) override; + + /** + * + * @return + */ + LIB4NEURO_API virtual std::vector<double> get_parameters() override; + + /** + * + * @param params + */ + LIB4NEURO_API virtual void set_parameters(std::vector<double>& params) override; + + /** + * + * @return + */ + LIB4NEURO_API virtual size_t get_n_data_set() override; + + /** + * + * @return + */ + LIB4NEURO_API virtual size_t get_n_test_data_set() override; + + /** + * + * @return + */ + LIB4NEURO_API virtual size_t get_n_outputs() override; + + /** + * + * @param scaling + */ + LIB4NEURO_API virtual void randomize_parameters(double scaling) override; + }; + + class ErrorSum : public ErrorFunction { + public: + /** + * + */ + LIB4NEURO_API ErrorSum(); + + /** + * + */ + LIB4NEURO_API ~ErrorSum(); + + /** + * + * @param weights + * @return + */ + LIB4NEURO_API double eval(std::vector<double>* weights = nullptr, + bool denormalize_data = false, + bool verbose = false); + + LIB4NEURO_API double eval_on_single_input(std::vector<double>* input, + std::vector<double>* output, + std::vector<double>* weights = nullptr) override; + + /** + * + * @param weights + * @return + */ + LIB4NEURO_API double eval_on_test_data(std::vector<double>* weights = nullptr, + bool verbose = false) override; + + /** + * + * @param results_file_path + * @param weights + * @return + */ + LIB4NEURO_API double eval_on_test_data(std::string results_file_path, + std::vector<double>* weights = nullptr, + bool verbose = false) override; + + /** + * + * @param results_file_path + * @param weights + * @return + */ + LIB4NEURO_API double eval_on_test_data(std::ofstream* results_file_path, + std::vector<double>* weights = nullptr, + bool verbose = false) override; + + /** + * + * @param data_set + * @param weights + * @return + */ + LIB4NEURO_API double eval_on_data_set(DataSet* data_set, + std::vector<double>* weights = nullptr, + bool verbose = false) override; + + /** + * + * @param data_set + * @param results_file_path + * @param weights + * @return + */ + LIB4NEURO_API double eval_on_data_set(DataSet* data_set, + std::string results_file_path, + std::vector<double>* weights = nullptr, + bool verbose = false) override; + + /** + * + * @param data_set + * @param results_file_path + * @param weights + * @return + */ + LIB4NEURO_API double eval_on_data_set(DataSet* data_set, + std::ofstream* results_file_path, + std::vector<double>* weights = nullptr, + bool verbose = false) override; + + /** + * + * @param i + * @param parameter_vector + * @param error_vector + * @return + */ + LIB4NEURO_API virtual double eval_single_item_by_idx(size_t i, + std::vector<double>* parameter_vector, + std::vector<double>& error_vector) override; + + /** + * + * @param error_vector + * @param gradient_vector + */ + LIB4NEURO_API virtual void calculate_error_gradient_single(std::vector<double>& error_vector, + std::vector<double>& gradient_vector) override; + + /** + * + * @param F + */ + LIB4NEURO_API void add_error_function(ErrorFunction* F, + double alpha = 1.0); + + /** + * + * @return + */ + LIB4NEURO_API size_t get_dimension() override; + + /** + * + * @param params + * @param grad + * @param alpha + * @param batch + */ + LIB4NEURO_API void + calculate_error_gradient(std::vector<double>& params, + std::vector<double>& grad, + double alpha = 1.0, + size_t batch = 0) override; + /** + * + * @param params + * @param grad + * @param alpha + * @param batch + */ + LIB4NEURO_API void + analyze_error_gradient(std::vector<double>& params, + std::vector<double>& grad, + double alpha = 1.0, + size_t batch = 0) override; + + LIB4NEURO_API void + calculate_residual_gradient(std::vector<double>* input, + std::vector<double>* output, + std::vector<double>* gradient, + double h = 1e-3) override; + + LIB4NEURO_API double + calculate_single_residual(std::vector<double>* input, + std::vector<double>* output, + std::vector<double>* parameters = nullptr) override; + + + /** + * + * @return + */ + LIB4NEURO_API virtual std::vector<double> get_parameters() override; + + /** + * + * @param params + */ + LIB4NEURO_API virtual void set_parameters(std::vector<double>& params) override; + + /** + * + * @return + */ + LIB4NEURO_API virtual size_t get_n_data_set() override; + + /** + * + * @return + */ + LIB4NEURO_API virtual size_t get_n_test_data_set() override; + + /** + * + * @return + */ + LIB4NEURO_API virtual size_t get_n_outputs() override; + + /** + * + * @param percent + */ + LIB4NEURO_API virtual void divide_data_train_test(double percent) override; + + /** + * + */ + LIB4NEURO_API virtual void return_full_data_set_for_training() override; + + /** + * + * @param jacobian + * @param rhs + */ + LIB4NEURO_API virtual void get_jacobian_and_rhs( + std::vector<std::vector<double>>& jacobian, + std::vector<double>& rhs) override; + + /** + * + * @param scaling + */ + LIB4NEURO_API virtual void randomize_parameters(double scaling) override; + + protected: + std::vector<ErrorFunction*>* summand; + std::vector<double> summand_coefficient; + }; +} #endif //INC_4NEURO_ERRORFUNCTION_H diff --git a/src/ErrorFunction/ErrorFunctionsMock.h b/src/ErrorFunction/ErrorFunctionsMock.h new file mode 100644 index 0000000000000000000000000000000000000000..b1735436e030fe7e85c2ba702f3d720f16d08dcd --- /dev/null +++ b/src/ErrorFunction/ErrorFunctionsMock.h @@ -0,0 +1,108 @@ + +#ifndef LIB4NEURO_ERRORFUNCTIONSMOCK_H +#define LIB4NEURO_ERRORFUNCTIONSMOCK_H + +#include "../ErrorFunction/ErrorFunctions.h" +#include "../DataSet/DataSet.h" + +#include <turtle/mock.hpp> + +using namespace lib4neuro; + + +MOCK_BASE_CLASS(mock_ErrorFunction, + lib4neuro::ErrorFunction +) { + MOCK_METHOD(eval, + 3) + + MOCK_METHOD(eval_on_single_input, + 3) + + MOCK_METHOD(get_dimension, + 0) + + MOCK_METHOD(calculate_error_gradient, + 4) + + MOCK_METHOD(analyze_error_gradient, + 4) + + MOCK_METHOD(get_parameters, + 0) + + MOCK_METHOD(get_n_data_set, + 0) + + MOCK_METHOD(get_n_test_data_set, + 0) + + MOCK_METHOD(get_n_outputs, + 0) + + MOCK_METHOD(set_parameters, + 1) + + MOCK_METHOD(divide_data_train_test, + 1) + + MOCK_METHOD(return_full_data_set_for_training, + 0) + + MOCK_METHOD(get_jacobian_and_rhs, + 2) + + MOCK_METHOD(eval_on_test_data, + 2, + double(std::vector<double> + *, bool), + id1) + + MOCK_METHOD(eval_on_test_data, + 3, + double(std::string, std::vector<double> + *, bool), + id2) + + MOCK_METHOD(eval_on_test_data, + 3, + double(std::ofstream + *, std::vector<double> *, bool), + id3) + + MOCK_METHOD(eval_on_data_set, + 3, + double(DataSet + *, std::vector<double> *, bool), + id4) + + MOCK_METHOD(eval_on_data_set, + 4, + double(DataSet + *, std::string, std::vector<double> *, bool), + id5) + + MOCK_METHOD(eval_on_data_set, + 4, + double(DataSet + *, std::ofstream *, std::vector<double> *, bool), + id6) + + + MOCK_METHOD(eval_single_item_by_idx, + 3) + + MOCK_METHOD(calculate_error_gradient_single, + 2) + + MOCK_METHOD(calculate_residual_gradient, + 4) + + MOCK_METHOD(calculate_single_residual, + 3) + + MOCK_METHOD(randomize_parameters, + 1) +}; + +#endif //LIB4NEURO_ERRORFUNCTIONSMOCK_H diff --git a/src/General/ExprtkWrapper.cpp b/src/General/ExprtkWrapper.cpp index 03738f6474be96957e52c0296d1631daa8198d3b..cf6144f6fd9a44f2a2dc8339d2c07b670bd235c8 100644 --- a/src/General/ExprtkWrapper.cpp +++ b/src/General/ExprtkWrapper.cpp @@ -5,75 +5,101 @@ * @date 4.9.18 - */ +#include <boost/serialization/export.hpp> + +#include "exprtk.hpp" #include "ExprtkWrapper.h" +#include "ExprtkWrapperSerialization.h" +#include "exceptions.h" +BOOST_CLASS_EXPORT_IMPLEMENT(ExprtkWrapper); -ExprtkWrapper::ExprtkWrapper( std::string expression_string ) { +ExprtkWrapper::ExprtkWrapper() { + // Because of serialization + // TODO implement? + THROW_NOT_IMPLEMENTED_ERROR("This constructors is being used only for serialization purposes."); +} - this->expression_str = expression_string; +ExprtkWrapper::ExprtkWrapper(std::string expression_string) { - this->symbol_table = new symbol_table_t( ); + this->p_impl = new ExprtkWrapperImpl(); - this->symbol_table->add_variable("x", this->x); - this->symbol_table->add_variable("y", this->y); - this->symbol_table->add_variable("z", this->z); - this->symbol_table->add_variable("t", this->t); - this->symbol_table->add_variable("f", this->z); + this->p_impl->expression_str = expression_string; - this->expression = new expression_t( ); - this->expression->register_symbol_table( *this->symbol_table ); + this->p_impl->symbol_table = new symbol_table_t(); - this->parser = new parser_t( ); - parser->compile(this->expression_str, *this->expression ); -} + this->p_impl->symbol_table->add_variable("x", + this->p_impl->x); + this->p_impl->symbol_table->add_variable("y", + this->p_impl->y); + this->p_impl->symbol_table->add_variable("z", + this->p_impl->z); + this->p_impl->symbol_table->add_variable("t", + this->p_impl->t); + this->p_impl->symbol_table->add_variable("f", + this->p_impl->z); + this->p_impl->expression = new expression_t(); + this->p_impl->expression->register_symbol_table(*this->p_impl->symbol_table); + + this->p_impl->parser = new parser_t(); + this->p_impl->parser->compile(this->p_impl->expression_str, + *this->p_impl->expression); +} ExprtkWrapper::~ExprtkWrapper() { - if( this->expression ){ - delete this->expression; - this->expression = nullptr; + if (this->p_impl->expression) { + delete this->p_impl->expression; + this->p_impl->expression = nullptr; } - if( this->symbol_table ){ - delete this->symbol_table; - this->symbol_table = nullptr; + if (this->p_impl->symbol_table) { + delete this->p_impl->symbol_table; + this->p_impl->symbol_table = nullptr; } - if( this->parser ){ - delete this->parser; - this->parser = nullptr; + if (this->p_impl->parser) { + delete this->p_impl->parser; + this->p_impl->parser = nullptr; } + delete this->p_impl; + this->p_impl = nullptr; + } -double ExprtkWrapper::eval(double x1, double x2, double x3, double x4) { +double ExprtkWrapper::eval(double x1, + double x2, + double x3, + double x4) { + + this->p_impl->x = x1; + this->p_impl->y = x2; + this->p_impl->z = x3; + this->p_impl->t = x4; - this->x = x1; - this->y = x2; - this->z = x3; - this->t = x4; + return this->p_impl->expression->value(); - return this->expression->value(); } -double ExprtkWrapper::eval(std::vector<double> &p) { +double ExprtkWrapper::eval(std::vector<double>& p) { - if(p.size() > 0){ - this->x = p[0]; + if (p.size() > 0) { + this->p_impl->x = p[0]; } - if(p.size() > 1){ - this->y = p[1]; + if (p.size() > 1) { + this->p_impl->y = p[1]; } - if(p.size() > 2){ - this->z = p[2]; + if (p.size() > 2) { + this->p_impl->z = p[2]; } - if(p.size() > 3){ - this->t = p[3]; + if (p.size() > 3) { + this->p_impl->t = p[3]; } - double result = this->expression->value(); + double result = this->p_impl->expression->value(); return result; } \ No newline at end of file diff --git a/src/General/ExprtkWrapper.h b/src/General/ExprtkWrapper.h index df58142e2386bb566cf55061b653b6408ba5198c..f77bf824ca8169efa7a82313f1e6f7fb754b8d78 100644 --- a/src/General/ExprtkWrapper.h +++ b/src/General/ExprtkWrapper.h @@ -8,29 +8,32 @@ #ifndef LIB4NEURO_EXPRTKWRAPPER_H #define LIB4NEURO_EXPRTKWRAPPER_H -#include "../settings.h" +#include <memory> +#include <vector> -#include "exprtk.hpp" +#include "../settings.h" class ExprtkWrapper { - typedef exprtk::symbol_table<double> symbol_table_t; - typedef exprtk::expression<double> expression_t; - typedef exprtk::parser<double> parser_t; - public: + /** + * Struct used to access private properties from + * the serialization function + */ + struct access; + /** * * @param expression_string * @param var_dim */ - LIB4NEURO_API ExprtkWrapper( std::string expression_string ); + LIB4NEURO_API ExprtkWrapper(std::string expression_string); /** * */ - LIB4NEURO_API ExprtkWrapper( ); + LIB4NEURO_API ExprtkWrapper(); /** * @@ -45,42 +48,30 @@ public: * @param x4 * @return */ - LIB4NEURO_API double eval(double x1 = 0.0, double x2 = 0.0, double x3 = 0.0, double x4 = 0.0); + LIB4NEURO_API double eval(double x1 = 0.0, + double x2 = 0.0, + double x3 = 0.0, + double x4 = 0.0); /** * * @param p * @return */ - LIB4NEURO_API double eval(std::vector<double> &p); + LIB4NEURO_API double eval(std::vector<double>& p); private: /** + * Private properties * + * They are hidden in .cpp file + * to isolate Exprtk dependency from header */ - expression_t *expression = nullptr; - - /** - * - */ - symbol_table_t *symbol_table = nullptr; + class ExprtkWrapperImpl; - /** - * - */ - parser_t * parser = nullptr; - - /* - * variables - */ - double x, y, z, t, f; - - /** - * referential expression string - */ - - std::string expression_str; + ExprtkWrapperImpl* p_impl; }; + #endif //LIB4NEURO_EXPRTKWRAPPER_H diff --git a/src/General/ExprtkWrapperSerialization.h b/src/General/ExprtkWrapperSerialization.h new file mode 100644 index 0000000000000000000000000000000000000000..153048120e6b33e471c0c46d849cb1e7f3515572 --- /dev/null +++ b/src/General/ExprtkWrapperSerialization.h @@ -0,0 +1,91 @@ + +#ifndef LIB4NEURO_EXPRTKWRAPPERSERIALIZATION_H +#define LIB4NEURO_EXPRTKWRAPPERSERIALIZATION_H + +#include <boost/archive/text_oarchive.hpp> +#include <boost/archive/text_iarchive.hpp> +#include <boost/serialization/export.hpp> + +//TODO check different order of headers - possible bug +#include "ExprtkWrapper.h" +#include "exprtk.hpp" + +BOOST_CLASS_EXPORT_KEY(ExprtkWrapper); + +typedef exprtk::symbol_table<double> symbol_table_t; +typedef exprtk::expression<double> expression_t; +typedef exprtk::parser<double> parser_t; + +/** + * Class implementing the private properties + * of ExprtkWrapper class. + */ +class ExprtkWrapper::ExprtkWrapperImpl { + +public: + + /** + * Struct used to access private properties from + * the serialization function + */ + struct access; + + /** + * + */ + expression_t* expression = nullptr; + + /** + * + */ + symbol_table_t* symbol_table = nullptr; + + /** + * + */ + parser_t* parser = nullptr; + + /** + * variables + */ + double x, y, z, t, f; + + /** + * referential expression string + */ + std::string expression_str; +}; + +struct ExprtkWrapper::access { + template<class Archive> + static void serialize(Archive& ar, + ExprtkWrapper& n, + const unsigned int version) { + ar & n.p_impl->expression_str; + ar & n.p_impl->x & n.p_impl->y & n.p_impl->z & n.p_impl->t & n.p_impl->f; + } +}; + +namespace boost { + namespace serialization { + + /** + * Serialization function + * @tparam Archive Boost library template + * @param ar Boost parameter - filled automatically during serialization! + * @param n ExprtkWrapper instance + * @param version Boost parameter - filled automatically during serialization! + */ + template<class Archive> + void serialize(Archive& ar, + ExprtkWrapper& n, + const unsigned int version) { + ExprtkWrapper::access::serialize(ar, + n, + version); + } + + } // namespace serialization +} // namespace boost + +#endif //LIB4NEURO_EXPRTKWRAPPERSERIALIZATION_H diff --git a/src/LearningMethods/GradientDescent.cpp b/src/LearningMethods/GradientDescent.cpp index ef5c12fc4164437dfdca429faac18fcfe1a13874..d9a2c55b298054dc3aeceba006d87680237053f3 100644 --- a/src/LearningMethods/GradientDescent.cpp +++ b/src/LearningMethods/GradientDescent.cpp @@ -5,4 +5,256 @@ * @date 30.7.18 - */ +#include <random.hpp> #include "GradientDescent.h" +#include "message.h" + +namespace lib4neuro { + GradientDescent::GradientDescent(double epsilon, + size_t n_to_restart, + int max_iters, + size_t batch) { + this->tolerance = epsilon; + this->restart_frequency = n_to_restart; + this->maximum_niters = max_iters; + this->batch = batch; + } + + GradientDescent::~GradientDescent() { + } + + void GradientDescent::eval_step_size_mk(double& gamma, + double beta, + double& c, + double grad_norm_prev, + double grad_norm, + double fi, + double fim) { + + if (fi > fim) { + c /= 1.0000005; + } else if (fi < fim) { + c *= 1.0000005; + } + + gamma *= std::pow(c, + 1.0 - 2.0 * beta) * std::pow(grad_norm_prev / grad_norm, + 1.0 / c); + + } + + bool GradientDescent::perform_feasible_1D_step( + lib4neuro::ErrorFunction& ef, + double error_previous, + double step_coefficient, + std::shared_ptr<std::vector<double>> direction, + std::shared_ptr<std::vector<double>> parameters_before, + std::shared_ptr<std::vector<double>> parameters_after + ) { + + size_t i; + + boost::random::mt19937 gen(std::time(0)); + boost::random::uniform_int_distribution<> dis(0, + direction->size()); + size_t max_dir_idx = dis(gen); + + double error_current = error_previous + 1.0; + while (error_current >= error_previous) { + (*parameters_after)[max_dir_idx] = + (*parameters_before)[max_dir_idx] - step_coefficient * (*direction)[max_dir_idx]; + + error_current = ef.eval(parameters_after.get()); + if (step_coefficient < 1e-32) { + for (i = 0; i < direction->size(); ++i) { + (*parameters_after)[i] = (*parameters_before)[i] - step_coefficient * (*direction)[i]; + } + return false; + } else { + if (error_current >= error_previous) { + step_coefficient *= 0.5; + } else { + } + } + } + return true; + } + + void GradientDescent::optimize(lib4neuro::ErrorFunction& ef, + std::ofstream* ofs) { + + + COUT_INFO("Finding a solution via a Gradient Descent method with adaptive step-length..." << std::endl); + COUT_INFO("Initial error: " << ef.eval() << std::endl); + + if (ofs && ofs->is_open()) { + *ofs << "Finding a solution via a Gradient Descent method with adaptive step-length..." << std::endl; + *ofs << "Initial error: " << ef.eval() << std::endl; + } + + double grad_norm = this->tolerance * 10.0, gamma, sx, beta; + double grad_norm_prev; + size_t i; + long long int iter_idx = this->maximum_niters; + size_t iter_counter = 0; + + gamma = 1.0; + double prev_val, val = 0.0, c = 1.25; + + size_t n_parameters = ef.get_dimension(); + + + std::vector<double>* gradient_current(new std::vector<double>(n_parameters)); + std::vector<double>* gradient_prev(new std::vector<double>(n_parameters)); + std::vector<double>* params_current = new std::vector<double>(ef.get_parameters()); + std::vector<double>* params_prev(new std::vector<double>(n_parameters)); + std::vector<double>* ptr_mem; + + + std::fill(gradient_current->begin(), + gradient_current->end(), + 0.0); + std::fill(gradient_prev->begin(), + gradient_prev->end(), + 0.0); + + val = ef.eval(params_current); + size_t counter_good_guesses = 0, counter_bad_guesses = 0, counter_simplified_direction_good = 0, counter_simplified_direction_bad = 0; + double cooling = 1.0; + while (grad_norm > this->tolerance && (iter_idx != 0)) { + iter_idx--; + iter_counter++; + prev_val = val; + grad_norm_prev = grad_norm; + + /* reset of the current gradient */ + std::fill(gradient_current->begin(), + gradient_current->end(), + 0.0); + ef.calculate_error_gradient(*params_current, + *gradient_current, + 1.0, + this->batch); + + + grad_norm = 0.0; + for (auto v: *gradient_current) { + grad_norm += v * v; + } + grad_norm = std::sqrt(grad_norm); + + /* Update of the parameters */ + /* step length calculation */ + if (iter_counter < 10 || iter_counter % this->restart_frequency == 0) { + /* fixed step length */ + gamma = 0.1 * this->tolerance; + cooling = 1.0; + } else { + /* angle between two consecutive gradients */ + sx = 0.0; + for (i = 0; i < gradient_current->size(); ++i) { + sx += (gradient_current->at(i) * gradient_prev->at(i)); + } + sx /= grad_norm * grad_norm_prev; + if (sx < -1.0 + 5e-12) { + sx = -1 + 5e-12; + } else if (sx > 1.0 - 5e-12) { + sx = 1 - 5e-12; + } + beta = std::sqrt(std::acos(sx) / lib4neuro::PI); + + eval_step_size_mk(gamma, + beta, + c, + grad_norm_prev, + grad_norm, + val, + prev_val); + } + + for (i = 0; i < gradient_current->size(); ++i) { + (*params_prev)[i] = (*params_current)[i] - cooling * gamma * (*gradient_current)[i]; + } + val = ef.eval(params_prev); + + + + + /* switcheroo */ + ptr_mem = gradient_prev; + gradient_prev = gradient_current; + gradient_current = ptr_mem; + + ptr_mem = params_prev; + params_prev = params_current; + params_current = ptr_mem; + + + COUT_DEBUG(std::string("Iteration: ") << (unsigned int) (iter_counter) + << ". Step size: " << gamma * cooling + << ". C: " << c + << ". Gradient norm: " << grad_norm + << ". Total error: " << val + << ".\r"); + + WRITE_TO_OFS_DEBUG(ofs, + "Iteration: " << (unsigned int) (iter_counter) + << ". Step size: " << gamma * cooling + << ". C: " << c + << ". Gradient norm: " << grad_norm + << ". Total error: " << val + << "." << std::endl); + + + cooling *= 0.9999; + + } + COUT_DEBUG(std::string("Iteration: ") << (unsigned int) (iter_counter) + << ". Step size: " << gamma + << ". C: " << c + << ". Gradient norm: " << grad_norm + << ". Total error: " << val + << "." << std::endl); + COUT_DEBUG("Number of total steps: " << counter_bad_guesses + counter_good_guesses << ", good: " + << counter_good_guesses << ", bad: " << counter_bad_guesses + << ", from which " + << counter_simplified_direction_good + counter_simplified_direction_bad + << " were attempted by simplified direction, success: " + << counter_simplified_direction_good << ", fail: " + << counter_simplified_direction_bad << std::endl << std::endl); + + if (iter_idx == 0) { + COUT_INFO(std::endl << "Maximum number of iterations (" << this->maximum_niters + << ") was reached! Final error: " << val << std::endl); + + if (ofs && ofs->is_open()) { + *ofs << "Maximum number of iterations (" << this->maximum_niters << ") was reached! Final error: " + << val << std::endl; + + } + + } else { + COUT_INFO(std::endl << "Gradient Descent method converged after " + << this->maximum_niters - iter_idx + << " iterations. Final error:" << val + << std::endl); +#ifdef L4N_DEBUG + if (ofs && ofs->is_open()) { + *ofs << "Gradient Descent method converged after " + << this->maximum_niters - iter_idx + << " iterations." + << std::endl; + } +#endif + } + + this->optimal_parameters = *params_current; + ef.set_parameters(this->optimal_parameters); + + delete gradient_current; + delete gradient_prev; + delete params_current; + delete params_prev; + + } +} diff --git a/src/LearningMethods/GradientDescent.h b/src/LearningMethods/GradientDescent.h index 91f72c53c242b375b39de4c8f9f038b98cfdb50f..52ff75804b0410d03eca0fe4293cb7dce78f9645 100644 --- a/src/LearningMethods/GradientDescent.h +++ b/src/LearningMethods/GradientDescent.h @@ -8,10 +8,110 @@ #ifndef INC_4NEURO_GRADIENTDESCENT_H #define INC_4NEURO_GRADIENTDESCENT_H +#include "../settings.h" +#include "../constants.h" +#include "LearningMethod.h" +#include "../ErrorFunction/ErrorFunctions.h" -class GradientDescent { +namespace lib4neuro { + /** + * + */ + class GradientDescent : public GradientLearningMethod { -}; + private: + /** + * Threshold for the successful ending of the optimization - deviation from minima + */ + double tolerance; + + /** + * Number of iterations to reset step size to tolerance/10.0 + */ + size_t restart_frequency; + + /** + * + */ + size_t batch; + + /** + * Maximal number of iterations - optimization will stop after that, even if not converged + */ + long long int maximum_niters; + + /** + * Adaptive calculation of the step-size based on several historical characteristics. + * ---------------------------------------------------------------------------------- + * If the current error @fi is larger than the error in the previous step @fim, the rate of step-size change decreases (the algorithm is going in the direction too quickly) + * Otherwise the rate of step-size change increases (the algorithm is on the right path, we can attempts to push through more rapidly) + * ---------------------------------------------------------------------------------- + * The step size is then calculated via: @c^(1-2@beta) * (@grad_norm_prev/@grad_norm)^(1/@c) + * If the previous gradient norm is lower then the current gradient norm, then the step-size decreases (as we probably took a too large of a step) + * Otherwise it increases (as we are likely on the right track, we can try to speed-up the convergence) + * + * @param gamma[in, out] a step size used in the last iteration + * @param beta[in] a number in the interval [0, 1]. it represents a measure of direction change between two last steps, 0: no change, 1:opposite directions + * @param c[in, out] greater than zero. it is a measure of a non-linear step-size change. the higher @c is, the more rapidly the step-size increases/decreases + * @param grad_norm_prev[in] gradient norm of the error in the previous iteration + * @param grad_norm[in] gradient norm of the error in the current iteration + * @param fi[in] value of the error + * @param fim[in] value of the error in the previous iteration + */ + virtual void + eval_step_size_mk(double& gamma, + double beta, + double& c, + double grad_norm_prev, + double grad_norm, + double fi, + double fim); + + /** + * Analyses direction of parameters change and performs the most feasible step in one parameter + * @param ef[in] error function to be optimized + * @param error_previous[in] evaluation of the error function on the @parameters_before state + * @param step_coefficient[in] scalar value denoting the scaling of the step in one direction + * @param direction direction[in] vector to be analyzed + * @param parameters_before[in] state of the parameter space before analysis + * @param parameters_after[out] suggested state of the parameters after the analysis completes + */ + virtual bool perform_feasible_1D_step( + lib4neuro::ErrorFunction& ef, + double error_previous, + double step_coefficient, + std::shared_ptr<std::vector<double>> direction, + std::shared_ptr<std::vector<double>> parameters_before, + std::shared_ptr<std::vector<double>> parameters_after + ); + + public: + + /** + * Creates an instance of Gradient Descent Optimizer (i.e. back-propagation) + * @param epsilon Threshold for the successful ending of the optimization - deviation from minima + * @param n_to_restart Number of iterations to reset step size to tolerance/10.0 + * @param max_iters Maximal number of iterations - optimization will stop after that, even if not converged + */ + LIB4NEURO_API explicit GradientDescent(double epsilon = 1e-3, + size_t n_to_restart = 100, + int max_iters = 1000, + size_t batch = 0); + + /** + * Deallocates the instance + */ + LIB4NEURO_API ~GradientDescent(); + + /** + * + * @param ef + */ + LIB4NEURO_API void optimize(lib4neuro::ErrorFunction& ef, + std::ofstream* ofs = nullptr) override; + + }; +} #endif //INC_4NEURO_GRADIENTDESCENT_H diff --git a/src/LearningMethods/GradientDescentBB.cpp b/src/LearningMethods/GradientDescentBB.cpp new file mode 100644 index 0000000000000000000000000000000000000000..971d15efd24bb78dd2a4f209f12933e793189ce8 --- /dev/null +++ b/src/LearningMethods/GradientDescentBB.cpp @@ -0,0 +1,215 @@ +/** + * DESCRIPTION OF THE FILE + * + * @author Michal KravÄenko + * @date 4.2.19 - + */ + +#include "GradientDescentBB.h" +#include "message.h" + +namespace lib4neuro { + GradientDescentBB::GradientDescentBB(double epsilon, + size_t n_to_restart, + int max_iters, + size_t batch) { + this->tolerance = epsilon; + this->restart_frequency = n_to_restart; + this->maximum_niters = max_iters; + this->batch = batch; + } + + GradientDescentBB::~GradientDescentBB() { + } + + + void GradientDescentBB::optimize(lib4neuro::ErrorFunction& ef, + std::ofstream* ofs) { + + + COUT_INFO("Finding a solution via a Gradient Descent method with adaptive step-length..." << std::endl); + COUT_INFO("Initial error: " << ef.eval() << std::endl); + + if (ofs && ofs->is_open()) { + *ofs << "Finding a solution via a Gradient Descent method with adaptive step-length..." << std::endl; + *ofs << "Initial error: " << ef.eval() << std::endl; + } + + double grad_norm = this->tolerance * 10.0, gamma, sx, beta; + double grad_norm_prev; + size_t i; + long long int iter_idx = this->maximum_niters; + size_t iter_counter = 0; + + gamma = 1.0; + double prev_val, val = 0.0, c = 1.25, val_best; + + size_t n_parameters = ef.get_dimension(); + + + std::vector<double>* gradient_current(new std::vector<double>(n_parameters)); + std::vector<double>* gradient_prev(new std::vector<double>(n_parameters)); + std::vector<double>* params_current = new std::vector<double>(ef.get_parameters()); + std::vector<double>* params_prev(new std::vector<double>(n_parameters)); + std::vector<double>* params_best(new std::vector<double>(*params_current)); + + std::vector<double>* ptr_mem; + + double alpha = -1.0, cc, gg; + std::vector<double> dot__(3); + double d1 = 0.0, d2 = 0.0, d3 = 0.0; + + + std::fill(gradient_current->begin(), + gradient_current->end(), + 0.0); + std::fill(gradient_prev->begin(), + gradient_prev->end(), + 0.0); + val = ef.eval(params_current); + val_best = val; + + double cooling_factor = 1.0; + while (grad_norm > this->tolerance && (iter_idx != 0)) { + iter_idx--; + iter_counter++; + prev_val = val; + grad_norm_prev = grad_norm; + + /* reset of the current gradient */ + std::fill(gradient_current->begin(), + gradient_current->end(), + 0.0); + ef.calculate_error_gradient(*params_current, + *gradient_current, + 1.0, + this->batch); + + + grad_norm = 0.0; + for (auto v: *gradient_current) { + grad_norm += v * v; + //COUT_DEBUG( grad_norm << std::endl ); + } + grad_norm = std::sqrt(grad_norm); + + /* Update of the parameters */ + /* step length calculation */ + if (iter_counter < 10 || iter_counter % this->restart_frequency < 10) { + /* fixed step length */ + gamma = 0.1 * this->tolerance; + cooling_factor = 1.0; + } else { + + std::fill(dot__.begin(), + dot__.end(), + 0.0); + d1 = d2 = d3 = 0.0; + + for (size_t d = 0; d < gradient_current->size(); d++) { + cc = params_current->at(d) - params_prev->at(d); + gg = gradient_current->at(d) - gradient_prev->at(d); + + d1 += cc * cc; + d2 += cc * gg; + d3 += gg * gg; + } + + dot__[0] = d1; + dot__[1] = d2; + dot__[2] = d3; + + gamma = 1; + if (fabs(dot__[1]) > 0.0) { + gamma = 0.25 * (dot__[0] / dot__[1]); + } + } + + for (i = 0; i < gradient_current->size(); ++i) { + (*params_prev)[i] = (*params_current)[i] - cooling_factor * gamma * (*gradient_current)[i]; + } + + + /* switcheroo */ + ptr_mem = gradient_prev; + gradient_prev = gradient_current; + gradient_current = ptr_mem; + + ptr_mem = params_prev; + params_prev = params_current; + params_current = ptr_mem; + + val = ef.eval(params_current); + if (val < val_best) { + val_best = val; + + for (i = 0; i < gradient_current->size(); ++i) { + params_best->at(i) = params_current->at(i); + } + } + + COUT_DEBUG(std::string("Iteration: ") << (unsigned int) (iter_counter) + << ". Step size: " << gamma * cooling_factor + << ". C: " << c + << ". Gradient norm: " << grad_norm + << ". Total error: " << val << ". the lowest error: " << val_best + << ".\r"); + + WRITE_TO_OFS_DEBUG(ofs, + "Iteration: " << (unsigned int) (iter_counter) + << ". Step size: " << gamma * cooling_factor + << ". C: " << c + << ". Gradient norm: " << grad_norm + << ". Total error: " << val << ". the lowest error: " << val_best + << "." << std::endl); + + + cooling_factor *= 0.99999; + + } + COUT_DEBUG(std::string("Iteration: ") << (unsigned int) (iter_counter) + << ". Step size: " << gamma * cooling_factor + << ". C: " << c + << ". Gradient norm: " << grad_norm + << ". Total error: " << val + << "." << std::endl); + + + if (iter_idx == 0) { + COUT_INFO(std::endl << "Maximum number of iterations (" << this->maximum_niters + << ") was reached! Final error: " << val_best << std::endl); + + if (ofs && ofs->is_open()) { + *ofs << "Maximum number of iterations (" << this->maximum_niters << ") was reached! Final error: " + << val_best << std::endl; + + } + + } else { + COUT_INFO(std::endl << "Gradient Descent method converged after " + << this->maximum_niters - iter_idx + << " iterations. Final error:" << val_best + << std::endl); +#ifdef L4N_DEBUG + if (ofs && ofs->is_open()) { + *ofs << "Gradient Descent method converged after " + << this->maximum_niters - iter_idx + << " iterations." + << std::endl; + } +#endif + } + + this->optimal_parameters = *params_best; + + ef.set_parameters(this->optimal_parameters); + + delete gradient_current; + delete gradient_prev; + delete params_current; + delete params_prev; + delete params_best; + + } + +} diff --git a/src/LearningMethods/GradientDescentBB.h b/src/LearningMethods/GradientDescentBB.h new file mode 100644 index 0000000000000000000000000000000000000000..ae65c36f6bbab9404fc0f4b06d036cb3ee265810 --- /dev/null +++ b/src/LearningMethods/GradientDescentBB.h @@ -0,0 +1,85 @@ +/** + * DESCRIPTION OF THE FILE + * + * @author Michal KravÄenko + * @date 4.2.19 - + */ + +#ifndef LIB4NEURO_GRADIENTDESCENTBB_H +#define LIB4NEURO_GRADIENTDESCENTBB_H + + +#include "../settings.h" +#include "../constants.h" +#include "LearningMethod.h" +#include "../ErrorFunction/ErrorFunctions.h" + +namespace lib4neuro { + /** + * + */ + class GradientDescentBB : public GradientLearningMethod { + + private: + + /** + * Threshold for the successful ending of the optimization - deviation from minima + */ + double tolerance; + + /** + * + */ + double max_error; + + /** + * Number of iterations to reset step size to tolerance/10.0 + */ + size_t restart_frequency; + + /** + * + */ + size_t batch; + + /** + * + */ + size_t iter_max; + + /** + * Maximal number of iterations - optimization will stop after that, even if not converged + */ + long long int maximum_niters; + + public: + + /** + * Creates an instance of Gradient Descent Optimizer (i.e. back-propagation) + * @param epsilon Threshold for the successful ending of the optimization - deviation from minima + * @param n_to_restart Number of iterations to reset step size to tolerance/10.0 + * @param max_iters Maximal number of iterations - optimization will stop after that, even if not converged + */ + LIB4NEURO_API explicit GradientDescentBB(double epsilon = 1e-3, + size_t n_to_restart = 100, + int max_iters = 1000, + size_t batch = 0); + + /** + * Deallocates the instance + */ + LIB4NEURO_API ~GradientDescentBB(); + + /** + * + * @param ef + */ + LIB4NEURO_API void optimize(lib4neuro::ErrorFunction& ef, + std::ofstream* ofs = nullptr) override; + + }; + +} + + +#endif //LIB4NEURO_GRADIENTDESCENTBB_H diff --git a/src/LearningMethods/GradientDescentSingleItem.cpp b/src/LearningMethods/GradientDescentSingleItem.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1f9cdc308d09f2aa7c236f63f09034f68212e076 --- /dev/null +++ b/src/LearningMethods/GradientDescentSingleItem.cpp @@ -0,0 +1,123 @@ +/** + * DESCRIPTION OF THE FILE + * + * @author Michal KravÄenko + * @date 19.2.19 - + */ + +#include "GradientDescentSingleItem.h" + +#include <random.hpp> +#include "message.h" + +namespace lib4neuro { + GradientDescentSingleItem::GradientDescentSingleItem(double epsilon, + size_t n_to_restart, + int max_iters, + size_t batch) { + this->tolerance = epsilon; + this->restart_frequency = n_to_restart; + this->maximum_niters = max_iters; + this->batch = batch; + } + + GradientDescentSingleItem::~GradientDescentSingleItem() { + if (this->optimal_parameters) { + delete this->optimal_parameters; + this->optimal_parameters = nullptr; + } + } + + + double GradientDescentSingleItem::get_optimal_step_size(lib4neuro::ErrorFunction& f, + std::vector<double>& x, + std::vector<double>& d, + size_t n_elems) { + + double alpha = 10.0 / n_elems; + alpha = 1.0; + double value = f.eval(); + double value_shifted = value + 1.0; + + + std::shared_ptr<std::vector<double>> shifted_x = std::make_shared<std::vector<double>>(std::vector<double>(x)); + while (value_shifted > value) { + alpha *= 0.5; + + for (size_t i = 0; i < x.size(); ++i) { + (*shifted_x).at(i) = x.at(i) - alpha * d.at(i); + } + + value_shifted = f.eval(shifted_x.get()); + } + return alpha; + } + + + void GradientDescentSingleItem::optimize(lib4neuro::ErrorFunction& ef, + std::ofstream* ofs) { + + COUT_INFO("Finding a solution via a Gradient Descent [Single Item] method with adaptive step-length..." + << std::endl); + COUT_INFO("Initial error: " << ef.eval() << std::endl); + + size_t total_elements = ef.get_n_data_set(), updated_elements = 0, iter = 0; + double max_error = 1.0, error, gamma; + size_t iter_idx = this->maximum_niters; + size_t dim = ef.get_dimension(); + + std::vector<double> parameter_vector = ef.get_parameters(); + std::vector<double> gradient_vector(dim); + std::vector<double> search_direction(dim); + std::vector<double> error_vector(ef.get_n_outputs()); + while (max_error >= this->tolerance && iter_idx >= 1) { + iter_idx--; + iter++; + + max_error = 0.0; + updated_elements = 0; + std::fill(search_direction.begin(), + search_direction.end(), + 0); + for (size_t i = 0; i < total_elements; ++i) { + error = ef.eval_single_item_by_idx(i, + ¶meter_vector, + error_vector); + + if (error > max_error) { + max_error = error; + } + + if (error > this->tolerance) { + updated_elements++; + ef.calculate_error_gradient_single(error_vector, + gradient_vector); + + for (size_t j = 0; j < dim; ++j) { + search_direction[j] += gradient_vector[j]; + } + } + } + gamma = this->get_optimal_step_size(ef, + parameter_vector, + search_direction, + updated_elements); + + for (size_t j = 0; j < dim; ++j) { + parameter_vector[j] -= gamma * search_direction[j]; + } + + COUT_DEBUG("Iteration: " << iter << ", Total elements in train set: " << total_elements + << ", # of elements with high error: " << updated_elements << ", max. error: " + << max_error << "\r"); + } + COUT_DEBUG("Iteration: " << iter << ", Total elements in train set: " << total_elements + << ", # of elements with high error: " << updated_elements << ", max. error: " + << max_error << std::endl); + + this->optimal_parameters = ¶meter_vector; + ef.set_parameters(*this->optimal_parameters); + + } + +} diff --git a/src/LearningMethods/GradientDescentSingleItem.h b/src/LearningMethods/GradientDescentSingleItem.h new file mode 100644 index 0000000000000000000000000000000000000000..85737e5deb5caff7a567f58352b8dbf3b98703f6 --- /dev/null +++ b/src/LearningMethods/GradientDescentSingleItem.h @@ -0,0 +1,107 @@ +/** + * DESCRIPTION OF THE FILE + * + * @author Michal KravÄenko + * @date 19.2.19 - + */ + +#ifndef LIB4NEURO_GRADIENTDESCENTSINGLEITEM_H +#define LIB4NEURO_GRADIENTDESCENTSINGLEITEM_H + + +#include "../settings.h" +#include "../constants.h" +#include "LearningMethod.h" +#include "../ErrorFunction/ErrorFunctions.h" +#include "GradientDescentBB.h" + +namespace lib4neuro { + /** + * + */ + class GradientDescentSingleItem : public GradientLearningMethod { + + private: + + /** + * Threshold for the successful ending of the optimization - deviation from minima + */ + double tolerance; + + /** + * + */ + double max_error; + + /** + * Number of iterations to reset step size to tolerance/10.0 + */ + size_t restart_frequency; + + /** + * + */ + size_t batch; + + /** + * + */ + size_t iter_max; + + /** + * Maximal number of iterations - optimization will stop after that, even if not converged + */ + long long int maximum_niters; + + /** + * Vector of minima coordinates + */ + std::vector<double>* optimal_parameters = new std::vector<double>(5); + + + protected: + + /** + * Finds the optimal value of step-length in direction @d from position @x of function @f + * @param f + * @param x + * @param d + * @param n_elems + * @return + */ + virtual double get_optimal_step_size(lib4neuro::ErrorFunction& f, + std::vector<double>& x, + std::vector<double>& d, + size_t n_elems); + + + public: + + /** + * Creates an instance of Gradient Descent Optimizer (i.e. back-propagation) + * @param epsilon Threshold for the successful ending of the optimization - deviation from minima + * @param n_to_restart Number of iterations to reset step size to tolerance/10.0 + * @param max_iters Maximal number of iterations - optimization will stop after that, even if not converged + */ + LIB4NEURO_API explicit GradientDescentSingleItem(double epsilon = 1e-3, + size_t n_to_restart = 100, + int max_iters = 1000, + size_t batch = 0); + + /** + * Deallocates the instance + */ + LIB4NEURO_API ~GradientDescentSingleItem(); + + /** + * + * @param ef + */ + LIB4NEURO_API void optimize(lib4neuro::ErrorFunction& ef, + std::ofstream* ofs = nullptr) override; + }; + +} + + +#endif //LIB4NEURO_GRADIENTDESCENTSINGLEITEM_H diff --git a/src/LearningMethods/ILearningMethods.cpp b/src/LearningMethods/ILearningMethods.cpp deleted file mode 100644 index 6aa47daf102bea9655ef0dc8f33c9dce54073092..0000000000000000000000000000000000000000 --- a/src/LearningMethods/ILearningMethods.cpp +++ /dev/null @@ -1,9 +0,0 @@ -/** - * DESCRIPTION OF THE FILE - * - * @author Michal KravÄenko - * @date 10.9.18 - - */ - -#include "ILearningMethods.h" - diff --git a/src/LearningMethods/ILearningMethods.h b/src/LearningMethods/ILearningMethods.h deleted file mode 100644 index c49891fd0cc07ccefcf481d24e1058f42d17185a..0000000000000000000000000000000000000000 --- a/src/LearningMethods/ILearningMethods.h +++ /dev/null @@ -1,35 +0,0 @@ -/** - * This file contains an interface for all learning methods in the library - * - * @author Michal KravÄenko - * @date 12.8.18 - - */ - -#ifndef LIB4NEURO_ILEARNINGMETHODS_H -#define LIB4NEURO_ILEARNINGMETHODS_H - -#include <vector> -#include "../ErrorFunction/ErrorFunctions.h" - -class ILearningMethods { -private: - - /** - * - */ - ErrorFunction *ef = nullptr; - -public: - /* - * Runs the method specific learning algorithm minimizing the given error function - */ - virtual void optimize( ErrorFunction &ef ) = 0; - - /* - * Updates the optimal weight&bias settings in the passed vector - */ - virtual std::vector<double>* get_parameters( ) = 0; -}; - - -#endif //LIB4NEURO_ILEARNINGMETHODS_H diff --git a/src/LearningMethods/LearningMethod.h b/src/LearningMethods/LearningMethod.h new file mode 100644 index 0000000000000000000000000000000000000000..aadc75a30e533a1e485b57fcd3bda838aeac0a95 --- /dev/null +++ b/src/LearningMethods/LearningMethod.h @@ -0,0 +1,50 @@ +/** + * This file contains an interface for all learning methods in the library + * + * @author Michal KravÄenko + * @date 12.8.18 - + */ + +#ifndef LIB4NEURO_ILEARNINGMETHODS_H +#define LIB4NEURO_ILEARNINGMETHODS_H + +#include <vector> +#include "../ErrorFunction/ErrorFunctions.h" + + +namespace lib4neuro { + class LearningMethod { + protected: + + /** + * Vector of minima coordinates + */ + std::vector<double> optimal_parameters; + + public: + /** + * Runs the method specific learning algorithm minimizing the given error function + */ + virtual void optimize(lib4neuro::ErrorFunction& ef, + std::ofstream* ofs = nullptr) = 0; + + /** + * Updates the optimal weight&bias settings in the passed vector + */ + virtual std::vector<double>* get_parameters(); + }; + + class GradientLearningMethod : public LearningMethod { + + + public: + /** + * Runs the method specific learning algorithm minimizing the given error function + */ + virtual void optimize(ErrorFunction& ef, + std::ofstream* ofs = nullptr) override; + + }; +} + +#endif //LIB4NEURO_ILEARNINGMETHODS_H diff --git a/src/LearningMethods/LearningMethods.cpp b/src/LearningMethods/LearningMethods.cpp new file mode 100644 index 0000000000000000000000000000000000000000..545784952abd238a6c6f383157602d73b2f4fd0a --- /dev/null +++ b/src/LearningMethods/LearningMethods.cpp @@ -0,0 +1,15 @@ + +#include "LearningMethod.h" + +namespace lib4neuro { + //TODO NOT SAFE!!!! + std::vector<double>* LearningMethod::get_parameters() { + return &this->optimal_parameters; + } + + void GradientLearningMethod::optimize(ErrorFunction& ef, + std::ofstream* ofs) { + this->optimize(ef, + ofs); + } +} diff --git a/src/LearningMethods/LearningSequence.cpp b/src/LearningMethods/LearningSequence.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2bb38182d8fc91ee2cfa0c62fa7c01db390d433c --- /dev/null +++ b/src/LearningMethods/LearningSequence.cpp @@ -0,0 +1,61 @@ +/** + * DESCRIPTION OF THE FILE + * + * @author Michal KravÄenko + * @date 19.2.19 - + */ + +#include "LearningSequence.h" +#include "../message.h" + +namespace lib4neuro { + + LearningSequence::LearningSequence(double tolerance, + int max_n_cycles) { + this->tol = tolerance; + this->max_number_of_cycles = max_n_cycles; + } + + LearningSequence::~LearningSequence() = default; + + void LearningSequence::add_learning_method(LearningMethod *method) { + this->learning_sequence.push_back(method); + } + + void LearningSequence::optimize(lib4neuro::ErrorFunction& ef, + std::ofstream* ofs) { + double error = ef.eval(); + this->optimal_parameters = ef.get_parameters(); + double the_best_error = error; + int mcycles = this->max_number_of_cycles, cycle_idx = 0; + + std::vector<double> params; + while (error > this->tol && mcycles != 0) { + mcycles--; + cycle_idx++; + + for (auto m: this->learning_sequence) { + m->optimize(ef, + ofs); + + //TODO do NOT copy vectors if not needed + params = *m->get_parameters(); + error = ef.eval(¶ms); + + ef.set_parameters(params); + + if (error < the_best_error) { + the_best_error = error; + this->optimal_parameters = ef.get_parameters(); + } + + if (error <= this->tol) { + ef.set_parameters(this->optimal_parameters); + return; + } + } + COUT_DEBUG("Cycle: " << cycle_idx << ", the lowest error: " << the_best_error << std::endl); + } + ef.set_parameters(this->optimal_parameters); + } +} diff --git a/src/LearningMethods/LearningSequence.h b/src/LearningMethods/LearningSequence.h new file mode 100644 index 0000000000000000000000000000000000000000..b6c81ef7d3c5c79ccd6a4826d907d0139f62af87 --- /dev/null +++ b/src/LearningMethods/LearningSequence.h @@ -0,0 +1,71 @@ +/** + * DESCRIPTION OF THE FILE + * + * @author Michal KravÄenko + * @date 19.2.19 - + */ + +#ifndef LIB4NEURO_LEARNINGSEQUENCE_H +#define LIB4NEURO_LEARNINGSEQUENCE_H + +#include <4neuro.h> +#include "../settings.h" +#include "../constants.h" +#include "LearningMethod.h" + +namespace lib4neuro { + /** + * + */ + class LearningSequence : public LearningMethod { + + private: + + /** + * + */ + std::vector<LearningMethod*> learning_sequence; + + /** + * + */ + double tol; + + /** + * + */ + int max_number_of_cycles = -1; + + + public: + + /** + * + */ + LIB4NEURO_API explicit LearningSequence(double tolerance = 1e-6, + int max_n_cycles = -1); + + /** + * Deallocates the instance + */ + LIB4NEURO_API ~LearningSequence(); + + /** + * + * @param ef + * @param ofs + */ + LIB4NEURO_API void optimize(lib4neuro::ErrorFunction& ef, + std::ofstream* ofs = nullptr) override; + + /** + * + * @param method + */ + LIB4NEURO_API void add_learning_method(LearningMethod *method); + }; + +} + + +#endif //LIB4NEURO_LEARNINGSEQUENCE_H diff --git a/src/LearningMethods/LevenbergMarquardt.cpp b/src/LearningMethods/LevenbergMarquardt.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a73cc8d66aca32d0e5578ea4f6ef6daa4063458b --- /dev/null +++ b/src/LearningMethods/LevenbergMarquardt.cpp @@ -0,0 +1,272 @@ +#include <armadillo> + +#include "LevenbergMarquardt.h" +#include "../message.h" + +struct lib4neuro::LevenbergMarquardt::LevenbergMarquardtImpl { + /** + * Threshold for the successful ending of the optimization - deviation from minima + */ + double tolerance; + + double tolerance_gradient; + + double tolerance_parameters; + + double LM_step_acceptance_threshold; + + double lambda_initial; + + double lambda_increase; + + double lambda_decrease; + + /** + * Maximal number of iterations - optimization will stop after that, even if not converged + */ + size_t maximum_niters; + + size_t batch_size; + + /** + * Returns Jacobian matrix of the residual function using the backpropagation algorithm + * Returns the right hand side of the resulting system of equations related to data errors in @data and approximating function @f + * @param f + * @param J + * @param rhs + * @param data + */ + void get_jacobian_and_rhs(lib4neuro::ErrorFunction& ef, + arma::Mat<double>& J, + arma::Col<double>& rhs, + size_t data_subset_size); +}; + +void lib4neuro::LevenbergMarquardt::LevenbergMarquardtImpl::get_jacobian_and_rhs( + lib4neuro::ErrorFunction& ef, + arma::Mat<double>& J, + arma::Col<double>& rhs, + size_t data_subset_size) { + + + std::vector<std::vector<double>> jacobian; + std::vector<double> rhs_vec; + + if (data_subset_size <= 0) { + data_subset_size = ef.get_n_data_set(); + } + + if (data_subset_size < ef.get_n_data_set()) { + ef.divide_data_train_test((double) data_subset_size / (double) ef.get_n_data_set()); + } + ef.get_jacobian_and_rhs(jacobian, + rhs_vec); + + if (data_subset_size < ef.get_n_data_set()) { + ef.return_full_data_set_for_training(); + } + + size_t dim_out = jacobian.size(); + size_t n_parameters = rhs_vec.size(); + + J.reshape(dim_out, + n_parameters); + rhs.resize(n_parameters); + J.fill(0.0); + rhs.fill(0.0); + + for (size_t ri = 0; ri < jacobian.size(); ++ri) { + for (size_t ci = 0; ci < n_parameters; ++ci) { + J.at(ri, + ci) = jacobian[ri][ci]; + } + } + for (size_t ci = 0; ci < n_parameters; ++ci) { + rhs.at(ci) = rhs_vec.at(ci); + } +} + +namespace lib4neuro { + LevenbergMarquardt::LevenbergMarquardt(size_t max_iters, + size_t bs, + double tolerance, + double tolerance_gradient, + double tolerance_parameters, + double LM_step_acceptance_threshold, + double lambda_initial, + double lambda_increase, + double lambda_decrease) : p_impl(new LevenbergMarquardtImpl()) { + + this->p_impl->batch_size = bs; + this->p_impl->tolerance = tolerance; + this->p_impl->tolerance_gradient = tolerance_gradient; + this->p_impl->tolerance_parameters = tolerance_parameters; + this->p_impl->LM_step_acceptance_threshold = LM_step_acceptance_threshold; + this->p_impl->lambda_initial = lambda_initial; + this->p_impl->lambda_increase = lambda_increase; + this->p_impl->lambda_decrease = lambda_decrease; + this->p_impl->maximum_niters = max_iters; + } + + void LevenbergMarquardt::optimize(lib4neuro::ErrorFunction& ef, + std::ofstream* ofs) { + optimize(ef, + LM_UPDATE_TYPE::MARQUARDT, + ofs); + } + + void LevenbergMarquardt::optimize(lib4neuro::ErrorFunction& ef, + lib4neuro::LM_UPDATE_TYPE update_type, + std::ofstream* ofs) { + double current_err = ef.eval(); + + COUT_INFO( + "Finding a solution via a Levenberg-Marquardt method... Starting error: " << current_err << std::endl); + if (ofs && ofs->is_open()) { + *ofs << "Finding a solution via a Levenberg-Marquardt method... Starting error: " << current_err + << std::endl; + } + + size_t n_parameters = ef.get_dimension(); + size_t n_data_points = ef.get_n_data_set(); + if (this->p_impl->batch_size > 0) { + n_data_points = this->p_impl->batch_size; + } + std::vector<double>* params_current = new std::vector<double>(ef.get_parameters()); + + std::shared_ptr<std::vector<double>> params_tmp; + params_tmp.reset(new std::vector<double>(n_parameters)); + arma::Mat<double> J(n_data_points, + n_parameters); // Jacobian matrix + arma::Mat<double> H(n_data_points, + n_parameters); // Hessian matrix + arma::Mat<double> H_new(n_data_points, + n_parameters); + + double lambda = this->p_impl->lambda_initial; // Dumping parameter + double prev_err = 0, update_norm = 0, gradient_norm = 0, mem_double = 0, jacobian_norm = 1; + + + bool update_J = true; + arma::Col<double> update; + arma::Col<double> rhs; + std::vector<double> d_prep(n_data_points); + arma::Col<double> d; + + double slowdown_coeff = 0.25; + //-------------------// + // Solver iterations // + //-------------------// + size_t iter_counter = 0; + do { + + if (update_J) { + /* Get Jacobian matrix */ + this->p_impl->get_jacobian_and_rhs(ef, + J, + rhs, + this->p_impl->batch_size); + + + gradient_norm = 0; + + for (size_t ci = 0; ci < n_parameters; ++ci) { + mem_double = rhs[ci]; + mem_double *= mem_double; + gradient_norm += mem_double; + } + gradient_norm = std::sqrt(gradient_norm) / J.n_rows; + + /* Get approximation of Hessian (H ~ J'*J) */ + H = J.t() * J; + + jacobian_norm = 0; + for (size_t ri = 0; ri < n_parameters; ++ri) { + for (size_t ci = 0; ci < n_parameters; ++ci) { + jacobian_norm += H.at(ri, + ci) * H.at(ri, + ci); + } + } + jacobian_norm = std::sqrt(jacobian_norm); + + /* Evaluate the error before updating parameters */ + prev_err = ef.eval(); + } + + /* H_new = H + lambda*I */ + H_new = H + lambda * arma::eye(n_parameters, + n_parameters); + + + /* Compute the update vector */ + update = arma::solve(H_new, + rhs); + + /* Compute the error after update of parameters */ + update_norm = 0.0; + for (size_t i = 0; i < n_parameters; i++) { + params_tmp->at(i) = params_current->at(i) + update.at(i); + update_norm += update.at(i) * update.at(i); + } + update_norm = std::sqrt(update_norm); + current_err = ef.eval(params_tmp.get()); + + /* Check, if the parameter update improved the function */ + if (current_err < prev_err) { + + /* If the convergence threshold is achieved, finish the computation */ + if (current_err < this->p_impl->tolerance) { + break; + } + + /* If the error is lowered after parameter update, accept the new parameters and lower the damping + * factor lambda */ + + //TODO rewrite without division! + lambda /= this->p_impl->lambda_decrease; + + for (size_t i = 0; i < n_parameters; i++) { + params_current->at(i) = params_tmp->at(i); + } + + prev_err = current_err; + update_J = true; + + + } else { + /* If the error after parameters update is not lower, increase the damping factor lambda */ + update_J = false; + lambda *= this->p_impl->lambda_increase; + } + COUT_DEBUG("Iteration: " << iter_counter << " Current error: " << current_err << ", Current gradient norm: " + << gradient_norm << ", Direction norm: " << update_norm << "\r"); + + if (ofs && ofs->is_open()) { + *ofs << "Iteration: " << iter_counter << " Current error: " << current_err << ", Current gradient norm: " + << gradient_norm << ", Direction norm: " << update_norm << std::endl; + } + } while (iter_counter++ < this->p_impl->maximum_niters && (update_norm > this->p_impl->tolerance)); + COUT_DEBUG("Iteration: " << iter_counter << " Current error: " << current_err << ", Current gradient norm: " + << gradient_norm << ", Direction norm: " << update_norm << std::endl); + if (ofs && ofs->is_open()) { + *ofs << "Iteration: " << iter_counter << " Current error: " << current_err << ", Current gradient norm: " + << gradient_norm << ", Direction norm: " << update_norm << std::endl; + } + + /* Store the optimized parameters */ + this->optimal_parameters = *params_current; + + /* Dealloc vector of parameters */ + if (params_current) { + delete params_current; + params_current = nullptr; + } + + ef.set_parameters(this->optimal_parameters); + + + } + + LevenbergMarquardt::~LevenbergMarquardt() = default; +} diff --git a/src/LearningMethods/LevenbergMarquardt.h b/src/LearningMethods/LevenbergMarquardt.h new file mode 100644 index 0000000000000000000000000000000000000000..aef59b207bf4f84807c217b4353a0a266de2f968 --- /dev/null +++ b/src/LearningMethods/LevenbergMarquardt.h @@ -0,0 +1,53 @@ + +#ifndef LIB4NEURO_LEVENBERGMARQUARDT_H +#define LIB4NEURO_LEVENBERGMARQUARDT_H + +#include <memory> + +#include "LearningMethod.h" + +namespace lib4neuro { + + enum LM_UPDATE_TYPE { + MARQUARDT, + QUADRATIC, + NIELSEN + }; + + /** + * Method implementing Levenberg-Marquardt optimization algorithm + * + * This version is described in the paper: + * Gavin, Henri. "The Levenberg-Marquardt method for nonlinear least squares curve-fitting problems." + * Department of Civil and Environmental Engineering, Duke University (2011): 1-15. + */ + class LevenbergMarquardt : public GradientLearningMethod { + + private: + struct LevenbergMarquardtImpl; + std::shared_ptr<LevenbergMarquardtImpl> p_impl; + + public: + LevenbergMarquardt(size_t max_iters, + size_t bs = 0, + double tolerance = 1e-2, + double tolerance_gradient = 1e-3, + double tolerance_parameters = 1e-3, + double LM_step_acceptance_threshold = 1e-1, + double lambda_initial = 1e-2, + double lambda_increase = 11, + double lambda_decrease = 9); + + void optimize(ErrorFunction& ef, + std::ofstream* ofs = nullptr); + + void optimize(ErrorFunction& ef, + LM_UPDATE_TYPE update_type, + std::ofstream* ofs = nullptr); + + ~LevenbergMarquardt(); + }; + +} + +#endif //LIB4NEURO_LEVENBERGMARQUARDT_H diff --git a/src/LearningMethods/ParticleSwarm.cpp b/src/LearningMethods/ParticleSwarm.cpp index 763932404ea155386244b962e30db4173006ad32..c8528554ffd7ffc7747aa92f6d4c24e699071b68 100644 --- a/src/LearningMethods/ParticleSwarm.cpp +++ b/src/LearningMethods/ParticleSwarm.cpp @@ -5,7 +5,22 @@ * @date 2.7.18 - */ +#include <cstdlib> +#include <ctime> +#include <cmath> +#include <set> +#include <stdexcept> +#include <random> +#include <iterator> +#include <algorithm> #include <iostream> +#include <format.hpp> + +#include "message.h" +#include "../Network/NeuralNetwork.h" +#include "../DataSet/DataSet.h" +#include "exceptions.h" + #include "ParticleSwarm.h" /** @@ -20,42 +35,83 @@ void Particle::randomize_coordinates() { std::random_device seeder; - std::mt19937 gen(seeder()); - this->domain_bounds = domain_bounds; - for(unsigned int i = 0; i < this->coordinate_dim; ++i){ - std::uniform_real_distribution<double> dist_coord(-1, 1); + std::mt19937 gen(seeder()); + for (unsigned int i = 0; i < this->coordinate_dim; ++i) { + std::uniform_real_distribution<double> dist_coord(this->domain_bounds->at(2 * i), + this->domain_bounds->at(2 * i + 1)); (*this->coordinate)[i] = dist_coord(gen); } } void Particle::randomize_parameters() { - std::random_device seeder; - std::mt19937 gen(seeder()); - std::uniform_real_distribution<double> dist_vel(0.5, 1.0); + std::random_device seeder; + std::mt19937 gen(seeder()); + std::uniform_real_distribution<double> dist_vel(0.5, + 1.0); this->r1 = dist_vel(gen); this->r2 = dist_vel(gen); this->r3 = dist_vel(gen); } void Particle::randomize_velocity() { - std::random_device seeder; - std::mt19937 gen(seeder()); - std::uniform_real_distribution<double> dist_vel(0.5, 1.0); - for(unsigned int i = 0; i < this->coordinate_dim; ++i){ + std::random_device seeder; + std::mt19937 gen(seeder()); + std::uniform_real_distribution<double> dist_vel(0.5, + 1.0); + for (unsigned int i = 0; i < this->coordinate_dim; ++i) { (*this->velocity)[i] = dist_vel(gen); } } -Particle::Particle(ErrorFunction *ef, std::vector<double> *domain_bounds) { +Particle::Particle(lib4neuro::ErrorFunction* ef, + std::vector<double>* domain_bounds) { - this->ef = ef; - this->domain_bounds = domain_bounds; + this->ef = ef; + this->domain_bounds = new std::vector<double>(*domain_bounds); this->coordinate_dim = ef->get_dimension(); + this->ef = ef; + + this->coordinate = new std::vector<double>(this->coordinate_dim); + this->velocity = new std::vector<double>(this->coordinate_dim); + this->optimal_coordinate = new std::vector<double>(this->coordinate_dim); + + + this->randomize_velocity(); + this->randomize_parameters(); + this->randomize_coordinates(); + + for (unsigned int i = 0; i < this->coordinate_dim; ++i) { + (*this->optimal_coordinate)[i] = (*this->coordinate)[i]; + } + + this->optimal_value = this->ef->eval(this->coordinate); + +} + +Particle::Particle(lib4neuro::ErrorFunction* ef, + std::vector<double>* central_system, + double dispersion_coeff) { + this->ef = ef; - this->coordinate = new std::vector<double>(this->coordinate_dim); - this->velocity = new std::vector<double>(this->coordinate_dim); + if (this->domain_bounds) { + delete this->domain_bounds; + } + + this->domain_bounds = new std::vector<double>(2 * central_system->size()); + + + for (size_t i = 0; i < central_system->size(); ++i) { + this->domain_bounds->at(2 * i) = central_system->at(i) - dispersion_coeff; + this->domain_bounds->at(2 * i + 1) = central_system->at(i) + dispersion_coeff; + } + + this->coordinate_dim = ef->get_dimension(); + this->ef = ef; + + this->coordinate = new std::vector<double>(this->coordinate_dim); + this->velocity = new std::vector<double>(this->coordinate_dim); this->optimal_coordinate = new std::vector<double>(this->coordinate_dim); @@ -63,7 +119,7 @@ Particle::Particle(ErrorFunction *ef, std::vector<double> *domain_bounds) { this->randomize_parameters(); this->randomize_coordinates(); - for(unsigned int i = 0; i < this->coordinate_dim; ++i){ + for (unsigned int i = 0; i < this->coordinate_dim; ++i) { (*this->optimal_coordinate)[i] = (*this->coordinate)[i]; } @@ -73,18 +129,25 @@ Particle::Particle(ErrorFunction *ef, std::vector<double> *domain_bounds) { Particle::~Particle() { - if( this->optimal_coordinate ){ + if (this->optimal_coordinate) { delete this->optimal_coordinate; + this->optimal_coordinate = nullptr; } - if( this->coordinate ){ + if (this->coordinate) { delete this->coordinate; + this->coordinate = nullptr; } - if( this->velocity ){ + if (this->velocity) { delete this->velocity; + this->velocity = nullptr; } + if (this->domain_bounds) { + delete this->domain_bounds; + this->domain_bounds = nullptr; + } } std::vector<double>* Particle::get_coordinate() { @@ -99,13 +162,18 @@ double Particle::get_optimal_value() { return this->optimal_value; } -void Particle::get_optimal_coordinate(std::vector<double> &ref_coordinate) { - for( unsigned int i = 0; i < this->coordinate_dim; ++i ){ +void Particle::get_optimal_coordinate(std::vector<double>& ref_coordinate) { + for (unsigned int i = 0; i < this->coordinate_dim; ++i) { ref_coordinate[i] = (*this->optimal_coordinate)[i]; } } -double Particle::change_coordinate(double w, double c1, double c2, std::vector<double> &glob_min_coord, std::vector<std::vector<double>> &global_min_vec, double penalty_coef) { +double Particle::change_coordinate(double w, + double c1, + double c2, + std::vector<double>& glob_min_coord, + std::vector<std::vector<double>>& global_min_vec, + double penalty_coef) { /** * v = w * v + c1r1(p_min_loc - x) + c2r2(p_min_glob - x) + c3r3(random_global_min - x) @@ -116,20 +184,21 @@ double Particle::change_coordinate(double w, double c1, double c2, std::vector<d double output = 0.0; /* Choose random global minima */ - std::vector<double> *random_global_best; - std::random_device rand_dev; - std::mt19937 engine{rand_dev()}; - std::uniform_int_distribution<size_t> dist(0, global_min_vec.size() - 1); + std::vector<double>* random_global_best; + std::random_device rand_dev; + std::mt19937 engine{rand_dev()}; + std::uniform_int_distribution<size_t> dist(0, + global_min_vec.size() - 1); random_global_best = &global_min_vec[dist(engine)]; // TODO use std::sample to choose random vector //std::sample(global_min_vec.begin(), global_min_vec.end(), std::back_inserter(random_global_best), 1, std::mt19937{std::random_device{}()}); - for(unsigned int i = 0; i < this->coordinate_dim; ++i) { + for (unsigned int i = 0; i < this->coordinate_dim; ++i) { vel_mem = w * (*this->velocity)[i] + c1 * this->r1 * ((*this->optimal_coordinate)[i] - (*this->coordinate)[i]) + c2 * this->r2 * (glob_min_coord[i] - (*this->coordinate)[i]) - + (c1+c2)/2 * this->r3 * ((*random_global_best)[i] - (*this->coordinate)[i]); + + (c1 + c2) / 2 * this->r3 * ((*random_global_best)[i] - (*this->coordinate)[i]); if ((*this->coordinate)[i] + vel_mem > this->domain_bounds->at(2 * i + 1)) { this->randomize_velocity(); @@ -144,11 +213,11 @@ double Particle::change_coordinate(double w, double c1, double c2, std::vector<d } } - for(unsigned int i = 0; i < this->coordinate_dim; ++i){ + for (unsigned int i = 0; i < this->coordinate_dim; ++i) { vel_mem = w * (*this->velocity)[i] - + c1 * this->r1 * ((*this->optimal_coordinate)[i] - (*this->coordinate)[i]) - + c2 * this->r2 * (glob_min_coord[i] - (*this->coordinate)[i]) - + (c1+c2)/2 * this->r3 * ((*random_global_best)[i] - (*this->coordinate)[i]); + + c1 * this->r1 * ((*this->optimal_coordinate)[i] - (*this->coordinate)[i]) + + c2 * this->r2 * (glob_min_coord[i] - (*this->coordinate)[i]) + + (c1 + c2) / 2 * this->r3 * ((*random_global_best)[i] - (*this->coordinate)[i]); (*this->velocity)[i] = vel_mem; @@ -160,9 +229,9 @@ double Particle::change_coordinate(double w, double c1, double c2, std::vector<d vel_mem = this->ef->eval(this->coordinate); this->current_val = vel_mem; - if(vel_mem < this->optimal_value){ + if (vel_mem < this->optimal_value) { this->optimal_value = vel_mem; - for(unsigned int i = 0; i < this->coordinate_dim; ++i){ + for (unsigned int i = 0; i < this->coordinate_dim; ++i) { (*this->optimal_coordinate)[i] = (*this->coordinate)[i]; } } @@ -171,146 +240,132 @@ double Particle::change_coordinate(double w, double c1, double c2, std::vector<d } void Particle::print_coordinate() { - for(unsigned int i = 0; i < this->coordinate_dim - 1; ++i){ - printf("%10.8f, ", (*this->coordinate)[i]); + for (unsigned int i = 0; i < this->coordinate_dim - 1; ++i) { + std::cout << (*this->coordinate)[i] << " "; } - printf("%10.8f\n", (*this->coordinate)[this->coordinate_dim - 1]); + std::cout << (*this->coordinate)[this->coordinate_dim - 1] << std::endl; } -ParticleSwarm::ParticleSwarm( - std::vector<double> *domain_bounds, - double c1, - double c2, - double w, - double gamma, - double epsilon, - double delta, - size_t n_particles, - size_t iter_max - ) { - - srand(time(NULL)); - - +namespace lib4neuro { + ParticleSwarm::ParticleSwarm(std::vector<double>* domain_bounds, + double c1, + double c2, + double w, + double gamma, + double epsilon, + double delta, + size_t n_particles, + size_t iter_max) { + srand(time(NULL)); //TODO rewrite using boost.random + + if (epsilon < 0 || gamma < 0 || delta < 0) { + THROW_INVALID_ARGUMENT_ERROR( + "Parameters 'gamma', 'epsilon' and 'delta' must be greater than or equal to zero!"); + } - if(epsilon < 0 || gamma < 0 || delta < 0) { - throw std::invalid_argument("Parameters 'gamma', 'epsilon' and 'delta' must be greater than or equal to zero!"); + this->gamma = gamma; + this->epsilon = epsilon; + this->delta = delta; + this->pst = PARTICLE_SWARM_TYPE::GENERAL; + + this->init_constructor(domain_bounds, + c1, + c2, + w, + n_particles, + iter_max); } - this->c1 = c1; - - this->c2 = c2; - - this->c3 = (c1 + c2)/2.0; - - this->w = w; - - this->gamma = gamma; - - this->epsilon = epsilon; - - this->delta = delta; - - this->n_particles = n_particles; - - this->iter_max = iter_max; - - this->particle_swarm = new std::vector<Particle*>( this->n_particles ); - - this->domain_bounds = domain_bounds; - - std::fill( this->particle_swarm->begin(), this->particle_swarm->end(), nullptr ); - -} - -ParticleSwarm::~ParticleSwarm() { + ParticleSwarm::~ParticleSwarm() { + for (size_t pi = 0; pi < this->particle_swarm.size(); ++pi) { + if (this->particle_swarm.at(pi)) { + delete this->particle_swarm.at(pi); + } - if( this->particle_swarm ){ - for( size_t i = 0; i < this->n_particles; ++i ){ - delete this->particle_swarm->at( i ); } - - delete this->particle_swarm; - this->particle_swarm = nullptr; + this->particle_swarm.clear(); } - if(this->p_min_glob){ - delete this->p_min_glob; - this->p_min_glob = nullptr; - } + /** + * + * + */ + void ParticleSwarm::optimize(lib4neuro::ErrorFunction& ef, + std::ofstream* ofs) { + //TODO add output to the 'ofs' -} + COUT_INFO("Finding optima via Globalized Particle Swarm method..." << std::endl); + if (ofs && ofs->is_open()) { + *ofs << "Finding optima via Globalized Particle Swarm method..." << std::endl; + } -/** - * - * @param gamma - * @param epsilon - * @param delta - */ -void ParticleSwarm::optimize( ErrorFunction &ef ) { + if (this->epsilon < 0 || this->gamma < 0 || this->delta < 0) { + THROW_INVALID_ARGUMENT_ERROR( + "Parameters 'gamma', 'epsilon' and 'delta' must be greater than or equal to zero!"); + } - if(this->domain_bounds->size() < 2 * ef.get_dimension()){ - std::cerr << "The supplied domain bounds dimension is too low! It should be at least " << 2 * ef.get_dimension() << "\n" << std::endl; - } + this->func_dim = ef.get_dimension(); - this->func_dim = ef.get_dimension(); - /* initialize the particles */ - for( size_t pi = 0; pi < this->n_particles; ++pi ){ - if(this->particle_swarm->at( pi )){ - delete this->particle_swarm->at( pi ); + /* initialize the particles */ + std::vector<double> centroids(ef.get_parameters()); + for (size_t pi = 0; pi < this->particle_swarm.size(); ++pi) { + if (this->particle_swarm.at(pi)) { + delete this->particle_swarm.at(pi); + } + this->particle_swarm.at(pi) = new Particle(&ef, + ¢roids, + this->radius_factor); } - this->particle_swarm->at( pi ) = new Particle( &ef, this->domain_bounds ); - } + this->radius_factor *= 1.25; - if(!this->p_min_glob){ - this->p_min_glob = new std::vector<double>(this->func_dim); - } - else{ - this->p_min_glob->resize(this->func_dim); - } + this->optimal_parameters.resize(this->func_dim); - size_t outer_it = 0; - Particle *particle; + size_t outer_it = 0; + Particle* particle; - std::vector<std::vector<double>> global_best_vec; - double optimal_value = 0.0; + std::vector<std::vector<double>> global_best_vec; + double optimal_value = 0.0; - std::set<Particle*> cluster; //!< Particles in a cluster - std::vector<double>* centroid = new std::vector<double>(this->func_dim);//<! Centroid coordinates + std::set<Particle*> cluster; //!< Particles in a cluster + std::vector<double>* centroid = new std::vector<double>(this->func_dim);//<! Centroid coordinates - double tmp_velocity; - double prev_max_velocity = 0; - double max_velocity; - double max_vel_step = 0; - double prev_max_vel_step; - double euclidean_dist; + double tmp_velocity; + double prev_max_velocity = 0; + double max_velocity; + double max_vel_step = 0; + double prev_max_vel_step; + double euclidean_dist; + double current_err = -1; - this->determine_optimal_coordinate_and_value(*this->p_min_glob, optimal_value); -// for(unsigned int i = 0; i < this->n_particles; ++i){ -// this->particle_swarm[i]->print_coordinate(); -// } - printf("Initial best value: %10.8f\n", optimal_value); + this->determine_optimal_coordinate_and_value(this->optimal_parameters, + optimal_value); + COUT_INFO("Initial best value: " << optimal_value << std::endl); - while( outer_it < this->iter_max ) { - max_velocity = 0; - euclidean_dist = 0; + while (outer_it < this->iter_max) { + max_velocity = 0; + euclidean_dist = 0; - ////////////////////////////////////////////////// - // Clustering algorithm - termination condition // - ////////////////////////////////////////////////// - particle = this->determine_optimal_coordinate_and_value(*this->p_min_glob, optimal_value); + ////////////////////////////////////////////////// + // Clustering algorithm - termination condition // + ////////////////////////////////////////////////// + particle = this->determine_optimal_coordinate_and_value(this->optimal_parameters, + optimal_value); - if(std::find(global_best_vec.begin(), global_best_vec.end(), *this->p_min_glob) == global_best_vec.end()) { - global_best_vec.emplace_back(*this->p_min_glob); // TODO rewrite as std::set - } + if (std::find(global_best_vec.begin(), + global_best_vec.end(), + this->optimal_parameters) == global_best_vec.end()) { + global_best_vec.emplace_back(this->optimal_parameters); // TODO rewrite as std::set + } - cluster.insert(particle); + cluster.insert(particle); - //for(unsigned int i=0; i < 5; i++) { + //for(unsigned int i=0; i < 5; i++) { /* Zero AVG coordinates */ - std::fill(centroid->begin(), centroid->end(), 0); - std::vector<double> *c_ptr; + std::fill(centroid->begin(), + centroid->end(), + 0); + std::vector<double>* c_ptr; /* Looking for a centroid */ for (auto it : cluster) { @@ -320,18 +375,21 @@ void ParticleSwarm::optimize( ErrorFunction &ef ) { } } - for(size_t di = 0; di < this->func_dim; di++) { + for (size_t di = 0; di < this->func_dim; di++) { (*centroid)[di] /= cluster.size(); } - for(size_t pi=0; pi < this->n_particles; pi++) { - particle = this->particle_swarm->at(pi); - tmp_velocity = particle->change_coordinate( this->w, this->c1, this->c2, *this->p_min_glob, global_best_vec); -// particle->print_coordinate(); + for (size_t pi = 0; pi < this->n_particles; pi++) { + particle = this->particle_swarm.at(pi); + tmp_velocity = particle->change_coordinate(this->w, + this->c1, + this->c2, + this->optimal_parameters, + global_best_vec); - if(tmp_velocity > max_velocity) { + if (tmp_velocity > max_velocity) { prev_max_velocity = max_velocity; - max_velocity = tmp_velocity; + max_velocity = tmp_velocity; } /* Looking for nearby particles */ @@ -339,110 +397,192 @@ void ParticleSwarm::optimize( ErrorFunction &ef ) { // TODO - only in verbose mode // only for info purposes - euclidean_dist += this->get_euclidean_distance(particle->get_coordinate(), centroid); + euclidean_dist += this->get_euclidean_distance(particle->get_coordinate(), + centroid); - if(this->get_euclidean_distance(particle->get_coordinate(), centroid) < this->epsilon) { + if (this->get_euclidean_distance(particle->get_coordinate(), + centroid) < epsilon) { cluster.insert(particle); } } - //} + //} - prev_max_vel_step = max_vel_step; - max_vel_step = max_velocity - prev_max_velocity; + prev_max_vel_step = max_vel_step; + max_vel_step = max_velocity - prev_max_velocity; - //TODO only in verbose mode - euclidean_dist /= this->n_particles; - if(outer_it % 10 == 0){ - //printf("Iteration %d, avg euclidean distance: %f, cluster percent: %f, f-value: %f\r", (int)outer_it, euclidean_dist, - // double(cluster.size())/this->n_particles, optimal_value); - //std::cout.flush(); - } + //TODO only in verbose mode + euclidean_dist /= this->n_particles; + if (outer_it % 10 == 0) { + //printf("Iteration %d, avg euclidean distance: %f, cluster percent: %f, f-value: %f\r", (int)outer_it, euclidean_dist, + // double(cluster.size())/this->n_particles, optimal_value); + //std::cout.flush(); + } -// for(unsigned int i=0; i < this->n_particles; i++) { -// printf("Particle %d (%f): \n", i, this->particle_swarm[i]->get_current_value()); -// for(unsigned int j=0; j < this->func_dim; j++) { -// printf("\t%f\n", this->particle_swarm[i]->get_coordinate()[j]); -// } -// } - /* Check if the particles are near to each other AND the maximal velocity is less than 'gamma' */ - if( cluster.size() > this->delta * this->n_particles && prev_max_vel_step < this->gamma * max_vel_step ) { - break; + current_err = ef.eval(&this->optimal_parameters); + + COUT_DEBUG(std::string("Iteration: ") << (unsigned int) (outer_it) + << ". Total error: " << current_err + << ". Objective function value: " << optimal_value + << ".\r"); + if (ofs && ofs->is_open()) { + *ofs << "Iteration: " << (outer_it) + << ". Total error: " << current_err + << ". Objective function value: " << optimal_value + << std::endl; + } + + + if (this->err_thresh) { + + /* If the error threshold is given, then check the current error */ + if (current_err <= this->err_thresh) { + break; + } + } else { + + /* Check if the particles are near to each other AND the maximal velocity is less than 'gamma' */ + if (cluster.size() >= this->delta * this->n_particles && + prev_max_vel_step <= this->gamma * max_vel_step) { + break; + } + } + + outer_it++; + + //TODO parameter for inertia weight decrease? + + } + COUT_DEBUG(std::string("Iteration: ") << (unsigned int) (outer_it) + << ". Total error: " << current_err + << ". Objective function value: " << optimal_value + << "." << std::endl); + if (ofs && ofs->is_open()) { + *ofs << "Iteration: " << (outer_it) + << ". Total error: " << current_err + << ". Objective function value: " << optimal_value + << std::endl; } - outer_it++; -// this->w *= 0.99; - } + this->determine_optimal_coordinate_and_value(this->optimal_parameters, + optimal_value); + //TODO rewrite following output using COUT_INFO + if (outer_it < this->iter_max) { + /* Convergence reached */ + COUT_INFO(std::endl << "Found optimum in " << outer_it << " iterations. Objective function value: " + << optimal_value << std::endl); + if (ofs && ofs->is_open()) { + *ofs << "Found optimum in " << outer_it << " iterations. Objective function value: " + << optimal_value << std::endl; + } + } else { + /* Maximal number of iterations reached */ + COUT_INFO(std::endl << "Max number of iterations reached (" << outer_it << ")! Objective function value: " + << optimal_value << std::endl); + if (ofs && ofs->is_open()) { + *ofs << "Max number of iterations reached (" << outer_it << ")! Objective function value: " + << optimal_value << std::endl; + } + } - this->determine_optimal_coordinate_and_value(*this->p_min_glob, optimal_value); - if(outer_it < this->iter_max) { - /* Convergence reached */ - printf("\nFound optimum in %d iterations. Objective function value: %10.8f\n", (int)outer_it, optimal_value); - } else { - /* Maximal number of iterations reached */ - printf("\nMax number of iterations reached (%d)! Objective function value: %10.8f\n", (int)outer_it, optimal_value); + ef.set_parameters(this->optimal_parameters); + + delete centroid; } -// for (size_t i = 0; i <= this->func_dim - 1; ++i) { -// printf("%10.8f \n", (*this->p_min_glob)[i]); -// } -// -// this->f->eval( this->get_solution() ); + ParticleSwarm::ParticleSwarm(std::vector<double>* domain_bounds, + double err_thresh, + PARTICLE_SWARM_TYPE pst, + double c1, + double c2, + double w, + size_t n_particles, + size_t iter_max) { + + if (err_thresh <= 0) { + THROW_INVALID_ARGUMENT_ERROR("Error threshold has to be greater then 0!"); + } - delete centroid; -} + this->err_thresh = err_thresh; + this->pst = pst; -Particle* ParticleSwarm::determine_optimal_coordinate_and_value(std::vector<double> &coord, double &val) { + this->init_constructor(domain_bounds, + c1, + c2, + w, + n_particles, + iter_max); + } + + Particle* ParticleSwarm::determine_optimal_coordinate_and_value(std::vector<double>& coord, + double& val) { - Particle* p; + Particle* p; - val = this->particle_swarm->at(0)->get_optimal_value( ); - this->particle_swarm->at(0)->get_optimal_coordinate(coord); - p = this->particle_swarm->at(0); + val = this->particle_swarm.at(0)->get_optimal_value(); + this->particle_swarm.at(0)->get_optimal_coordinate(coord); + p = this->particle_swarm.at(0); - for(size_t i = 1; i < this->n_particles; ++i){ + for (size_t i = 1; i < this->n_particles; ++i) { - double val_m = this->particle_swarm->at(i)->get_optimal_value( ); + double val_m = this->particle_swarm.at(i)->get_optimal_value(); - if(val_m < val){ - val = val_m; - this->particle_swarm->at(i)->get_optimal_coordinate(coord); - p = this->particle_swarm->at(i); + if (val_m < val) { + val = val_m; + this->particle_swarm.at(i)->get_optimal_coordinate(coord); + p = this->particle_swarm.at(i); + } } + + return p; } - return p; -} + std::vector<double>* ParticleSwarm::get_centroid_coordinates() { + std::vector<double>* coords = new std::vector<double>(this->func_dim); + std::vector<double>* tmp; -std::vector<double>* ParticleSwarm::get_centroid_coordinates() { - std::vector<double>* coords = new std::vector<double>(this->func_dim); - std::vector<double>* tmp; + for (size_t pi = 0; pi < this->n_particles; pi++) { + tmp = this->particle_swarm.at(pi)->get_coordinate(); - for (size_t pi = 0; pi < this->n_particles; pi++) { - tmp = this->particle_swarm->at(pi)->get_coordinate(); + for (size_t di = 0; di < this->func_dim; di++) { + (*coords)[di] += (*tmp)[di]; + } + } for (size_t di = 0; di < this->func_dim; di++) { - (*coords)[di] += (*tmp)[di]; + (*coords)[di] /= this->n_particles; } - } - for(size_t di = 0; di < this->func_dim; di++) { - (*coords)[di] /= this->n_particles; + return coords; } - return coords; -} + double ParticleSwarm::get_euclidean_distance(std::vector<double>* a, + std::vector<double>* b) { + double dist = 0, m; + for (size_t i = 0; i < a->size(); i++) { + m = (*a)[i] - (*b)[i]; + m *= m; + dist += m; + } + return std::sqrt(dist); + } -double ParticleSwarm::get_euclidean_distance(std::vector<double>* a, std::vector<double>* b) { - double dist = 0, m; - for(size_t i = 0; i < a->size(); i++) { - m = (*a)[i]-(*b)[i]; - m *= m; - dist += m; + void ParticleSwarm::init_constructor(std::vector<double>* domain_bounds, + double c1, + double c2, + double w, + size_t n_particles, + size_t iter_max) { + this->c1 = c1; + this->c2 = c2; + this->c3 = (c1 + c2) / 2.0; + this->w = w; + this->n_particles = n_particles; + this->iter_max = iter_max; + this->particle_swarm.resize(this->n_particles); + std::fill(this->particle_swarm.begin(), + this->particle_swarm.end(), + nullptr); } - return std::sqrt(dist); -} -std::vector<double>* ParticleSwarm::get_parameters( ) { - return this->p_min_glob; -} \ No newline at end of file +} diff --git a/src/LearningMethods/ParticleSwarm.h b/src/LearningMethods/ParticleSwarm.h index bf2bb2db363dd60766d5d824ea1bd2e4698c645b..642b925cec9585641c6f87fd57173bc1c87b5948 100644 --- a/src/LearningMethods/ParticleSwarm.h +++ b/src/LearningMethods/ParticleSwarm.h @@ -9,30 +9,21 @@ #define INC_4NEURO_PARTICLESWARM_H #include "../settings.h" - -#include <cstdlib> -#include <ctime> -#include <cmath> -#include <set> -#include <stdexcept> -#include <random> -#include <iterator> -#include <algorithm> - -#include "../Network/NeuralNetwork.h" -#include "../DataSet/DataSet.h" #include "../ErrorFunction/ErrorFunctions.h" -#include "ILearningMethods.h" +#include "LearningMethod.h" -class Particle{ +/** + * + */ +class Particle { private: size_t coordinate_dim; - std::vector<double> *coordinate = nullptr; - std::vector<double> *velocity = nullptr; + std::vector<double>* coordinate = nullptr; + std::vector<double>* velocity = nullptr; - std::vector<double> *optimal_coordinate = nullptr; + std::vector<double>* optimal_coordinate = nullptr; double optimal_value; double r1; @@ -41,9 +32,9 @@ private: double current_val; - ErrorFunction* ef; + lib4neuro::ErrorFunction* ef = nullptr; - std::vector<double> *domain_bounds; + std::vector<double>* domain_bounds = nullptr; void randomize_coordinates(); @@ -63,8 +54,20 @@ public: * * @param f_dim */ - LIB4NEURO_API Particle( ErrorFunction *ef, std::vector<double> *domain_bounds ); - LIB4NEURO_API ~Particle( ); + LIB4NEURO_API Particle(lib4neuro::ErrorFunction* ef, + std::vector<double>* domain_bounds); + + /** + * + * @param ef + * @param central_system + * @param dispersion_coeff + */ + LIB4NEURO_API Particle(lib4neuro::ErrorFunction* ef, + std::vector<double>* central_system, + double dispersion_coeff); + + LIB4NEURO_API ~Particle(); /** * @@ -88,7 +91,7 @@ public: * * @param ref_coordinate */ - LIB4NEURO_API void get_optimal_coordinate(std::vector<double> &ref_coordinate); + LIB4NEURO_API void get_optimal_coordinate(std::vector<double>& ref_coordinate); /** * @@ -98,90 +101,162 @@ public: * @param glob_min_coord * @param penalty_coef */ - LIB4NEURO_API double change_coordinate(double w, double c1, double c2, std::vector<double> &glob_min_coord, std::vector<std::vector<double>> &global_min_vec, double penalty_coef=0.25); + LIB4NEURO_API double change_coordinate(double w, + double c1, + double c2, + std::vector<double>& glob_min_coord, + std::vector<std::vector<double>>& global_min_vec, + double penalty_coef = 0.25); }; - -class ParticleSwarm: public ILearningMethods { - -private: +namespace lib4neuro { /** - * + * Particle Swarm method type differentiating between a general version and a version expecting cost function minima + * to be 0! */ - std::vector<Particle*> *particle_swarm = nullptr; + enum PARTICLE_SWARM_TYPE { + GENERAL, + MIN_ZERO + }; /** - * + * Class implementing the Global Particle Swarm Optimization method */ - ErrorFunction* f; - - size_t func_dim; - - size_t n_particles; - - size_t iter_max; - - double c1; - - double c2; - - double c3; - - double w; - - double gamma; - - double epsilon; - - double delta; - - double global_optimal_value; - - std::vector<double> *domain_bounds = nullptr; - - std::vector<double> *p_min_glob = nullptr; - -protected: - /** - * - * @param coord - * @param val - * @return - */ - LIB4NEURO_API Particle* determine_optimal_coordinate_and_value(std::vector<double> &coord, double &val); - - /** - * - * @return - */ - LIB4NEURO_API std::vector<double>* get_centroid_coordinates(); - - /** - * - * @param a - * @param b - * @param n - * @return - */ - LIB4NEURO_API double get_euclidean_distance(std::vector<double>* a, std::vector<double>* b); - -public: - - /** - * - * @param ef - * @param f_dim - * @param domain_bounds - * @param c1 - * @param c2 - * @param w - * @param n_particles - * @param iter_max - */ - //TODO make domain_bounds constant - LIB4NEURO_API ParticleSwarm( - std::vector<double> *domain_bounds, + class ParticleSwarm : public LearningMethod { + + private: + + /** + * Vector of particles contained in the swarm + */ + std::vector<Particle*> particle_swarm; // = nullptr; + + /** + * Dimension of the optimized function + */ + size_t func_dim; + + /** + * Number of particles in the swarm + */ + size_t n_particles; + + /** + * Maximal number of iterations - optimization will stop after that, even if not converged + */ + size_t iter_max; + + /** + * Cognitive parameter + */ + double c1; + + /** + * Social parameter + */ + double c2; + + /** + * Experience parameter -mean of c1 and c2 + */ + double c3; + + /** + * Inertia weight + */ + double w; + + /** + * Threshold value for particle velocity - all particles must posses the same or slower velocity for the algorithm to end + */ + double gamma; + + /** + * Radius of the cluster area (Euclidean distance) + */ + double epsilon; + + /** + * Amount of particles, which has to be in the cluster for the algorithm to stop (0-1) + */ + double delta; + + /** + * increases the range of the particle dispersion with each consecutive run of @this->optimize + */ + double radius_factor = 1.0; + + /** + * Error threshold - determines a successful convergence + * + * Must be greater than 0! + */ + double err_thresh = 0; + + /** + * Type of particle swarm optimizer + */ + PARTICLE_SWARM_TYPE pst; + + /** + * Bounds for every optimized parameter (p1_lower, p1_upper, p2_lower, p2_upper...) + */ + std::vector<double> domain_bounds; // = nullptr; + + protected: + /** + * + * @param coord + * @param val + * @return + */ + LIB4NEURO_API Particle* determine_optimal_coordinate_and_value(std::vector<double>& coord, + double& val); + + /** + * + * @return + */ + LIB4NEURO_API std::vector<double>* get_centroid_coordinates(); + + /** + * + * @param a + * @param b + * @param n + * @return + */ + LIB4NEURO_API double get_euclidean_distance(std::vector<double>* a, + std::vector<double>* b); + + /** + * + */ + void init_constructor(std::vector<double>* domain_bounds, + double c1, + double c2, + double w, + size_t n_particles, + size_t iter_max); + + public: + + /** + * Creates an instance of the Global Particle Swarm Optimizer + * + * @param domain_bounds Bounds for every optimized parameter (p1_lower, p1_upper, p2_lower, p2_upper...) + * @param c1 Cognitive parameter + * @param c2 Social parameter + * @param w Inertia weight + * @param gamma Threshold value for particle velocity - all particles must posses the same or slower velocity for the algorithm to end + * @param epsilon Radius of the cluster area (Euclidean distance) + * @param delta Amount of particles, which has to be in the cluster for the algorithm to stop (0-1) + * @param n_particles Number of particles in the swarm + * @param iter_max Maximal number of iterations - optimization will stop after that, even if not converged + */ + LIB4NEURO_API explicit ParticleSwarm( + std::vector<double>* domain_bounds, double c1 = 1.711897, double c2 = 1.711897, double w = 0.711897, @@ -190,30 +265,53 @@ public: double delta = 0.7, size_t n_particles = 50, size_t iter_max = 1000 - ); - - /** - * - */ - LIB4NEURO_API ~ParticleSwarm( ); - - - /** - * - * @param gamma - * @param epsilon - * @param delta - */ - LIB4NEURO_API void optimize( ErrorFunction &ef ) override; + ); + + /** + * Creates an instance of the Global Particle Swarm Optimizer + * + * WARNING: This constructor expects the cost function minimum to be 0! + * + * @TODO rewrite WITHOUT PARTICLE_SWARM_TYPE parameter! + * + * @param domain_bounds Bounds for every optimized parameter (p1_lower, p1_upper, p2_lower, p2_upper...) + * @param err_thresh Convergence threshold - error function is given externally + * @param PARTICLE_SWARM_TYPE Method type + * @param c1 Cognitive parameter + * @param c2 Social parameter + * @param w Inertia weight + * @param n_particles Number of particles in the swarm + * @param iter_max Maximal number of iterations - optimization will stop after that, even if not converged + * @param err_thresh Error threshold for the method to converge successfully - depending on the used + * ErrorFunction + */ + LIB4NEURO_API explicit ParticleSwarm( + std::vector<double>* domain_bounds, + double err_thresh, + PARTICLE_SWARM_TYPE, + double c1 = 1.711897, + double c2 = 1.711897, + double w = 0.711897, + size_t n_particles = 50, + size_t iter_max = 1000 + ); - /** - * - * @return - */ - LIB4NEURO_API std::vector<double>* get_parameters( ) override; + /** + * + */ + LIB4NEURO_API ~ParticleSwarm(); + /** + * + * @param gamma + * @param epsilon + * @param delta + */ + LIB4NEURO_API void optimize(lib4neuro::ErrorFunction& ef, + std::ofstream* ofs = nullptr) override; -}; + }; +} #endif //INC_4NEURO_PARTICLESWARM_H diff --git a/src/LearningMethods/RandomSolution.cpp b/src/LearningMethods/RandomSolution.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7d905767d0b13cac14a9b5994d55fad8c80ae205 --- /dev/null +++ b/src/LearningMethods/RandomSolution.cpp @@ -0,0 +1,27 @@ +/** + * DESCRIPTION OF THE FILE + * + * @author Michal KravÄenko + * @date 25.2.19 - + */ + +#include "RandomSolution.h" +#include "../message.h" + +namespace lib4neuro { + + RandomSolution::RandomSolution() { + } + + RandomSolution::~RandomSolution() {} + + void RandomSolution::optimize(lib4neuro::ErrorFunction& ef, + std::ofstream* ofs) { + ef.randomize_parameters(1.0); + + this->optimal_parameters = ef.get_parameters(); + + COUT_INFO("Producing a random solution... error: " << ef.eval(&this->optimal_parameters) << std::endl); + } + +} \ No newline at end of file diff --git a/src/LearningMethods/RandomSolution.h b/src/LearningMethods/RandomSolution.h new file mode 100644 index 0000000000000000000000000000000000000000..8345853f5609b1eb25200fa19977cae2fdde550c --- /dev/null +++ b/src/LearningMethods/RandomSolution.h @@ -0,0 +1,32 @@ +/** + * DESCRIPTION OF THE FILE + * + * @author Michal KravÄenko + * @date 25.2.19 - + */ + +#ifndef LIB4NEURO_RANDOMSOLUTION_H +#define LIB4NEURO_RANDOMSOLUTION_H + +#include "../settings.h" +#include "../constants.h" +#include "LearningMethod.h" + +namespace lib4neuro { + + class RandomSolution : public lib4neuro::LearningMethod { + + protected: + public: + + RandomSolution(); + + ~RandomSolution(); + + void optimize(lib4neuro::ErrorFunction& ef, + std::ofstream* ofs = nullptr) override; + }; + +} + +#endif //LIB4NEURO_RANDOMSOLUTION_H diff --git a/src/NetConnection/ConnectionFunctionConstant.cpp b/src/NetConnection/ConnectionFunctionConstant.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3723e6fcdb13c7a1b80830b9eb3afd56979041e0 --- /dev/null +++ b/src/NetConnection/ConnectionFunctionConstant.cpp @@ -0,0 +1,29 @@ +/** + * DESCRIPTION OF THE FILE + * + * @author Michal KravÄenko + * @date 15.3.19 - + */ + +#include <boost/serialization/export.hpp> + +#include "ConnectionFunctionConstant.h" +#include "ConnectionFunctionConstantSerialization.h" + +BOOST_CLASS_EXPORT_IMPLEMENT(ConnectionFunctionConstant); + +ConnectionFunctionConstant::ConnectionFunctionConstant(double w) { + this->weight = w; +} + +ConnectionFunctionConstant::~ConnectionFunctionConstant() { + +} + +double ConnectionFunctionConstant::eval(std::vector<double>& parameter_space) { + return this->weight; +} + +void ConnectionFunctionConstant::eval_partial_derivative(std::vector<double>& parameter_space, + std::vector<double>& weight_gradient, + double alpha) {} \ No newline at end of file diff --git a/src/NetConnection/ConnectionFunctionConstant.h b/src/NetConnection/ConnectionFunctionConstant.h new file mode 100644 index 0000000000000000000000000000000000000000..886189590342d96b223e43b416f3a582d462d625 --- /dev/null +++ b/src/NetConnection/ConnectionFunctionConstant.h @@ -0,0 +1,38 @@ +/** + * DESCRIPTION OF THE FILE + * + * @author Michal KravÄenko + * @date 15.3.19 - + */ + +#ifndef LIB4NEURO_CONNECTIONFUNCTIONCONSTANT_H +#define LIB4NEURO_CONNECTIONFUNCTIONCONSTANT_H + +#include "../settings.h" +#include "ConnectionFunctionGeneral.h" + +class ConnectionFunctionConstant : public ConnectionFunctionGeneral { +private: + double weight; + +public: + /** + * Struct used to access private properties from + * the serialization function + */ + struct access; + + LIB4NEURO_API ConnectionFunctionConstant(double w = 1); + + LIB4NEURO_API ~ConnectionFunctionConstant(); + + LIB4NEURO_API double eval(std::vector<double>& parameter_space) override; + + LIB4NEURO_API void eval_partial_derivative(std::vector<double>& parameter_space, + std::vector<double>& weight_gradient, + double alpha) override; + +}; + + +#endif //LIB4NEURO_CONNECTIONFUNCTIONCONSTANT_H diff --git a/src/NetConnection/ConnectionFunctionConstantSerialization.h b/src/NetConnection/ConnectionFunctionConstantSerialization.h new file mode 100644 index 0000000000000000000000000000000000000000..457f630d1f1a34ed96347ba07e05f7f2bb90f7ff --- /dev/null +++ b/src/NetConnection/ConnectionFunctionConstantSerialization.h @@ -0,0 +1,55 @@ +#ifndef LIB4NEURO_CONNECTIONFUNCTIONCONSTANTSERIALIZATION_H +#define LIB4NEURO_CONNECTIONFUNCTIONCONSTANTSERIALIZATION_H + +#include <boost/archive/text_oarchive.hpp> +#include <boost/archive/text_iarchive.hpp> +#include <boost/serialization/vector.hpp> +#include <boost/serialization/export.hpp> + +#include "ConnectionFunctionConstant.h" +#include "ConnectionFunctionGeneralSerialization.h" + + +BOOST_CLASS_EXPORT_KEY(ConnectionFunctionConstant); + +struct ConnectionFunctionConstant::access { + template<class Archive> + static void serialize(Archive& ar, + ConnectionFunctionConstant& c, + const unsigned int version) { + ar & boost::serialization::base_object<ConnectionFunctionGeneral>(c); + ar & c.weight; + } +}; + +// TODO what's the following template doing exactly? +template void +ConnectionFunctionConstant::access::serialize<boost::archive::text_oarchive>(boost::archive::text_oarchive&, + ConnectionFunctionConstant&, + const unsigned int); + +namespace boost { + namespace serialization { + + /** + * Serialization function + * @tparam Archive Boost library template + * @param ar Boost parameter - filled automatically during serialization! + * @param n ConnectionFunctionConstant instance + * @param version Boost parameter - filled automatically during serialization! + */ + template<class Archive> + void serialize(Archive& ar, + ConnectionFunctionConstant& c, + const unsigned int version) { + ConnectionFunctionConstant::access::serialize(ar, + c, + version); + } + + + } // namespace serialization +} // namespace boost + + +#endif //LIB4NEURO_CONNECTIONFUNCTIONCONSTANTSERIALIZATION_H diff --git a/src/NetConnection/ConnectionFunctionGeneral.cpp b/src/NetConnection/ConnectionFunctionGeneral.cpp index 9b1b3e645541284985ea7011158d0033ca59a5aa..358348581cb4a69cae31aa98fc849d8f36793eb5 100644 --- a/src/NetConnection/ConnectionFunctionGeneral.cpp +++ b/src/NetConnection/ConnectionFunctionGeneral.cpp @@ -5,11 +5,17 @@ * @date 14.6.18 - */ -#include "ConnectionFunctionGeneral.h" +#include <boost/serialization/export.hpp> + +#include "ConnectionFunctionGeneralSerialization.h" +#include "exceptions.h" + +BOOST_CLASS_EXPORT_IMPLEMENT(ConnectionFunctionGeneral); ConnectionFunctionGeneral::ConnectionFunctionGeneral() {} -ConnectionFunctionGeneral::ConnectionFunctionGeneral(std::vector<size_t > ¶m_indices, std::string &function_string) { +ConnectionFunctionGeneral::ConnectionFunctionGeneral(std::vector<size_t>& param_indices, + std::string& function_string) { this->param_indices = param_indices; } @@ -17,12 +23,15 @@ ConnectionFunctionGeneral::~ConnectionFunctionGeneral() { } -double ConnectionFunctionGeneral::eval( std::vector<double> ¶meter_space ) { +double ConnectionFunctionGeneral::eval(std::vector<double>& parameter_space) { //TODO - + THROW_NOT_IMPLEMENTED_ERROR(); return 0.0; } -void ConnectionFunctionGeneral::eval_partial_derivative(std::vector<double> ¶meter_space, std::vector<double> &weight_gradient, double alpha) { +void ConnectionFunctionGeneral::eval_partial_derivative(std::vector<double>& parameter_space, + std::vector<double>& weight_gradient, + double alpha) { //TODO + THROW_NOT_IMPLEMENTED_ERROR(); } diff --git a/src/NetConnection/ConnectionFunctionGeneral.h b/src/NetConnection/ConnectionFunctionGeneral.h index d19bb30cb20b9719fc12b648249d3291ea19a3ea..a50ace150ce35e92bfb40bc4aa0bc079f70de999 100644 --- a/src/NetConnection/ConnectionFunctionGeneral.h +++ b/src/NetConnection/ConnectionFunctionGeneral.h @@ -10,21 +10,10 @@ #include "../settings.h" -#include <boost/archive/text_oarchive.hpp> -#include <boost/archive/text_iarchive.hpp> -#include <boost/serialization/export.hpp> -#include <boost/serialization/vector.hpp> #include <functional> #include <vector> class ConnectionFunctionGeneral { -private: - friend class boost::serialization::access; - - template <class Archive> - void serialize(Archive & ar, const unsigned int version) { - ar & this->param_indices; - }; protected: @@ -35,6 +24,12 @@ protected: public: + /** + * Struct used to access private properties from + * the serialization function + */ + struct access; + /** * */ @@ -45,27 +40,29 @@ public: * @param param_count * @param f */ - LIB4NEURO_API ConnectionFunctionGeneral(std::vector<size_t> ¶m_indices, std::string &function_string); + LIB4NEURO_API ConnectionFunctionGeneral(std::vector<size_t>& param_indices, + std::string& function_string); /** * */ - LIB4NEURO_API virtual ~ConnectionFunctionGeneral( ); + LIB4NEURO_API virtual ~ConnectionFunctionGeneral(); /** * * @return */ - LIB4NEURO_API virtual double eval( std::vector<double> ¶meter_space ); + LIB4NEURO_API virtual double eval(std::vector<double>& parameter_space); /** * Performs partial derivative of this transfer function according to all parameters. Adds the values multiplied * by alpha to the corresponding gradient vector */ - LIB4NEURO_API virtual void eval_partial_derivative( std::vector<double> ¶meter_space, std::vector<double> &weight_gradient, double alpha ); + LIB4NEURO_API virtual void eval_partial_derivative(std::vector<double>& parameter_space, + std::vector<double>& weight_gradient, + double alpha); }; - #endif //INC_4NEURO_CONNECTIONWEIGHT_H diff --git a/src/NetConnection/ConnectionFunctionGeneralSerialization.h b/src/NetConnection/ConnectionFunctionGeneralSerialization.h new file mode 100644 index 0000000000000000000000000000000000000000..80590153810cf75a8477c504ce0ac187b63b6ced --- /dev/null +++ b/src/NetConnection/ConnectionFunctionGeneralSerialization.h @@ -0,0 +1,52 @@ + +#ifndef LIB4NEURO_CONNECTIONFUNCTIONGENERALSERIALIZATION_H +#define LIB4NEURO_CONNECTIONFUNCTIONGENERALSERIALIZATION_H + +#include <boost/archive/text_oarchive.hpp> +#include <boost/archive/text_iarchive.hpp> +#include <boost/serialization/vector.hpp> +#include <boost/serialization/export.hpp> + +#include "ConnectionFunctionGeneral.h" + +BOOST_CLASS_EXPORT_KEY(ConnectionFunctionGeneral); + +struct ConnectionFunctionGeneral::access { + template<class Archive> + static void serialize(Archive& ar, + ConnectionFunctionGeneral& c, + const unsigned int version) { + ar & c.param_indices; + } +}; + +template void +ConnectionFunctionGeneral::access::serialize<boost::archive::text_oarchive>(boost::archive::text_oarchive&, + ConnectionFunctionGeneral&, + const unsigned int); + + +namespace boost { + namespace serialization { + + /** + * Serialization function + * @tparam Archive Boost library template + * @param ar Boost parameter - filled automatically during serialization! + * @param n ConnectionFunctionGeneral instance + * @param version Boost parameter - filled automatically during serialization! + */ + template<class Archive> + void serialize(Archive& ar, + ConnectionFunctionGeneral& c, + const unsigned int version) { + ConnectionFunctionGeneral::access::serialize(ar, + c, + version); + } + + + } // namespace serialization +} // namespace boost + +#endif //LIB4NEURO_CONNECTIONFUNCTIONGENERALSERIALIZATION_H diff --git a/src/NetConnection/ConnectionFunctionIdentity.cpp b/src/NetConnection/ConnectionFunctionIdentity.cpp index bd535cbf94af0e1b019abc5c5dee4592d9c67c43..1f41d76f905ec47021067a0e7d1a2c190624f502 100644 --- a/src/NetConnection/ConnectionFunctionIdentity.cpp +++ b/src/NetConnection/ConnectionFunctionIdentity.cpp @@ -5,33 +5,39 @@ * @date 14.6.18 - */ +#include <boost/serialization/export.hpp> + #include "ConnectionFunctionIdentity.h" +#include "ConnectionFunctionIdentitySerialization.h" +#include "ConnectionFunctionGeneralSerialization.h" + +BOOST_CLASS_EXPORT_IMPLEMENT(ConnectionFunctionIdentity); -ConnectionFunctionIdentity::ConnectionFunctionIdentity( ) { -// this->type = CONNECTION_TYPE::IDENTITY; +ConnectionFunctionIdentity::ConnectionFunctionIdentity() { this->is_unitary = true; } -ConnectionFunctionIdentity::ConnectionFunctionIdentity( size_t pidx ) { -// this->type = CONNECTION_TYPE::IDENTITY; - this->param_idx = pidx; +ConnectionFunctionIdentity::ConnectionFunctionIdentity(size_t pidx) { + this->param_idx = pidx; this->is_unitary = false; } -double ConnectionFunctionIdentity::eval( std::vector<double> ¶meter_space ) { +double ConnectionFunctionIdentity::eval(std::vector<double>& parameter_space) { - if( this->is_unitary ){ + if (this->is_unitary) { return 1.0; } return parameter_space.at(this->param_idx); } -void ConnectionFunctionIdentity::eval_partial_derivative(std::vector<double> ¶meter_space, std::vector<double> &weight_gradient, double alpha) { +void ConnectionFunctionIdentity::eval_partial_derivative(std::vector<double>& parameter_space, + std::vector<double>& weight_gradient, + double alpha) { - if( this->is_unitary ){ + if (this->is_unitary) { return; } weight_gradient[this->param_idx] += alpha; -} \ No newline at end of file +} diff --git a/src/NetConnection/ConnectionFunctionIdentity.h b/src/NetConnection/ConnectionFunctionIdentity.h index 3478d9a248bbd9660876b09700feb23906c136bb..0358f0fcc73ab11dd6008150c5a452f978be0d6c 100644 --- a/src/NetConnection/ConnectionFunctionIdentity.h +++ b/src/NetConnection/ConnectionFunctionIdentity.h @@ -9,17 +9,12 @@ #define INC_4NEURO_CONNECTIONWEIGHTIDENTITY_H #include "../settings.h" - #include "ConnectionFunctionGeneral.h" -class ConnectionFunctionGeneral; - /** * */ -class ConnectionFunctionIdentity:public ConnectionFunctionGeneral { - friend class boost::serialization::access; - friend class NeuralNetwork; +class ConnectionFunctionIdentity : public ConnectionFunctionGeneral { private: @@ -27,38 +22,35 @@ private: bool is_unitary = false; -protected: - template<class Archive> - void serialize(Archive & ar, const unsigned int version){ - ar & boost::serialization::base_object<ConnectionFunctionGeneral>(*this); - ar & this->param_idx; - ar & this->is_unitary; - }; public: + struct access; + /** * */ - LIB4NEURO_API ConnectionFunctionIdentity( ); + LIB4NEURO_API ConnectionFunctionIdentity(); /** * */ - LIB4NEURO_API ConnectionFunctionIdentity( size_t pidx ); + LIB4NEURO_API ConnectionFunctionIdentity(size_t pidx); /** * * @return */ - LIB4NEURO_API double eval( std::vector<double> ¶meter_space ) override; + LIB4NEURO_API double eval(std::vector<double>& parameter_space) override; /** * * @param weight_gradient * @param alpha */ - LIB4NEURO_API void eval_partial_derivative(std::vector<double> ¶meter_space, std::vector<double> &weight_gradient, double alpha) override; + LIB4NEURO_API void eval_partial_derivative(std::vector<double>& parameter_space, + std::vector<double>& weight_gradient, + double alpha) override; }; diff --git a/src/NetConnection/ConnectionFunctionIdentitySerialization.h b/src/NetConnection/ConnectionFunctionIdentitySerialization.h new file mode 100644 index 0000000000000000000000000000000000000000..beec347f7ad92428b201a4fe724a82e11c2a6ec7 --- /dev/null +++ b/src/NetConnection/ConnectionFunctionIdentitySerialization.h @@ -0,0 +1,53 @@ + +#ifndef LIB4NEURO_CONNECTIONFUNCTIONIDENTITYSERIALIZATION_H +#define LIB4NEURO_CONNECTIONFUNCTIONIDENTITYSERIALIZATION_H + +#include <boost/serialization/base_object.hpp> +#include <boost/archive/text_oarchive.hpp> +#include <boost/archive/text_iarchive.hpp> +#include <boost/serialization/export.hpp> + +#include "ConnectionFunctionIdentity.h" +#include "ConnectionFunctionGeneralSerialization.h" + +BOOST_CLASS_EXPORT_KEY(ConnectionFunctionIdentity); + +struct ConnectionFunctionIdentity::access { + template<class Archive> + static void serialize(Archive& ar, + ConnectionFunctionIdentity& c, + const unsigned int version) { + ar & boost::serialization::base_object<ConnectionFunctionGeneral>(c); + ar & c.is_unitary; + ar & c.param_idx; + } +}; + +template void +ConnectionFunctionIdentity::access::serialize<boost::archive::text_oarchive>(boost::archive::text_oarchive&, + ConnectionFunctionIdentity&, + const unsigned int); + +namespace boost { + namespace serialization { + + /** + * Serialization function + * @tparam Archive Boost library template + * @param ar Boost parameter - filled automatically during serialization! + * @param n ConnectionFunctionIdentity instance + * @param version Boost parameter - filled automatically during serialization! + */ + template<class Archive> + void serialize(Archive& ar, + ConnectionFunctionIdentity& c, + const unsigned int version) { + ConnectionFunctionIdentity::access::serialize(ar, + c, + version); + } + + } // namespace serialization +} // namespace boost + +#endif //LIB4NEURO_CONNECTIONFUNCTIONIDENTITYSERIALIZATION_H diff --git a/src/Network/NeuralNetwork.cpp b/src/Network/NeuralNetwork.cpp index e33c5b424a785756a283e48c5ff0c4d5127ab4fb..2f6bb5c92b7f94f454fcdc1b55e9ca8efebe1ef8 100644 --- a/src/Network/NeuralNetwork.cpp +++ b/src/Network/NeuralNetwork.cpp @@ -5,786 +5,1053 @@ * @date 13.6.18 - */ +#include <iostream> +#include <4neuro.h> +#include <NetConnection/ConnectionFunctionConstant.h> + +#include "message.h" #include "NeuralNetwork.h" +#include "NeuralNetworkSerialization.h" +#include "exceptions.h" -BOOST_CLASS_EXPORT(NeuronBinary); -BOOST_CLASS_EXPORT(NeuronConstant); -BOOST_CLASS_EXPORT(NeuronLinear); -BOOST_CLASS_EXPORT(NeuronLogistic); -BOOST_CLASS_EXPORT(NeuronLogistic_d1); -BOOST_CLASS_EXPORT(NeuronLogistic_d2); -BOOST_CLASS_EXPORT(ConnectionFunctionGeneral); -BOOST_CLASS_EXPORT(ConnectionFunctionIdentity); - -NeuralNetwork::NeuralNetwork() { - this->neurons = new std::vector<Neuron*>(0); - this->neuron_biases = new std::vector<double>(0); - this->neuron_potentials = new std::vector<double>(0); - this->neuron_bias_indices = new std::vector<int>(0); - - this->connection_weights =new std::vector<double>(0); - this->connection_list = new std::vector<ConnectionFunctionGeneral*>(0); - this->inward_adjacency = new std::vector<std::vector<std::pair<size_t, size_t>>*>(0); - this->outward_adjacency = new std::vector<std::vector<std::pair<size_t, size_t>>*>(0); - - this->neuron_layers_feedforward = new std::vector<std::vector<size_t>*>(0); - this->neuron_layers_feedbackward = new std::vector<std::vector<size_t>*>(0); - - this->input_neuron_indices = new std::vector<size_t>(0); - this->output_neuron_indices = new std::vector<size_t>(0); - - this->delete_weights = true; - this->delete_biases = true; - this->layers_analyzed = false; -} -NeuralNetwork::NeuralNetwork(std::string filepath) { - std::ifstream ifs(filepath); - boost::archive::text_iarchive ia(ifs); - ia >> *this; - ifs.close(); -} +namespace lib4neuro { + NeuralNetwork::NeuralNetwork() { -NeuralNetwork::~NeuralNetwork() { - if(this->neurons){ - for( auto n: *(this->neurons) ){ - delete n; - n = nullptr; - } - delete this->neurons; - this->neurons = nullptr; + this->delete_weights = true; + this->delete_biases = true; + this->layers_analyzed = false; } - if(this->neuron_potentials){ - delete this->neuron_potentials; - this->neuron_potentials = nullptr; - } + NeuralNetwork::NeuralNetwork(std::string filepath) { - if(this->neuron_bias_indices){ - delete this->neuron_bias_indices; - this->neuron_bias_indices = nullptr; - } + this->init_from_file( filepath ); - if(this->output_neuron_indices){ - delete this->output_neuron_indices; - this->output_neuron_indices = nullptr; } - if(this->input_neuron_indices){ - delete this->input_neuron_indices; - this->input_neuron_indices = nullptr; - } + NeuralNetwork::~NeuralNetwork() {} - if(this->connection_weights && this->delete_weights){ - delete this->connection_weights; - this->connection_weights = nullptr; - } + NeuralNetwork* NeuralNetwork::get_subnet(::std::vector<size_t>& input_neuron_indices, + ::std::vector<size_t>& output_neuron_indices) { - if(this->neuron_biases && this->delete_biases){ - delete this->neuron_biases; - this->neuron_biases = nullptr; - } + THROW_NOT_IMPLEMENTED_ERROR(); - if(this->connection_list){ + NeuralNetwork* output_net = nullptr; +// TODO rework due to the changed structure of the class + return output_net; + } - if(this->delete_weights){ - for(auto c: *this->connection_list){ - delete c; - c = nullptr; + size_t NeuralNetwork::add_neuron(std::shared_ptr<Neuron> n, + BIAS_TYPE bt, + size_t bias_idx) { + + if (bt == BIAS_TYPE::NO_BIAS) { + this->neuron_bias_indices.push_back(-1); + } else if (bt == BIAS_TYPE::NEXT_BIAS) { + this->neuron_bias_indices.push_back((int) this->neuron_biases.size()); + this->neuron_biases.resize(this->neuron_biases.size() + 1); + } else if (bt == BIAS_TYPE::EXISTING_BIAS) { + if (bias_idx >= this->neuron_biases.size()) { + ::std::cerr << "The supplied bias index is too large!\n" << ::std::endl; } + this->neuron_bias_indices.push_back((int) bias_idx); } - delete this->connection_list; - this->connection_list = nullptr; + + this->outward_adjacency.push_back(std::make_shared<std::vector<std::pair<size_t, size_t>>>(::std::vector<std::pair<size_t, size_t>>(0))); + this->inward_adjacency.push_back(std::make_shared<std::vector<std::pair<size_t, size_t>>>(::std::vector<std::pair<size_t, size_t>>(0))); + + this->neurons.push_back(n); + + this->layers_analyzed = false; + return this->neurons.size() - 1; } - if(this->inward_adjacency){ - for(auto e: *this->inward_adjacency){ - if(e){ - delete e; - e = nullptr; - } + void NeuralNetwork::eval_single_debug(::std::vector<double>& input, + ::std::vector<double>& output, + std::vector<double>* custom_weights_and_biases) { + if ((this->input_neuron_indices.size() * this->output_neuron_indices.size()) <= 0) { + THROW_INVALID_ARGUMENT_ERROR("Input and output neurons have not been specified!"); } - delete this->inward_adjacency; - this->inward_adjacency = nullptr; - } - if(this->outward_adjacency){ - for(auto e: *this->outward_adjacency){ - if(e){ - delete e; - e = nullptr; + if (this->input_neuron_indices.size() != input.size()) { + THROW_INVALID_ARGUMENT_ERROR("Data input size != Network input size"); + } + + if (this->output_neuron_indices.size() != output.size()) { + THROW_INVALID_ARGUMENT_ERROR("Data output size != Network output size"); + } + + double potential, bias; + int bias_idx; + + this->copy_parameter_space(custom_weights_and_biases); + + this->analyze_layer_structure(); + + /* reset of the output and the neuron potentials */ + ::std::fill(output.begin(), + output.end(), + 0.0); + ::std::fill(this->neuron_potentials.begin(), + this->neuron_potentials.end(), + 0.0); + + /* set the potentials of the input neurons */ + for (size_t i = 0; i < this->input_neuron_indices.size(); ++i) { + this->neuron_potentials.at(this->input_neuron_indices.at(i)) = input[i]; + std::cout << this->neuron_potentials.at(this->input_neuron_indices.at(i)) << ", "; + } + std::cout << std::endl; + + + + /* we iterate through all the feed-forward layers and transfer the signals */ + for (auto layer: this->neuron_layers_feedforward) { + /* we iterate through all neurons in this layer and propagate the signal to the neighboring neurons */ + + for (auto si: *layer) { + bias = 0.0; + bias_idx = this->neuron_bias_indices.at(si); + if (bias_idx >= 0) { + bias = this->neuron_biases.at(bias_idx); + } + potential = this->neurons.at(si)->activate(this->neuron_potentials.at(si), + bias); + std::cout << "Neuron" << si << " (" << this->neuron_potentials.at(si) << " - " << bias << ") -> (" << potential << ")" << std::endl; +// std::cout << " applying bias: " << bias << " to neuron potential: " << this->neuron_potentials.at(si) +// << " -> " << potential << std::endl; + + for (auto c: *this->outward_adjacency.at(si)) { + size_t ti = c.first; + size_t ci = c.second; + + this->neuron_potentials.at(ti) += + this->connection_list.at(ci)->eval(this->connection_weights) * potential; + std::cout << " EDGE(" << si << ", " << ti << ")" << this->connection_list.at(ci)->eval(this->connection_weights) << std::endl; + +// std::cout << " adding input to neuron " << ti << " += " +// << this->connection_list.at(ci)->eval(this->connection_weights) << "*" << potential +// << std::endl; + } } } - delete this->outward_adjacency; - this->outward_adjacency = nullptr; - } - if(this->neuron_layers_feedforward){ - for(auto e: *this->neuron_layers_feedforward){ - delete e; - e = nullptr; + unsigned int i = 0; + for (auto oi: this->output_neuron_indices) { + bias = 0.0; + bias_idx = this->neuron_bias_indices.at(oi); + if (bias_idx >= 0) { + bias = this->neuron_biases.at(bias_idx); + } + output[i] = this->neurons.at(oi)->activate(this->neuron_potentials.at(oi), + bias); +// std::cout << "setting the output[" << i << "] = " << output[i] << "(bias = " << bias << ")" << std::endl; + ++i; } - delete this->neuron_layers_feedforward; - this->neuron_layers_feedforward = nullptr; } - if(this->neuron_layers_feedbackward){ - for(auto e: *this->neuron_layers_feedbackward){ - delete e; - e = nullptr; + + size_t + NeuralNetwork::add_connection_simple(size_t n1_idx, + size_t n2_idx, + SIMPLE_CONNECTION_TYPE sct, + size_t weight_idx) { + + std::shared_ptr<ConnectionFunctionIdentity> con_weight_u1u2; + if (sct == SIMPLE_CONNECTION_TYPE::UNITARY_WEIGHT) { + con_weight_u1u2 = std::make_shared<ConnectionFunctionIdentity>(ConnectionFunctionIdentity()); + } else { + if (sct == SIMPLE_CONNECTION_TYPE::NEXT_WEIGHT) { + weight_idx = this->connection_weights.size(); + this->connection_weights.resize(weight_idx + 1); + } else if (sct == SIMPLE_CONNECTION_TYPE::EXISTING_WEIGHT) { + if (weight_idx >= this->connection_weights.size()) { + ::std::cerr << "The supplied connection weight index is too large!\n" << ::std::endl; + } + } + + con_weight_u1u2 = std::make_shared<ConnectionFunctionIdentity>(ConnectionFunctionIdentity(weight_idx)); } - delete this->neuron_layers_feedbackward; - this->neuron_layers_feedbackward = nullptr; + + size_t conn_idx = this->add_new_connection_to_list(con_weight_u1u2); + + this->add_outward_connection(n1_idx, + n2_idx, + conn_idx); + this->add_inward_connection(n2_idx, + n1_idx, + conn_idx); + + this->layers_analyzed = false; + + return this->connection_list.size() - 1; } -} -NeuralNetwork* NeuralNetwork::get_subnet(std::vector<size_t> &input_neuron_indices, std::vector<size_t> &output_neuron_indices){ - NeuralNetwork *output_net = nullptr; -// TODO rework due to the changed structure of the class -// Neuron * active_neuron, * target_neuron; -// -// size_t n = this->neurons->size(); -// bool *part_of_subnetwork = new bool[n]; -// std::fill(part_of_subnetwork, part_of_subnetwork + n, false); -// -// bool *is_reachable_from_source = new bool[n]; -// bool *is_reachable_from_destination = new bool[n]; -// std::fill(is_reachable_from_source, is_reachable_from_source + n, false); -// std::fill(is_reachable_from_destination, is_reachable_from_destination + n, false); -// -// bool *visited_neurons = new bool[n]; -// std::fill(visited_neurons, visited_neurons + n, false); -// -// size_t active_set_size[2]; -// active_set_size[0] = 0; -// active_set_size[1] = 0; -// size_t * active_neuron_set = new size_t[2 * n]; -// size_t idx1 = 0, idx2 = 1; -// -// /* MAPPING BETWEEN NEURONS AND THEIR INDICES */ -// size_t idx = 0, idx_target; -// for(Neuron *v: *this->neurons){ -// v->set_idx( idx ); -// idx++; -// } -// -// /* INITIAL STATE FOR THE FORWARD PASS */ -// for(size_t i: input_neuron_indices ){ -// -// if( i < 0 || i >= n){ -// //invalid index -// continue; -// } -// active_neuron_set[idx1 * n + active_set_size[idx1]] = i; -// active_set_size[idx1]++; -// -// visited_neurons[i] = true; -// } -// -// /* FORWARD PASS */ -// while(active_set_size[idx1] > 0){ -// -// //we iterate through the active neurons and propagate the signal -// for(int i = 0; i < active_set_size[idx1]; ++i){ -// idx = active_neuron_set[i]; -// -// is_reachable_from_source[ idx ] = true; -// -// active_neuron = this->neurons->at( idx ); -// -// for(Connection* connection: *(active_neuron->get_connections_out())){ -// -// target_neuron = connection->get_neuron_out( ); -// idx_target = target_neuron->get_idx( ); -// -// if( visited_neurons[idx_target] ){ -// //this neuron was already analyzed -// continue; -// } -// -// visited_neurons[idx_target] = true; -// active_neuron_set[active_set_size[idx2] + n * idx2] = idx_target; -// active_set_size[idx2]++; -// } -// } -// idx1 = idx2; -// idx2 = (idx1 + 1) % 2; -// active_set_size[idx2] = 0; -// } -// -// -// /* INITIAL STATE FOR THE FORWARD PASS */ -// active_set_size[0] = active_set_size[1] = 0; -// std::fill(visited_neurons, visited_neurons + n, false); -// -// for(size_t i: output_neuron_indices ){ -// -// if( i < 0 || i >= n){ -// //invalid index -// continue; -// } -// active_neuron_set[idx1 * n + active_set_size[idx1]] = i; -// active_set_size[idx1]++; -// -// visited_neurons[i] = true; -// } -// -// /* BACKWARD PASS */ -// size_t n_new_neurons = 0; -// while(active_set_size[idx1] > 0){ -// -// //we iterate through the active neurons and propagate the signal -// for(int i = 0; i < active_set_size[idx1]; ++i){ -// idx = active_neuron_set[i]; -// -// is_reachable_from_destination[ idx ] = true; -// -// active_neuron = this->neurons->at( idx ); -// -// if(is_reachable_from_source[ idx ]){ -// n_new_neurons++; -// } -// -// for(Connection* connection: *(active_neuron->get_connections_in())){ -// -// target_neuron = connection->get_neuron_in( ); -// idx_target = target_neuron->get_idx( ); -// -// if( visited_neurons[idx_target] ){ -// //this neuron was already analyzed -// continue; -// } -// -// visited_neurons[idx_target] = true; -// active_neuron_set[active_set_size[idx2] + n * idx2] = idx_target; -// active_set_size[idx2]++; -// } -// } -// idx1 = idx2; -// idx2 = (idx1 + 1) % 2; -// active_set_size[idx2] = 0; -// } -// -// /* FOR CONSISTENCY REASONS */ -// for(size_t in: input_neuron_indices){ -// if( !is_reachable_from_destination[in] ){ -// n_new_neurons++; -// } -// is_reachable_from_destination[in] = true; -// } -// /* FOR CONSISTENCY REASONS */ -// for(size_t in: output_neuron_indices){ -// if( !is_reachable_from_source[in] ){ -// n_new_neurons++; -// } -// is_reachable_from_source[in] = true; -// } -// -// /* WE FORM THE SET OF NEURONS IN THE OUTPUT NETWORK */ -// if(n_new_neurons > 0){ -//// printf("Number of new neurons: %d\n", n_new_neurons); -// output_net = new NeuralNetwork(); -// output_net->set_weight_array( this->connection_weights ); -// -// std::vector<size_t > local_inputs(0), local_outputs(0); -// local_inputs.reserve(input_neuron_indices.size()); -// local_outputs.reserve(output_neuron_indices.size()); -// -// std::vector<Neuron*> local_n_arr(0); -// local_n_arr.reserve( n_new_neurons ); -// -// std::vector<Neuron*> local_local_n_arr(0); -// local_local_n_arr.reserve( n_new_neurons ); -// -// int * neuron_local_mapping = new int[ n ]; -// std::fill(neuron_local_mapping, neuron_local_mapping + n, -1); -// idx = 0; -// for(size_t i = 0; i < n; ++i){ -// if(is_reachable_from_source[i] && is_reachable_from_destination[i]){ -// neuron_local_mapping[i] = (int)idx; -// idx++; -// -// Neuron *new_neuron = this->neurons->at(i)->get_copy( ); -// -// output_net->add_neuron( new_neuron ); -// local_local_n_arr.push_back( new_neuron ); -// local_n_arr.push_back( this->neurons->at(i) ); -// } -// } -// for(size_t in: input_neuron_indices){ -// local_inputs.push_back(neuron_local_mapping[in]); -// } -// for(size_t in: output_neuron_indices){ -// local_outputs.push_back(neuron_local_mapping[in]); -// } -// -//// printf("%d\n", local_n_arr.size()); -//// printf("inputs: %d, outputs: %d\n", local_inputs.size(), local_outputs.size()); -// int local_idx_1, local_idx_2; -// for(Neuron* source_neuron: local_n_arr){ -// //we also add the relevant edges -// local_idx_1 = neuron_local_mapping[source_neuron->get_idx()]; -// -// for(Connection* connection: *(source_neuron->get_connections_out( ))){ -// target_neuron = connection->get_neuron_out(); -// -// local_idx_2 = neuron_local_mapping[target_neuron->get_idx()]; -// if(local_idx_2 >= 0){ -// //this edge is part of the subnetwork -// Connection* new_connection = connection->get_copy( local_local_n_arr[local_idx_1], local_local_n_arr[local_idx_2] ); -// -// local_local_n_arr[local_idx_1]->add_connection_out(new_connection); -// local_local_n_arr[local_idx_2]->add_connection_in(new_connection); -// -//// printf("adding a connection between neurons %d, %d\n", local_idx_1, local_idx_2); -// } -// -// } -// -// } -// output_net->specify_input_neurons(local_inputs); -// output_net->specify_output_neurons(local_outputs); -// -// -// delete [] neuron_local_mapping; -// } -// -// delete [] is_reachable_from_source; -// delete [] is_reachable_from_destination; -// delete [] part_of_subnetwork; -// delete [] visited_neurons; -// delete [] active_neuron_set; -// -// - return output_net; -} + size_t + NeuralNetwork::add_connection_constant(size_t n1_idx, + size_t n2_idx, + double weight) { + std::shared_ptr<ConnectionFunctionConstant> cfc = std::make_shared<ConnectionFunctionConstant>(ConnectionFunctionConstant(weight)); -size_t NeuralNetwork::add_neuron(Neuron* n, BIAS_TYPE bt, size_t bias_idx) { + size_t conn_idx = this->add_new_connection_to_list(cfc); - if( bt == BIAS_TYPE::NO_BIAS ){ - this->neuron_bias_indices->push_back(-1); + this->add_outward_connection(n1_idx, + n2_idx, + conn_idx); + this->add_inward_connection(n2_idx, + n1_idx, + conn_idx); + + this->layers_analyzed = false; + + return conn_idx; } - else if( bt == BIAS_TYPE::NEXT_BIAS ){ - this->neuron_bias_indices->push_back((int)this->neuron_biases->size()); - this->neuron_biases->resize(this->neuron_biases->size() + 1); + + void NeuralNetwork::add_existing_connection(size_t n1_idx, + size_t n2_idx, + size_t connection_idx, + NeuralNetwork& parent_network) { + + size_t conn_idx = this->add_new_connection_to_list(parent_network.connection_list.at(connection_idx)); + + this->add_outward_connection(n1_idx, + n2_idx, + conn_idx); + this->add_inward_connection(n2_idx, + n1_idx, + conn_idx); + + this->layers_analyzed = false; } - else if( bt == BIAS_TYPE::EXISTING_BIAS ){ - if( bias_idx >= this->neuron_biases->size()){ - std::cerr << "The supplied bias index is too large!\n" << std::endl; + + void NeuralNetwork::copy_parameter_space(std::vector<double>* parameters) { + if (parameters != nullptr) { + for (unsigned int i = 0; i < this->connection_weights.size(); ++i) { + this->connection_weights.at(i) = (*parameters).at(i); + } + + for (unsigned int i = 0; i < this->neuron_biases.size(); ++i) { + (this->neuron_biases).at(i) = (*parameters).at(i + this->connection_weights.size()); + } } - this->neuron_bias_indices->push_back((int)bias_idx); } - this->outward_adjacency->push_back(new std::vector<std::pair<size_t, size_t>>(0)); - this->inward_adjacency->push_back(new std::vector<std::pair<size_t, size_t>>(0)); + void NeuralNetwork::set_parameter_space_pointers(NeuralNetwork& parent_network) { - this->neurons->push_back(n); + if (!this->connection_weights.empty()) { + this->connection_weights.clear(); + } - this->layers_analyzed = false; - return this->neurons->size() - 1; -} + this->neuron_biases.clear(); -size_t NeuralNetwork::add_connection_simple( size_t n1_idx, size_t n2_idx, SIMPLE_CONNECTION_TYPE sct, size_t weight_idx ) { + this->connection_weights = parent_network.connection_weights; + this->neuron_biases = parent_network.neuron_biases; - ConnectionFunctionIdentity *con_weight_u1u2; - if( sct == SIMPLE_CONNECTION_TYPE::UNITARY_WEIGHT ){ - con_weight_u1u2 = new ConnectionFunctionIdentity( ); + this->delete_biases = false; + this->delete_weights = false; } - else{ - if( sct == SIMPLE_CONNECTION_TYPE::NEXT_WEIGHT ){ - weight_idx = this->connection_weights->size(); - this->connection_weights->resize(weight_idx + 1); + + void NeuralNetwork::eval_single(::std::vector<double>& input, + ::std::vector<double>& output, + std::vector<double>* custom_weights_and_biases) { + + if ((this->input_neuron_indices.size() * this->output_neuron_indices.size()) <= 0) { + THROW_INVALID_ARGUMENT_ERROR("Input and output neurons have not been specified!"); } - else if( sct == SIMPLE_CONNECTION_TYPE::EXISTING_WEIGHT ){ - if( weight_idx >= this->connection_weights->size()){ - std::cerr << "The supplied connection weight index is too large!\n" << std::endl; - } + + if (this->input_neuron_indices.size() != input.size()) { + THROW_INVALID_ARGUMENT_ERROR("Data input size != Network input size"); } - con_weight_u1u2 = new ConnectionFunctionIdentity( weight_idx ); - } + if (this->output_neuron_indices.size() != output.size()) { + THROW_INVALID_ARGUMENT_ERROR("Data output size != Network output size"); + } - size_t conn_idx = this->add_new_connection_to_list(con_weight_u1u2); + double potential, bias; + int bias_idx; - this->add_outward_connection(n1_idx, n2_idx, conn_idx); - this->add_inward_connection(n2_idx, n1_idx, conn_idx); + this->copy_parameter_space(custom_weights_and_biases); - this->layers_analyzed = false; + this->analyze_layer_structure(); - return this->connection_list->size() - 1; -} + /* reset of the output and the neuron potentials */ + ::std::fill(output.begin(), + output.end(), + 0.0); + ::std::fill(this->neuron_potentials.begin(), + this->neuron_potentials.end(), + 0.0); -void NeuralNetwork::add_existing_connection(size_t n1_idx, size_t n2_idx, size_t connection_idx, - NeuralNetwork &parent_network) { + /* set the potentials of the input neurons */ + for (size_t i = 0; i < this->input_neuron_indices.size(); ++i) { + this->neuron_potentials.at(this->input_neuron_indices.at(i)) = input[i]; + } - size_t conn_idx = this->add_new_connection_to_list(parent_network.connection_list->at( connection_idx )); + /* we iterate through all the feed-forward layers and transfer the signals */ + for (auto layer: this->neuron_layers_feedforward) { + /* we iterate through all neurons in this layer and propagate the signal to the neighboring neurons */ - this->add_outward_connection(n1_idx, n2_idx, conn_idx); - this->add_inward_connection(n2_idx, n1_idx, conn_idx); + for (auto si: *layer) { + bias = 0.0; + bias_idx = this->neuron_bias_indices.at(si); + if (bias_idx >= 0) { + bias = this->neuron_biases.at(bias_idx); + } + potential = this->neurons.at(si)->activate(this->neuron_potentials.at(si), + bias); - this->layers_analyzed = false; -} + for (auto c: *this->outward_adjacency.at(si)) { + size_t ti = c.first; + size_t ci = c.second; -void NeuralNetwork::copy_parameter_space(std::vector<double> *parameters) { - if(parameters != nullptr){ - for(unsigned int i = 0; i < this->connection_weights->size(); ++i){ - (*this->connection_weights)[i] = (*parameters)[i]; + this->neuron_potentials.at(ti) += + this->connection_list.at(ci)->eval(this->connection_weights) * potential; + } + } } - for(unsigned int i = 0; i < this->neuron_biases->size(); ++i){ - (*this->neuron_biases)[i] = (*parameters)[i + this->connection_weights->size()]; + unsigned int i = 0; + for (auto oi: this->output_neuron_indices) { + bias = 0.0; + bias_idx = this->neuron_bias_indices.at(oi); + if (bias_idx >= 0) { + bias = this->neuron_biases.at(bias_idx); + } + output[i] = this->neurons.at(oi)->activate(this->neuron_potentials.at(oi), + bias); + ++i; } } -} - -void NeuralNetwork::set_parameter_space_pointers(NeuralNetwork &parent_network) { - if(this->connection_weights){ - delete connection_weights; - } + void NeuralNetwork::add_to_gradient_single(std::vector<double>& input, + ::std::vector<double>& error_derivative, + double error_scaling, + ::std::vector<double>& gradient) { + + ::std::vector<double> scaling_backprog(this->get_n_neurons()); + ::std::fill(scaling_backprog.begin(), + scaling_backprog.end(), + 0.0); + + size_t bias_shift = this->get_n_weights(); + size_t neuron_idx; + int bias_idx; + double neuron_potential, neuron_potential_t, neuron_bias, connection_weight; + + NeuronDifferentiable* active_neuron; + + /* initial error propagation */ + std::shared_ptr<::std::vector<size_t>> current_layer = this->neuron_layers_feedforward.at( + this->neuron_layers_feedforward.size() - 1); + //TODO might not work in the future as the output neurons could be permuted + for (size_t i = 0; i < current_layer->size(); ++i) { + neuron_idx = current_layer->at(i); + scaling_backprog[neuron_idx] = error_derivative[i] * error_scaling; + } - if(this->neuron_biases){ - delete this->neuron_biases; + /* we iterate through all the layers in reverse order and calculate partial derivatives scaled correspondingly */ + for (size_t j = this->neuron_layers_feedforward.size(); j > 0; --j) { + + current_layer = this->neuron_layers_feedforward.at(j - 1); + + for (size_t i = 0; i < current_layer->size(); ++i) { + + neuron_idx = current_layer->at(i); + active_neuron = dynamic_cast<NeuronDifferentiable*> (this->neurons.at(neuron_idx).get()); + + if (active_neuron) { + bias_idx = this->neuron_bias_indices.at(neuron_idx); + neuron_potential = this->neuron_potentials.at(neuron_idx); + + if (bias_idx >= 0) { + neuron_bias = this->neuron_biases.at(bias_idx); + gradient[bias_shift + bias_idx] += scaling_backprog[neuron_idx] * + active_neuron->activation_function_eval_derivative_bias( + neuron_potential, + neuron_bias); + scaling_backprog[neuron_idx] *= active_neuron->activation_function_eval_derivative( + neuron_potential, + neuron_bias); + } + + /* connections to lower level neurons */ + for (auto c: *this->inward_adjacency.at(neuron_idx)) { + size_t ti = c.first; + size_t ci = c.second; + + neuron_potential_t = this->neurons.at(ti)->get_last_activation_value(); + connection_weight = this->connection_list.at(ci)->eval(this->connection_weights); + + this->connection_list.at(ci)->eval_partial_derivative(*this->get_parameter_ptr_weights(), + gradient, + neuron_potential_t * + scaling_backprog[neuron_idx]); + + scaling_backprog[ti] += scaling_backprog[neuron_idx] * connection_weight; + } + } else { + THROW_INVALID_ARGUMENT_ERROR( + "Neuron used in backpropagation does not contain differentiable activation function!\n"); + } + } + } } - this->connection_weights = parent_network.connection_weights; - this->neuron_biases = parent_network.neuron_biases; + void NeuralNetwork::add_to_gradient_single_debug(std::vector<double>& input, + ::std::vector<double>& error_derivative, + double error_scaling, + ::std::vector<double>& gradient) { + + ::std::vector<double> scaling_backprog(this->get_n_neurons()); + ::std::fill(scaling_backprog.begin(), + scaling_backprog.end(), + 0.0); + + size_t bias_shift = this->get_n_weights(); + size_t neuron_idx; + int bias_idx; + double neuron_potential, neuron_activation_t, neuron_bias, connection_weight; + + NeuronDifferentiable* active_neuron; + + /* initial error propagation */ + std::shared_ptr<::std::vector<size_t>> current_layer = this->neuron_layers_feedforward.at( + this->neuron_layers_feedforward.size() - 1); + //TODO might not work in the future as the output neurons could be permuted + std::cout << "Error scaling on the output layer: "; + for (size_t i = 0; i < current_layer->size(); ++i) { + neuron_idx = current_layer->at(i); + scaling_backprog[neuron_idx] = error_derivative[i] * error_scaling; + + std::cout << scaling_backprog[neuron_idx] << " [neuron " << neuron_idx << "], "; + } + std::cout << std::endl; - this->delete_biases = false; - this->delete_weights = false; -} + /* we iterate through all the layers in reverse order and calculate partial derivatives scaled correspondingly */ + for (size_t j = this->neuron_layers_feedforward.size(); j > 0; --j) { -void NeuralNetwork::eval_single(std::vector<double> &input, std::vector<double> &output, std::vector<double> * custom_weights_and_biases) { - if((this->input_neuron_indices->size() * this->output_neuron_indices->size()) <= 0){ - std::cerr << "Input and output neurons have not been specified\n" << std::endl; - exit(-1); - } + current_layer = this->neuron_layers_feedforward.at(j - 1); + for (size_t i = 0; i < current_layer->size(); ++i) { - if(this->input_neuron_indices->size() != input.size()){ - std::cerr << "Error, input size != Network input size\n" << std::endl; - exit(-1); - } + neuron_idx = current_layer->at(i); + active_neuron = dynamic_cast<NeuronDifferentiable*> (this->neurons.at(neuron_idx).get()); - if(this->output_neuron_indices->size() != output.size()){ - std::cerr << "Error, output size != Network output size\n" << std::endl; - exit(-1); - } - double potential, bias; - int bias_idx; + if (active_neuron) { + std::cout << " [backpropagation] active neuron: " << neuron_idx << std::endl; - this->copy_parameter_space( custom_weights_and_biases ); + bias_idx = this->neuron_bias_indices.at(neuron_idx); + neuron_potential = this->neuron_potentials.at(neuron_idx); - this->analyze_layer_structure(); + if (bias_idx >= 0) { + neuron_bias = this->neuron_biases.at(bias_idx); + gradient[bias_shift + bias_idx] += scaling_backprog[neuron_idx] * + active_neuron->activation_function_eval_derivative_bias( + neuron_potential, + neuron_bias); + scaling_backprog[neuron_idx] *= active_neuron->activation_function_eval_derivative( + neuron_potential, + neuron_bias); + } - /* reset of the output and the neuron potentials */ - std::fill(output.begin(), output.end(), 0.0); - std::fill(this->neuron_potentials->begin(), this->neuron_potentials->end(), 0.0); + std::cout << " [backpropagation] scaling coefficient: " << scaling_backprog[neuron_idx] + << std::endl; - /* set the potentials of the input neurons */ - for(size_t i = 0; i < this->input_neuron_indices->size(); ++i){ - this->neuron_potentials->at( this->input_neuron_indices->at(i) ) = input[ i ]; - } + /* connections to lower level neurons */ + for (auto c: *this->inward_adjacency.at(neuron_idx)) { + size_t ti = c.first; + size_t ci = c.second; - /* we iterate through all the feed-forward layers and transfer the signals */ - for( auto layer: *this->neuron_layers_feedforward){ - /* we iterate through all neurons in this layer and propagate the signal to the neighboring neurons */ + neuron_activation_t = this->neurons.at(ti)->get_last_activation_value(); + connection_weight = this->connection_list.at(ci)->eval(this->connection_weights); - for( auto si: *layer ){ - bias = 0.0; - bias_idx = this->neuron_bias_indices->at( si ); - if( bias_idx >= 0 ){ - bias = this->neuron_biases->at( bias_idx ); - } - potential = this->neurons->at(si)->activate(this->neuron_potentials->at( si ), bias); + std::cout << " [backpropagation] value (" << ti << "): " << neuron_activation_t + << ", scaling: " << scaling_backprog[neuron_idx] << std::endl; - for(auto c: *this->outward_adjacency->at( si )){ - size_t ti = c.first; - size_t ci = c.second; + this->connection_list.at(ci)->eval_partial_derivative(*this->get_parameter_ptr_weights(), + gradient, + neuron_activation_t * + scaling_backprog[neuron_idx]); - this->neuron_potentials->at( ti ) += this->connection_list->at( ci )->eval( *this->connection_weights ) * potential; + scaling_backprog[ti] += scaling_backprog[neuron_idx] * connection_weight; + } + } else { + THROW_INVALID_ARGUMENT_ERROR( + "Neuron used in backpropagation does not contain differentiable activation function!\n"); + } } } } - unsigned int i = 0; - for(auto oi: *this->output_neuron_indices){ - bias = 0.0; - bias_idx = this->neuron_bias_indices->at( oi ); - if( bias_idx >= 0 ){ - bias = this->neuron_biases->at( bias_idx ); - } - output[i] = this->neurons->at( oi )->activate( this->neuron_potentials->at( oi ), bias ); - ++i; - } -} -void NeuralNetwork::randomize_weights( ) { + void NeuralNetwork::randomize_weights() { - boost::random::mt19937 gen; + boost::random::mt19937 gen(std::time(0)); - // Init weight guess ("optimal" for logistic activation functions) - double r = 4 * sqrt(6./(this->connection_weights->size())); + // Init weight guess ("optimal" for logistic activation functions) + double r = 4 * sqrt(6. / (this->connection_weights.size())); - boost::random::uniform_real_distribution<> dist(-r, r); + boost::random::uniform_real_distribution<> dist(-r, + r); - for(size_t i = 0; i < this->connection_weights->size(); i++) { - this->connection_weights->at(i) = dist(gen); + for (size_t i = 0; i < this->connection_weights.size(); i++) { + this->connection_weights.at(i) = dist(gen); + } } -} -void NeuralNetwork::randomize_biases( ) { + void NeuralNetwork::randomize_biases() { - boost::random::mt19937 gen; + boost::random::mt19937 gen(std::time(0)); - // Init weight guess ("optimal" for logistic activation functions) - boost::random::uniform_real_distribution<> dist(-1, 1); - for(size_t i = 0; i < this->neuron_biases->size(); i++) { - this->neuron_biases->at(i) = dist(gen); + // Init weight guess ("optimal" for logistic activation functions) + boost::random::uniform_real_distribution<> dist(-1, + 1); + for (size_t i = 0; i < this->neuron_biases.size(); i++) { + this->neuron_biases.at(i) = dist(gen); + } } -} -size_t NeuralNetwork::get_n_inputs() { - return this->input_neuron_indices->size(); -} + void NeuralNetwork::randomize_parameters() { + this->randomize_biases(); + this->randomize_weights(); + } -size_t NeuralNetwork::get_n_outputs() { - return this->output_neuron_indices->size(); -} + void NeuralNetwork::scale_biases(double alpha) { + for (size_t i = 0; i < this->get_n_biases(); ++i) { + this->neuron_biases.at(i) *= alpha; + } + } -size_t NeuralNetwork::get_n_weights() { - return this->connection_weights->size(); -} + void NeuralNetwork::scale_weights(double alpha) { + for (size_t i = 0; i < this->get_n_weights(); ++i) { + this->connection_weights.at(i) *= alpha; + } + } -size_t NeuralNetwork::get_n_biases() { - return this->neuron_biases->size(); -} + void NeuralNetwork::scale_parameters(double alpha) { + this->scale_biases(alpha); + this->scale_weights(alpha); + } -int NeuralNetwork::get_neuron_bias_index(size_t neuron_idx) { - return this->neuron_bias_indices->at( neuron_idx ); -} + size_t NeuralNetwork::get_n_inputs() { + return this->input_neuron_indices.size(); + } -size_t NeuralNetwork::get_n_neurons() { - return this->neurons->size(); -} + size_t NeuralNetwork::get_n_outputs() { + return this->output_neuron_indices.size(); + } -void NeuralNetwork::specify_input_neurons(std::vector<size_t> &input_neurons_indices) { - if( !this->input_neuron_indices ){ - this->input_neuron_indices = new std::vector<size_t>(input_neurons_indices); + size_t NeuralNetwork::get_n_weights() { + return this->connection_weights.size(); } - else{ - delete this->input_neuron_indices; - this->input_neuron_indices = new std::vector<size_t>(input_neurons_indices); + + size_t NeuralNetwork::get_n_biases() { + return this->neuron_biases.size(); } -} -void NeuralNetwork::specify_output_neurons(std::vector<size_t> &output_neurons_indices) { - if( !this->output_neuron_indices ){ - this->output_neuron_indices = new std::vector<size_t>(output_neurons_indices); + int NeuralNetwork::get_neuron_bias_index(size_t neuron_idx) { + return this->neuron_bias_indices.at(neuron_idx); } - else{ - delete this->output_neuron_indices; - this->output_neuron_indices = new std::vector<size_t>(output_neurons_indices); + + size_t NeuralNetwork::get_n_neurons() { + return this->neurons.size(); } -} -void NeuralNetwork::print_weights() { - printf("Connection weights: "); - if(this->connection_weights){ - for( size_t i = 0; i < this->connection_weights->size() - 1; ++i){ - printf("%f, ", this->connection_weights->at(i)); - } - printf("%f", this->connection_weights->at(this->connection_weights->size() - 1)); + void NeuralNetwork::specify_input_neurons(std::vector<size_t>& input_neurons_indices) { + this->input_neuron_indices = input_neurons_indices; + } - printf("\n"); -} + void NeuralNetwork::specify_output_neurons(std::vector<size_t>& output_neurons_indices) { + this->output_neuron_indices = output_neurons_indices; + } -void NeuralNetwork::print_stats(){ - std::cout << "Number of neurons: " << this->neurons->size() << std::endl - << "Number of connections: " << this->connection_list->size() << std::endl - << "Number of active weights: " << this->connection_weights->size() << std::endl - << "Number of active biases: " << this->neuron_biases->size() << std::endl; -} + void NeuralNetwork::write_weights() { + std::cout << "Connection weights: "; + if (!this->connection_weights.empty()) { + for (size_t i = 0; i < this->connection_weights.size() - 1; ++i) { + std::cout << this->connection_weights.at(i) << ", "; + } + std::cout << this->connection_weights.at(this->connection_weights.size() - 1) << std::endl; + } + } -std::vector<double>* NeuralNetwork::get_parameter_ptr_biases() { - return this->neuron_biases; -} + void NeuralNetwork::write_weights(std::string file_path) { + std::ofstream ofs(file_path); -std::vector<double>* NeuralNetwork::get_parameter_ptr_weights() { - return this->connection_weights; -} + if (!ofs.is_open()) { + THROW_RUNTIME_ERROR("File " + file_path + " can not be opened!"); + } -size_t NeuralNetwork::add_new_connection_to_list(ConnectionFunctionGeneral *con) { - this->connection_list->push_back(con); - return this->connection_list->size() - 1; -} + ofs << "Connection weights: "; -void NeuralNetwork::add_inward_connection(size_t s, size_t t, size_t con_idx) { - if(!this->inward_adjacency->at(s)){ - this->inward_adjacency->at(s) = new std::vector<std::pair<size_t, size_t>>(0); + if (!this->connection_weights.empty()) { + for (size_t i = 0; i < this->connection_weights.size() - 1; ++i) { + ofs << this->connection_weights.at(i) << ", "; + } + ofs << this->connection_weights.at(this->connection_weights.size() - 1) << std::endl; + } } - this->inward_adjacency->at(s)->push_back(std::pair<size_t, size_t>(t, con_idx)); -} -void NeuralNetwork::add_outward_connection(size_t s, size_t t, size_t con_idx) { - if(!this->outward_adjacency->at(s)){ - this->outward_adjacency->at(s) = new std::vector<std::pair<size_t, size_t>>(0); + void NeuralNetwork::write_weights(std::ofstream* file_path) { + *file_path << "Connection weights: "; + + if (!this->connection_weights.empty()) { + for (size_t i = 0; i < this->connection_weights.size() - 1; ++i) { + *file_path << this->connection_weights.at(i) << ", "; + } + *file_path << this->connection_weights.at(this->connection_weights.size() - 1) << std::endl; + } } - this->outward_adjacency->at(s)->push_back(std::pair<size_t, size_t>(t, con_idx)); -} -void NeuralNetwork::analyze_layer_structure() { + void NeuralNetwork::write_biases() { + std::cout << "Network biases: "; - if(this->layers_analyzed){ - //nothing to do - return; + if (!this->neuron_biases.empty()) { + for (unsigned int i = 0; i < this->neuron_biases.size() - 1; i++) { + std::cout << this->neuron_biases.at(i) << ", "; + } + std::cout << this->neuron_biases.at(this->neuron_biases.size() - 1) << std::endl; + } } - /* buffer preparation */ - this->neuron_potentials->resize(this->get_n_neurons()); + void NeuralNetwork::write_biases(std::string file_path) { + std::ofstream ofs(file_path); - /* space allocation */ - if(this->neuron_layers_feedforward){ - for(auto e: *this->neuron_layers_feedforward){ - delete e; - e = nullptr; + if (!ofs.is_open()) { + THROW_RUNTIME_ERROR("File " + file_path + " can not be opened!"); + } + + ofs << "Network biases: "; + + if (!this->neuron_biases.empty()) { + for (unsigned int i = 0; i < this->neuron_biases.size() - 1; i++) { + ofs << this->neuron_biases.at(i) << ", "; + } + ofs << this->neuron_biases.at(this->neuron_biases.size() - 1) << std::endl; } - delete this->neuron_layers_feedforward; - this->neuron_layers_feedforward = nullptr; } - if(this->neuron_layers_feedbackward){ - for(auto e: *this->neuron_layers_feedbackward){ - delete e; - e = nullptr; + void NeuralNetwork::write_biases(std::ofstream* file_path) { + *file_path << "Network biases: "; + + if (!this->neuron_biases.empty()) { + for (unsigned int i = 0; i < this->neuron_biases.size() - 1; i++) { + *file_path << this->neuron_biases.at(i) << ", "; + } + *file_path << this->neuron_biases.at(this->neuron_biases.size() - 1) << std::endl; } - delete this->neuron_layers_feedbackward; - this->neuron_layers_feedbackward = nullptr; } - this->neuron_layers_feedforward = new std::vector<std::vector<size_t>*>(0); - this->neuron_layers_feedbackward = new std::vector<std::vector<size_t>*>(0); + void NeuralNetwork::write_stats() { + ::std::cout << std::flush + << "Number of neurons: " << this->neurons.size() << ::std::endl + << "Number of connections: " << this->connection_list.size() << ::std::endl + << "Number of active weights: " << this->connection_weights.size() << ::std::endl + << "Number of active biases: " << this->neuron_biases.size() << ::std::endl; + + if (this->normalization_strategy) { + ::std::cout << std::flush + << "Normalization strategy maximum value: " + << this->normalization_strategy->get_max_value() << std::endl + << "Normalization strategy minimum value: " + << this->normalization_strategy->get_min_value() + << std::endl; + } + } + void NeuralNetwork::write_stats(std::string file_path) { + std::ofstream ofs(file_path); - auto n = this->neurons->size(); + if (!ofs.is_open()) { + THROW_RUNTIME_ERROR("File " + file_path + " can not be opened!"); + } - /* helpful counters */ - std::vector<size_t> inward_saturation(n); - std::vector<size_t> outward_saturation(n); - std::fill(inward_saturation.begin(), inward_saturation.end(), 0); - std::fill(outward_saturation.begin(), outward_saturation.end(), 0); - for(unsigned int i = 0; i < n; ++i){ - if(this->inward_adjacency->at(i)){ - inward_saturation[i] = this->inward_adjacency->at(i)->size(); + ofs << "Number of neurons: " << this->neurons.size() << ::std::endl + << "Number of connections: " << this->connection_list.size() << ::std::endl + << "Number of active weights: " << this->connection_weights.size() << ::std::endl + << "Number of active biases: " << this->neuron_biases.size() << ::std::endl; + + if (this->normalization_strategy) { + ofs << "Normalization strategy maximum value: " + << this->normalization_strategy->get_max_value() << std::endl + << "Normalization strategy minimum value: " + << this->normalization_strategy->get_min_value() + << std::endl; } - if(this->outward_adjacency->at(i)){ - outward_saturation[i] = this->outward_adjacency->at(i)->size(); + ofs.close(); + } + + void NeuralNetwork::write_stats(std::ofstream* file_path) { + *file_path << "Number of neurons: " << this->neurons.size() << ::std::endl + << "Number of connections: " << this->connection_list.size() << ::std::endl + << "Number of active weights: " << this->connection_weights.size() << ::std::endl + << "Number of active biases: " << this->neuron_biases.size() << ::std::endl; + + if (this->normalization_strategy) { + *file_path << "Normalization strategy maximum value: " + << this->normalization_strategy->get_max_value() << std::endl + << "Normalization strategy minimum value: " + << this->normalization_strategy->get_min_value() + << std::endl; } } + std::vector<double>* NeuralNetwork::get_parameter_ptr_biases() { + return &this->neuron_biases; + } + + std::vector<double>* NeuralNetwork::get_parameter_ptr_weights() { + return &this->connection_weights; + } - std::vector<size_t> active_eval_set(2 * n); - size_t active_set_size[2]; - /* feedforward analysis */ - active_set_size[0] = 0; - active_set_size[1] = 0; + size_t NeuralNetwork::add_new_connection_to_list(std::shared_ptr<ConnectionFunctionGeneral> con) { + this->connection_list.push_back(con); + return this->connection_list.size() - 1; + } - size_t idx1 = 0, idx2 = 1; + void NeuralNetwork::add_inward_connection(size_t s, + size_t t, + size_t con_idx) { + if (!this->inward_adjacency.at(s)) { + this->inward_adjacency.at(s) = std::make_shared<std::vector<std::pair<size_t, size_t>>>(::std::vector<std::pair<size_t, size_t>>(0)); + } + this->inward_adjacency.at(s)->push_back(std::pair<size_t, size_t>(t, + con_idx)); + } - active_set_size[0] = this->get_n_inputs(); - size_t i = 0; - for(i = 0; i < this->get_n_inputs(); ++i){ - active_eval_set[i] = this->input_neuron_indices->at(i); + void NeuralNetwork::add_outward_connection(size_t s, + size_t t, + size_t con_idx) { + if (!this->outward_adjacency.at(s)) { + this->outward_adjacency.at(s) = std::make_shared<std::vector<std::pair<size_t, size_t>>>(::std::vector<std::pair<size_t, size_t>>(0)); + } + this->outward_adjacency.at(s)->push_back(std::pair<size_t, size_t>(t, + con_idx)); } - size_t active_ni; - while(active_set_size[idx1] > 0){ + void NeuralNetwork::analyze_layer_structure() { + + if (this->layers_analyzed) { + //nothing to do + return; + } + + /* buffer preparation */ + this->neuron_potentials.resize(this->get_n_neurons()); - /* we add the current active set as the new outward layer */ - std::vector<size_t> *new_feedforward_layer = new std::vector<size_t>(active_set_size[idx1]); - this->neuron_layers_feedforward->push_back( new_feedforward_layer ); + /* space allocation */ + this->neuron_layers_feedforward.clear(); - //we iterate through the active neurons and propagate the signal - for(i = 0; i < active_set_size[idx1]; ++i){ - active_ni = active_eval_set[i + n * idx1]; - new_feedforward_layer->at( i ) = active_ni; - if(!this->outward_adjacency->at(active_ni)){ - continue; + auto n = this->neurons.size(); + + /* helpful counters */ + ::std::vector<size_t> inward_saturation(n); + ::std::vector<size_t> outward_saturation(n); + ::std::fill(inward_saturation.begin(), + inward_saturation.end(), + 0); + ::std::fill(outward_saturation.begin(), + outward_saturation.end(), + 0); + for (unsigned int i = 0; i < n; ++i) { + if (this->inward_adjacency.at(i)) { + inward_saturation[i] = this->inward_adjacency.at(i)->size(); } - for(auto ni: *(this->outward_adjacency->at(active_ni))){ - inward_saturation[ni.first]--; + if (this->outward_adjacency.at(i)) { + outward_saturation[i] = this->outward_adjacency.at(i)->size(); + } + } + + + ::std::vector<size_t> active_eval_set(2 * n); + size_t active_set_size[2]; + + /* feedforward analysis */ + active_set_size[0] = 0; + active_set_size[1] = 0; + + size_t idx1 = 0, idx2 = 1; + + active_set_size[0] = this->get_n_inputs(); + size_t i = 0; + for (i = 0; i < this->get_n_inputs(); ++i) { + active_eval_set[i] = this->input_neuron_indices.at(i); + } + + size_t active_ni; + while (active_set_size[idx1] > 0) { + + /* we add the current active set as the new outward layer */ + std::shared_ptr<::std::vector<size_t>> new_feedforward_layer = std::make_shared<::std::vector<size_t>>(::std::vector<size_t>(active_set_size[idx1])); + this->neuron_layers_feedforward.push_back(new_feedforward_layer); - if(inward_saturation[ni.first] == 0){ - active_eval_set[active_set_size[idx2] + n * idx2] = ni.first; - active_set_size[idx2]++; + //we iterate through the active neurons and propagate the signal + for (i = 0; i < active_set_size[idx1]; ++i) { + active_ni = active_eval_set[i + n * idx1]; + new_feedforward_layer->at(i) = active_ni; + + if (!this->outward_adjacency.at(active_ni)) { + continue; + } + + for (auto ni: *(this->outward_adjacency.at(active_ni))) { + inward_saturation[ni.first]--; + + if (inward_saturation[ni.first] == 0) { + active_eval_set[active_set_size[idx2] + n * idx2] = ni.first; + active_set_size[idx2]++; + } } } + + idx1 = idx2; + idx2 = (idx1 + 1) % 2; + + active_set_size[idx2] = 0; + } + + + this->layers_analyzed = true; + } + + void NeuralNetwork::init_from_file(const std::string &filepath) { + ::std::ifstream ifs(filepath); + if (ifs.is_open()) { + try { + boost::archive::text_iarchive ia(ifs); + ia >> *this; + } + catch (boost::archive::archive_exception& e) { + THROW_RUNTIME_ERROR( + "Serialized archive error: '" + e.what() + "'! Please, check if your file is really " + "the serialized DataSet."); + } + ifs.close(); + } else { + THROW_RUNTIME_ERROR("File '" + filepath + "' couldn't be open!"); } - idx1 = idx2; - idx2 = (idx1 + 1) % 2; + } - active_set_size[idx2] = 0; + void NeuralNetwork::save_text(std::string filepath) { + ::std::ofstream ofs(filepath); + { + boost::archive::text_oarchive oa(ofs); + oa << *this; + ofs.close(); + } } + NormalizationStrategy* NeuralNetwork::get_normalization_strategy_instance() { + return this->normalization_strategy; + } - /* feed backward analysis */ - active_set_size[0] = 0; - active_set_size[1] = 0; + void NeuralNetwork::set_normalization_strategy_instance(NormalizationStrategy* ns) { + if (!ns) { + THROW_RUNTIME_ERROR("Argument 'ns' is not initialized!"); + } + this->normalization_strategy = ns; + } - idx1 = 0; - idx2 = 1; + FullyConnectedFFN::FullyConnectedFFN(std::vector<unsigned int>* neuron_numbers, + NEURON_TYPE hidden_layer_neuron_type, + std::ofstream* ofs) : NeuralNetwork() { + std::vector<NEURON_TYPE> tmp; - active_set_size[0] = this->get_n_outputs(); - for(i = 0; i < this->get_n_outputs(); ++i){ - active_eval_set[i] = this->output_neuron_indices->at(i); + for (size_t i = 0; i < neuron_numbers->size(); i++) { + tmp.emplace_back(hidden_layer_neuron_type); + } + + this->init(neuron_numbers, + &tmp, + ofs); } - while(active_set_size[idx1] > 0){ + FullyConnectedFFN::FullyConnectedFFN(std::vector<unsigned int>* neuron_numbers, + std::vector<lib4neuro::NEURON_TYPE>* hidden_layer_neuron_types, + std::ofstream* ofs) : NeuralNetwork() { + this->init(neuron_numbers, + hidden_layer_neuron_types, + ofs); + } - /* we add the current active set as the new outward layer */ - std::vector<size_t> *new_feedbackward_layer = new std::vector<size_t>(active_set_size[idx1]); - this->neuron_layers_feedbackward->push_back( new_feedbackward_layer ); + void FullyConnectedFFN::init(std::vector<unsigned int>* neuron_numbers, + std::vector<NEURON_TYPE>* hidden_layer_neuron_types, + std::ofstream* ofs) { + if (neuron_numbers->size() < 2) { + THROW_INVALID_ARGUMENT_ERROR("Parameter 'neuron_numbers' specifying numbers of neurons in network's layers " + "doesn't specify input and output layers, which are compulsory!"); + } - //we iterate through the active neurons and propagate the signal backward - for(i = 0; i < active_set_size[idx1]; ++i){ - active_ni = active_eval_set[i + n * idx1]; - new_feedbackward_layer->at( i ) = active_ni; - if(!this->inward_adjacency->at(active_ni)){ - continue; - } + this->delete_weights = true; + this->delete_biases = true; + this->layers_analyzed = false; + + unsigned int inp_dim = neuron_numbers->at(0); //!< Network input dimension + unsigned int out_dim = neuron_numbers->back(); //!< Network output dimension + + COUT_DEBUG("Fully connected feed-forward network being constructed:" << std::endl); + COUT_DEBUG("# of inputs: " << inp_dim << std::endl); + COUT_DEBUG("# of outputs: " << out_dim << std::endl); + + WRITE_TO_OFS_DEBUG(ofs, + "Fully connected feed-forward network being constructed:" << std::endl + << "# of inputs: " << inp_dim + << std::endl + << "# of outputs: " << out_dim + << std::endl); + + std::vector<size_t> input_layer_neuron_indices; + std::vector<size_t> previous_layer_neuron_indices; + std::vector<size_t> current_layer_neuron_indices; + + /* Creation of INPUT layer neurons */ + current_layer_neuron_indices.reserve(inp_dim); + input_layer_neuron_indices.reserve(inp_dim); + for (unsigned int i = 0; i < inp_dim; i++) { + std::shared_ptr<Neuron> new_neuron; + new_neuron.reset(new NeuronLinear()); + size_t neuron_id = this->add_neuron(new_neuron, + BIAS_TYPE::NO_BIAS); + current_layer_neuron_indices.emplace_back(neuron_id); + } + input_layer_neuron_indices = current_layer_neuron_indices; + + /* Creation of HIDDEN layers */ + for (unsigned int i = 1; i <= neuron_numbers->size() - 2; i++) { + COUT_DEBUG("Hidden layer #" << i << ": " << neuron_numbers->at(i) << " neurons" << std::endl); + WRITE_TO_OFS_DEBUG(ofs, + "Hidden layer #" << i << ": " << neuron_numbers->at(i) << " neurons" << std::endl); + previous_layer_neuron_indices.reserve(neuron_numbers->at(i - 1)); + previous_layer_neuron_indices = current_layer_neuron_indices; + current_layer_neuron_indices.clear(); + current_layer_neuron_indices.reserve(neuron_numbers->at(i)); + + /* Creation of one single hidden layer */ + for (unsigned int j = 0; j < neuron_numbers->at(i); j++) { + size_t neuron_id; + + /* Create new hidden neuron */ + switch (hidden_layer_neuron_types->at(i - 1)) { + case NEURON_TYPE::BINARY: { + std::shared_ptr<Neuron> new_neuron; + new_neuron.reset(new NeuronBinary()); + neuron_id = this->add_neuron(new_neuron, + BIAS_TYPE::NEXT_BIAS); + COUT_DEBUG("Added BINARY neuron." << std::endl); + WRITE_TO_OFS_DEBUG(ofs, + "Added BINARY neuron." << std::endl); + break; + } + + case NEURON_TYPE::CONSTANT: { + THROW_INVALID_ARGUMENT_ERROR("Constant neurons can't be used in fully connected feed-forward networks!"); + break; + } + + case NEURON_TYPE::LINEAR: { + std::shared_ptr<Neuron> new_neuron; + new_neuron.reset(new NeuronLinear()); + neuron_id = this->add_neuron(new_neuron, + BIAS_TYPE::NEXT_BIAS); + COUT_DEBUG("Added LINEAR neuron." << std::endl); + WRITE_TO_OFS_DEBUG(ofs, + "Added LINEAR neuron." << std::endl); + break; + } + + case NEURON_TYPE::LOGISTIC: { + std::shared_ptr<Neuron> new_neuron; + new_neuron.reset(new NeuronLogistic()); + neuron_id = this->add_neuron(new_neuron, + BIAS_TYPE::NEXT_BIAS); + COUT_DEBUG("Added LOGISTIC neuron." << std::endl); + WRITE_TO_OFS_DEBUG(ofs, + "Added LINEAR neuron." << std::endl); + break; + } + } - for(auto ni: *(this->inward_adjacency->at(active_ni))){ - outward_saturation[ni.first]--; + current_layer_neuron_indices.emplace_back(neuron_id); - if(outward_saturation[ni.first] == 0){ - active_eval_set[active_set_size[idx2] + n * idx2] = ni.first; - active_set_size[idx2]++; + /* Connect new neuron with all neurons from the previous layer */ + for (auto ind : previous_layer_neuron_indices) { + this->add_connection_simple(ind, + neuron_id, + l4n::SIMPLE_CONNECTION_TYPE::NEXT_WEIGHT); } } } - idx1 = idx2; - idx2 = (idx1 + 1) % 2; + previous_layer_neuron_indices.reserve(neuron_numbers->back() - 1); + previous_layer_neuron_indices = current_layer_neuron_indices; + current_layer_neuron_indices.clear(); + current_layer_neuron_indices.reserve(out_dim); + + /* Creation of OUTPUT layer neurons */ + for (unsigned int i = 0; i < out_dim; i++) { + std::shared_ptr<Neuron> new_neuron; + new_neuron.reset(new NeuronLinear()); + size_t neuron_id = this->add_neuron(new_neuron, + BIAS_TYPE::NO_BIAS); + current_layer_neuron_indices.emplace_back(neuron_id); + + /* Connect new neuron with all neuron from the previous layer */ + for (auto ind : previous_layer_neuron_indices) { + this->add_connection_simple(ind, + neuron_id, + l4n::SIMPLE_CONNECTION_TYPE::NEXT_WEIGHT); + } + } + + /* Init variables containing indices of INPUT nad OUTPUT neurons */ - active_set_size[idx2] = 0; + this->input_neuron_indices = input_layer_neuron_indices; + this->output_neuron_indices = current_layer_neuron_indices; + + this->analyze_layer_structure(); } - this->layers_analyzed = true; -} + void NeuralNetwork::get_jacobian(std::vector<std::vector<double>>& jacobian, + std::pair<std::vector<double>, std::vector<double>>& data, + std::vector<double>& error) { -void NeuralNetwork::save_text(std::string filepath) { - std::ofstream ofs(filepath); - { - boost::archive::text_oarchive oa(ofs); - oa << *this; - ofs.close(); + std::vector<double> fv(this->get_n_outputs()); + + jacobian.resize(this->get_n_outputs()); + error.resize(this->get_n_outputs()); + for (size_t i = 0; i < this->get_n_outputs(); ++i) { + jacobian[i].resize(this->get_n_weights() + this->get_n_biases()); + std::fill(jacobian[i].begin(), + jacobian[i].end(), + 0); + } + + this->eval_single(data.first, + fv); + + std::vector<double> error_partial(this->get_n_outputs()); + std::fill(error_partial.begin(), + error_partial.end(), + 0.0); + + for (size_t i = 0; i < this->get_n_outputs(); ++i) { + error_partial[i] = 1; + this->add_to_gradient_single(data.first, + error_partial, + 1.0, + jacobian[i]); + error[i] = data.second[i] - fv[i]; + error_partial[i] = 0; + } } + } + diff --git a/src/Network/NeuralNetwork.h b/src/Network/NeuralNetwork.h index 7381ccd9dc2119d90d01fc23434e9873ec04c4f3..b3c7537b62b15181631cad40c300fc7dd1908c36 100644 --- a/src/Network/NeuralNetwork.h +++ b/src/Network/NeuralNetwork.h @@ -11,27 +11,15 @@ #ifndef INC_4NEURO_NEURALNETWORK_H #define INC_4NEURO_NEURALNETWORK_H -#include "../settings.h" - #include <iostream> #include <vector> + #include <algorithm> #include <utility> #include <fstream> +#include <memory> -#include <boost/random/mersenne_twister.hpp> -#include <boost/random/uniform_real_distribution.hpp> - -#include <boost/archive/text_oarchive.hpp> -#include <boost/archive/text_iarchive.hpp> -#include <boost/serialization/list.hpp> -#include <boost/serialization/string.hpp> -#include <boost/serialization/version.hpp> -#include <boost/serialization/split_member.hpp> -#include <boost/serialization/export.hpp> -#include <boost/serialization/vector.hpp> -#include <boost/serialization/utility.hpp> - +#include "../settings.h" #include "../Neuron/Neuron.h" #include "../Neuron/NeuronConstant.h" #include "../Neuron/NeuronBinary.h" @@ -39,304 +27,494 @@ #include "../Neuron/NeuronLogistic.h" #include "../NetConnection/ConnectionFunctionGeneral.h" #include "../NetConnection/ConnectionFunctionIdentity.h" +#include "../NormalizationStrategy/NormalizationStrategy.h" - - -enum class BIAS_TYPE{NEXT_BIAS, NO_BIAS, EXISTING_BIAS}; - -enum class SIMPLE_CONNECTION_TYPE{NEXT_WEIGHT, UNITARY_WEIGHT, EXISTING_WEIGHT}; - - -/** - * - */ -class NeuralNetwork { -private: - friend class boost::serialization::access; - - /** - * - */ - std::vector<Neuron*> *neurons = nullptr; - - /** - * - */ - std::vector<size_t>* input_neuron_indices = nullptr; - - /** - * - */ - std::vector<size_t>* output_neuron_indices = nullptr; - - /** - * - */ - std::vector<double>* connection_weights = nullptr; - - /** - * - */ - std::vector<double>* neuron_biases = nullptr; - - /** - * - */ - std::vector<int>* neuron_bias_indices = nullptr; - - /** - * - */ - std::vector<double>* neuron_potentials = nullptr; - - /** - * - */ - std::vector<ConnectionFunctionGeneral*> * connection_list = nullptr; - - /** - * - */ - std::vector<std::vector<std::pair<size_t, size_t>>*> * inward_adjacency = nullptr; - - /** - * - */ - std::vector<std::vector<std::pair<size_t, size_t>>*> * outward_adjacency = nullptr; - - /** - * - */ - std::vector<std::vector<size_t>*> *neuron_layers_feedforward = nullptr; - - /** - * - */ - std::vector<std::vector<size_t>*> *neuron_layers_feedbackward = nullptr; - - /** - * - */ - bool layers_analyzed = false; +namespace lib4neuro { /** * */ - bool delete_weights = true; - - /** - * - */ - bool delete_biases = true; - - /** - * Adds a new connection to the local list of connections - * @param con Connection object to be added - * @return Returns the index of the added connection among all the connections - */ - size_t add_new_connection_to_list(ConnectionFunctionGeneral* con); - - /** - * Adds a new entry (oriented edge s -> t) to the adjacency list of this network - * @param s Index of the source neuron - * @param t Index of the target neuron - * @param con_idx Index of the connection representing the edge - */ - void add_outward_connection(size_t s, size_t t, size_t con_idx); - - /** - * Adds a new entry (oriented edge s <- t) to the adjacency list of this network - * @param s Index of the source neuron - * @param t Index of the target neuron - * @param con_idx Index of the connection representing the edge - */ - void add_inward_connection(size_t s, size_t t, size_t con_idx); - - /** - * Performs one feedforward pass and feedbackward pass during which determines the layers of this neural network - * for simpler use during evaluation and learning - */ - void analyze_layer_structure( ); - - template<class Archive> - void serialize(Archive & ar, const unsigned int version) { - ar & this->neurons; - ar & this->input_neuron_indices; - ar & this->output_neuron_indices; - ar & this->connection_list; - ar & this->neuron_biases; - ar & this-> neuron_bias_indices; - ar & this->neuron_potentials; - ar & this->connection_weights; - ar & this->inward_adjacency; - ar & this->outward_adjacency; - ar & this->neuron_layers_feedforward; - ar & this->neuron_layers_feedbackward; - ar & this->layers_analyzed; - ar & this->delete_weights; - ar & this->delete_biases; + enum class BIAS_TYPE { + NEXT_BIAS, NO_BIAS, EXISTING_BIAS }; -public: - - /** - * - */ - LIB4NEURO_API explicit NeuralNetwork(); - /** * */ - LIB4NEURO_API explicit NeuralNetwork(std::string filepath); - - /** - * - */ - LIB4NEURO_API virtual ~NeuralNetwork(); - - /** - * If possible, returns a neural net with 'input_neuron_indices' neurons as inputs and 'output_neuron_indices' as - * outputs, otherwise returns nullptr. The returned object shares adjustable weights with this network. All - * neurons are coppied (new instances), edges also. Uses a breadth-first search as the underlying algorithm. - * @param input_neuron_indices - * @param output_neuron_indices - * @return - */ - LIB4NEURO_API NeuralNetwork* get_subnet(std::vector<size_t> &input_neuron_indices, std::vector<size_t> &output_neuron_indices); - - /** - * Replaces the values in @{this->connection_weights} and @{this->neuron_biases} by the provided values - * @param parameters - */ - LIB4NEURO_API virtual void copy_parameter_space(std::vector<double> *parameters); - - /** - * Copies the pointers @{this->connection_weights} and @{this->neuron_biases} from the parental network, sets - * flags to not delete the vectors in this object - * @param parent_network - */ - LIB4NEURO_API virtual void set_parameter_space_pointers( NeuralNetwork &parent_network ); - - /** - * - * @param input - * @param output - * @param custom_weights_and_biases - */ - LIB4NEURO_API virtual void eval_single(std::vector<double> &input, std::vector<double> &output, std::vector<double> *custom_weights_and_biases = nullptr); - - /** - * Adds a new neuron to the list of neurons. Also assigns a valid bias value to its activation function - * @param[in] n - * @return - */ - LIB4NEURO_API size_t add_neuron(Neuron* n, BIAS_TYPE bt = BIAS_TYPE::NEXT_BIAS, size_t bias_idx = 0); - - /** - * - * @param n1_idx - * @param n2_idx - * @return - */ - LIB4NEURO_API size_t add_connection_simple(size_t n1_idx, size_t n2_idx, SIMPLE_CONNECTION_TYPE sct = SIMPLE_CONNECTION_TYPE::NEXT_WEIGHT, size_t weight_idx = 0 ); - - /** - * Take the existing connection with index 'connection_idx' in 'parent_network' and adds it to the structure of this - * object - * @param n1_idx - * @param n2_idx - * @param connection_idx - * @param parent_network - */ - LIB4NEURO_API void add_existing_connection(size_t n1_idx, size_t n2_idx, size_t connection_idx, NeuralNetwork &parent_network ); - - - /** - * - */ - LIB4NEURO_API void randomize_weights(); - - /** - * - */ - LIB4NEURO_API void randomize_biases(); - - /** - * - * @return - */ - LIB4NEURO_API virtual size_t get_n_inputs(); - - /** - * - * @return - */ - LIB4NEURO_API virtual size_t get_n_outputs(); - - /** - * - * @return - */ - LIB4NEURO_API virtual size_t get_n_weights(); - - /** - * - * @return - */ - LIB4NEURO_API virtual size_t get_n_biases(); - - /** - * - * @return - */ - LIB4NEURO_API virtual int get_neuron_bias_index( size_t neuron_idx ); - - /** - * - * @return - */ - LIB4NEURO_API virtual size_t get_n_neurons(); - - /** - * - * @param input_neurons_indices - */ - LIB4NEURO_API void specify_input_neurons(std::vector<size_t> &input_neurons_indices); - - /** - * - * @param output_neurons_indices - */ - LIB4NEURO_API void specify_output_neurons(std::vector<size_t> &output_neurons_indices); - - /** - * - */ - LIB4NEURO_API void print_weights(); - - /** - * - */ - LIB4NEURO_API void print_stats(); - - /** - * - * @return - */ - LIB4NEURO_API std::vector<double>* get_parameter_ptr_weights(); + enum class SIMPLE_CONNECTION_TYPE { + NEXT_WEIGHT, UNITARY_WEIGHT, EXISTING_WEIGHT + }; - /** - * - * @return - */ - LIB4NEURO_API std::vector<double>* get_parameter_ptr_biases(); /** * - * @param filepath */ - LIB4NEURO_API void save_text(std::string filepath); - -}; + class NeuralNetwork { + protected: + + /** + * + */ + std::vector<std::shared_ptr<Neuron>> neurons; // = nullptr; + + /** + * + */ + std::vector<size_t> input_neuron_indices; // = nullptr; + + /** + * + */ + std::vector<size_t> output_neuron_indices; // = nullptr; + + /** + * + */ + std::vector<double> connection_weights; //= nullptr; + + /** + * + */ + std::vector<double> neuron_biases; // = nullptr; + + /** + * + */ + std::vector<int> neuron_bias_indices; // = nullptr; + + /** + * + */ + std::vector<double> neuron_potentials; // = nullptr; + + /** + * + */ + std::vector<std::shared_ptr<ConnectionFunctionGeneral>> connection_list; // = nullptr; + + /** + * + */ + std::vector<std::shared_ptr<std::vector<std::pair<size_t, size_t>>>> inward_adjacency; // = nullptr; + + /** + * + */ + std::vector<std::shared_ptr<std::vector<std::pair<size_t, size_t>>>> outward_adjacency; // = nullptr; + + /** + * + */ + std::vector<std::shared_ptr<std::vector<size_t>>> neuron_layers_feedforward; // = nullptr; + + /** + * + */ + std::vector<std::shared_ptr<std::vector<size_t>>> neuron_layers_feedbackward; // = nullptr; + + /** + * + */ + bool layers_analyzed = false; + + /** + * + */ + bool delete_weights = true; + + /** + * + */ + bool delete_biases = true; + + /** + * helpful arrays to be used in derived classes which are not neccessarily part of the lib4neauro library + */ + std::vector<size_t> ___ind1, ___ind2; + std::vector<std::vector<size_t>> ___ind_m1, ___ind_m2; + std::vector<std::vector<double>> ___val_m1; + + /** + * + */ + NormalizationStrategy* normalization_strategy = nullptr; + + /** + * Adds a new connection to the local list of connections + * @param con Connection object to be added + * @return Returns the index of the added connection among all the connections + */ + + size_t add_new_connection_to_list(std::shared_ptr<ConnectionFunctionGeneral> con); + + /** + * Adds a new entry (oriented edge s -> t) to the adjacency list of this network + * @param s Index of the source neuron + * @param t Index of the target neuron + * @param con_idx Index of the connection representing the edge + */ + void add_outward_connection(size_t s, + size_t t, + size_t con_idx); + + /** + * Adds a new entry (oriented edge s <- t) to the adjacency list of this network + * @param s Index of the source neuron + * @param t Index of the target neuron + * @param con_idx Index of the connection representing the edge + */ + void add_inward_connection(size_t s, + size_t t, + size_t con_idx); + + /** + * Performs one feedforward pass and feedbackward pass during which determines the layers of this neural network + * for simpler use during evaluation and learning + */ + void analyze_layer_structure(); + + /** + * for loading purposes outside the scope of this library + */ + + virtual void init_from_file(const std::string &fn); + + public: + + /** + * Runs @data through the network and then computes partial derivatives with respect to each output function and adds them + * to seperate vectors in @jacobian. Also computes the out error and stores in the vector @error + * @param[out] jacobian + * @param[in] data + * @param[out] error + */ + LIB4NEURO_API virtual void + get_jacobian(std::vector<std::vector<double>>& jacobian, + std::pair<std::vector<double>, std::vector<double>>& data, + std::vector<double>& error); + + + + /** + * + * @param input + * @param output + * @param custom_weights_and_biases + */ + LIB4NEURO_API virtual void eval_single_debug(std::vector<double>& input, + std::vector<double>& output, + std::vector<double>* custom_weights_and_biases = nullptr); + + + /** + * + * @param error_derivative + * @param gradient + */ + LIB4NEURO_API virtual void + add_to_gradient_single_debug(std::vector<double>& input, + std::vector<double>& error_derivative, + double error_scaling, + std::vector<double>& gradient); + + /** + * Struct used to access private properties from + * the serialization function + */ + struct access; + + /** + * + */ + LIB4NEURO_API explicit NeuralNetwork(); + + /** + * + */ + LIB4NEURO_API explicit NeuralNetwork(std::string filepath); + + /** + * + */ + LIB4NEURO_API virtual ~NeuralNetwork(); + + /** + * If possible, returns a neural net with 'input_neuron_indices' neurons as inputs and 'output_neuron_indices' as + * outputs, otherwise returns nullptr. The returned object shares adjustable weights with this network. All + * neurons are coppied (new instances), edges also. Uses a breadth-first search as the underlying algorithm. + * @param input_neuron_indices + * @param output_neuron_indices + * @return + */ + LIB4NEURO_API NeuralNetwork* + get_subnet(std::vector<size_t>& input_neuron_indices, + std::vector<size_t>& output_neuron_indices); + + /** + * Replaces the values in @{this->connection_weights} and @{this->neuron_biases} by the provided values + * @param parameters + */ + LIB4NEURO_API virtual void copy_parameter_space(std::vector<double>* parameters); + + /** + * Copies the pointers @{this->connection_weights} and @{this->neuron_biases} from the parental network, sets + * flags to not delete the vectors in this object + * @param parent_network + */ + LIB4NEURO_API virtual void set_parameter_space_pointers(NeuralNetwork& parent_network); + + /** + * + * @param input + * @param output + * @param custom_weights_and_biases + */ + LIB4NEURO_API virtual void eval_single(std::vector<double>& input, + std::vector<double>& output, + std::vector<double>* custom_weights_and_biases = nullptr); + + /** + * + * @param error_derivative + * @param gradient + */ + LIB4NEURO_API virtual void + add_to_gradient_single(std::vector<double>& input, + std::vector<double>& error_derivative, + double error_scaling, + std::vector<double>& gradient); + + /** + * Adds a new neuron to the list of neurons. Also assigns a valid bias value to its activation function + * @param[in] n + * @return + */ + LIB4NEURO_API size_t add_neuron(std::shared_ptr<Neuron> n, + BIAS_TYPE bt = BIAS_TYPE::NEXT_BIAS, + size_t bias_idx = 0); + + /** + * + * @param n1_idx + * @param n2_idx + * @return + */ + LIB4NEURO_API size_t add_connection_simple(size_t n1_idx, + size_t n2_idx, + SIMPLE_CONNECTION_TYPE sct = SIMPLE_CONNECTION_TYPE::NEXT_WEIGHT, + size_t weight_idx = 0); + + /** + * Adds a connection with constant weight value, will not be altered during any learning process + * @param n1_idx + * @param n2_idx + * @param weight + */ + LIB4NEURO_API size_t add_connection_constant(size_t n1_idx, + size_t n2_idx, + double weight); + + /** + * Take the existing connection with index 'connection_idx' in 'parent_network' and adds it to the structure of this + * object + * @param n1_idx + * @param n2_idx + * @param connection_idx + * @param parent_network + */ + LIB4NEURO_API void + add_existing_connection(size_t n1_idx, + size_t n2_idx, + size_t connection_idx, + NeuralNetwork& parent_network); + + /** + * + */ + LIB4NEURO_API virtual void randomize_weights(); + + /** + * + */ + LIB4NEURO_API virtual void randomize_biases(); + + /** + * + */ + LIB4NEURO_API virtual void randomize_parameters(); + + /** + * + */ + LIB4NEURO_API virtual void scale_biases(double alpha); + /** + * + */ + LIB4NEURO_API virtual void scale_weights(double alpha); + /** + * + */ + LIB4NEURO_API virtual void scale_parameters(double alpha); + + /** + * + * @return + */ + LIB4NEURO_API virtual size_t get_n_inputs(); + + /** + * + * @return + */ + LIB4NEURO_API virtual size_t get_n_outputs(); + + /** + * + * @return + */ + LIB4NEURO_API virtual size_t get_n_weights(); + + /** + * + * @return + */ + LIB4NEURO_API virtual size_t get_n_biases(); + + /** + * + * @return + */ + LIB4NEURO_API virtual int get_neuron_bias_index(size_t neuron_idx); + + /** + * + * @return + */ + LIB4NEURO_API virtual size_t get_n_neurons(); + + /** + * + * @param input_neurons_indices + */ + LIB4NEURO_API void specify_input_neurons(std::vector<size_t>& input_neurons_indices); + + /** + * + * @param output_neurons_indices + */ + LIB4NEURO_API void specify_output_neurons(std::vector<size_t>& output_neurons_indices); + + /** + * + */ + LIB4NEURO_API void write_weights(); + + /** + * + */ + LIB4NEURO_API void write_weights(std::string file_path); + + /** + * + * @param file_path + */ + LIB4NEURO_API void write_weights(std::ofstream* file_path); + + /** + * + */ + LIB4NEURO_API void write_biases(); + + /** + * + * @param file_name + */ + LIB4NEURO_API void write_biases(std::string file_path); + + /** + * + * @param file_path + */ + LIB4NEURO_API void write_biases(std::ofstream* file_path); + + /** + * + */ + LIB4NEURO_API void write_stats(); + + /** + * + * @param file_path + */ + LIB4NEURO_API void write_stats(std::string file_path); + + /** + * + * @param file_path + */ + LIB4NEURO_API void write_stats(std::ofstream* file_path); + + /** + * + * @return + */ + LIB4NEURO_API virtual std::vector<double>* get_parameter_ptr_weights(); + + /** + * + * @return + */ + LIB4NEURO_API virtual std::vector<double>* get_parameter_ptr_biases(); + + /** + * + * @param filepath + */ + LIB4NEURO_API void save_text(std::string filepath); + + /** + * + * @return + */ + //TODO WHY IS THIS HERE? + LIB4NEURO_API NormalizationStrategy* get_normalization_strategy_instance(); + + /** + * + * @param ns + */ + //TODO WHY IS THIS HERE? + LIB4NEURO_API void set_normalization_strategy_instance(NormalizationStrategy* ns); + + }; // class NeuralNetwork + + class FullyConnectedFFN : public NeuralNetwork { + public: + + /** + * Constructs a fully connected feed-forward neural network + * @param neuron_numbers Pointer to vector containing number of vectors in every layer (from input to output) + * @param hidden_layer_neuron_type + */ + LIB4NEURO_API explicit FullyConnectedFFN(std::vector<unsigned int>* neuron_numbers, + NEURON_TYPE hidden_layer_neuron_type, + std::ofstream* ofs = nullptr); + + /** + * + * @param neuron_numbers + * @param hidden_layer_neuron_types + */ + LIB4NEURO_API explicit FullyConnectedFFN(std::vector<unsigned int>* neuron_numbers, + std::vector<NEURON_TYPE>* hidden_layer_neuron_types, + std::ofstream* ofs = nullptr); + + + private: + void init(std::vector<unsigned int>* neuron_numbers, + std::vector<NEURON_TYPE>* hidden_layer_neuron_types, + std::ofstream* ofs = nullptr); + }; +} #endif //INC_4NEURO_NEURALNETWORK_H diff --git a/src/Network/NeuralNetworkSerialization.h b/src/Network/NeuralNetworkSerialization.h new file mode 100644 index 0000000000000000000000000000000000000000..bef1a82642b9833cd34ae9c4d820264abfccfb77 --- /dev/null +++ b/src/Network/NeuralNetworkSerialization.h @@ -0,0 +1,79 @@ + +#ifndef LIB4NEURO_NEURALNETWORKSERIALIZATION_H +#define LIB4NEURO_NEURALNETWORKSERIALIZATION_H + +#include <boost/random/mersenne_twister.hpp> +#include <boost/random/uniform_real_distribution.hpp> +#include <boost/archive/text_oarchive.hpp> +#include <boost/archive/text_iarchive.hpp> +#include <boost/serialization/list.hpp> +#include <boost/serialization/string.hpp> +#include <boost/serialization/export.hpp> +#include <boost/serialization/vector.hpp> +#include <boost/serialization/utility.hpp> +#include <boost/serialization/shared_ptr.hpp> + +#include "NeuralNetwork.h" +#include "Neuron/NeuronSerialization.h" +#include "Neuron/NeuronBinarySerialization.h" +#include "Neuron/NeuronConstantSerialization.h" +#include "Neuron/NeuronLinearSerialization.h" +#include "Neuron/NeuronLogisticSerialization.h" +#include "NetConnection/ConnectionFunctionGeneralSerialization.h" +#include "NetConnection/ConnectionFunctionIdentitySerialization.h" + +namespace lib4neuro { + struct NeuralNetwork::access { + template<class Archive> + static void serialize(Archive& ar, + NeuralNetwork& nn, + const unsigned int version) { + ar & nn.neurons; + ar & nn.input_neuron_indices; + ar & nn.output_neuron_indices; + ar & nn.connection_list; + ar & nn.neuron_biases; + ar & nn.neuron_bias_indices; + ar & nn.neuron_potentials; + ar & nn.connection_weights; + ar & nn.inward_adjacency; + ar & nn.outward_adjacency; + ar & nn.neuron_layers_feedforward; + ar & nn.neuron_layers_feedbackward; + ar & nn.layers_analyzed; + ar & nn.delete_weights; + ar & nn.delete_biases; + ar & nn.normalization_strategy; + + ar & nn.___ind1; + ar & nn.___ind2; + ar & nn.___ind_m1; + ar & nn.___ind_m2; + ar & nn.___val_m1; + } + }; +} + +namespace boost { + namespace serialization { + + /** + * Serialization function + * @tparam Archive Boost library template + * @param ar Boost parameter - filled automatically during serialization! + * @param ds NeuralNetwork instance + * @param version Boost parameter - filled automatically during serialization! + */ + template<class Archive> + void serialize(Archive& ar, + lib4neuro::NeuralNetwork& nn, + const unsigned int version) { + lib4neuro::NeuralNetwork::access::serialize(ar, + nn, + version); + } + + } // namespace serialization +} // namespace boost + +#endif //LIB4NEURO_NEURALNETWORKSERIALIZATION_H diff --git a/src/Network/NeuralNetworkSum.cpp b/src/Network/NeuralNetworkSum.cpp index 2b665fe77060b926d534c6e7149c653f8959be12..2ce253c546994924abd8c9592455a48efe9cda2d 100644 --- a/src/Network/NeuralNetworkSum.cpp +++ b/src/Network/NeuralNetworkSum.cpp @@ -5,112 +5,200 @@ * @date 18.7.18 - */ +#include <boost/serialization/export.hpp> + #include "NeuralNetworkSum.h" +#include "NeuralNetworkSumSerialization.h" +#include "General/ExprtkWrapperSerialization.h" + +BOOST_CLASS_EXPORT_IMPLEMENT(lib4neuro::NeuralNetworkSum); -NeuralNetworkSum::NeuralNetworkSum(){ - this->summand = nullptr; - this->summand_coefficient = nullptr; -} +namespace lib4neuro { -NeuralNetworkSum::~NeuralNetworkSum() { - if( this->summand ){ - delete this->summand; - this->summand = nullptr; + NeuralNetworkSum::NeuralNetworkSum() { + this->summand = nullptr; + this->summand_coefficient = nullptr; } - if( this->summand_coefficient ){ - for(auto el: *this->summand_coefficient){ - delete el; + NeuralNetworkSum::~NeuralNetworkSum() { + if (this->summand) { + delete this->summand; + this->summand = nullptr; } + if (this->summand_coefficient) { - delete this->summand_coefficient; - this->summand_coefficient = nullptr; - } -} + for (auto el: *this->summand_coefficient) { + delete el; + } -void NeuralNetworkSum::add_network( NeuralNetwork *net, std::string expression_string ) { - if(!this->summand){ - this->summand = new std::vector<NeuralNetwork*>(0); + delete this->summand_coefficient; + this->summand_coefficient = nullptr; + } } - this->summand->push_back( net ); - if(!this->summand_coefficient){ - this->summand_coefficient = new std::vector<ExprtkWrapper*>(0); + void NeuralNetworkSum::add_network(NeuralNetwork* net, + std::string expression_string) { + if (!this->summand) { + this->summand = new std::vector<NeuralNetwork*>(0); + } + this->summand->push_back(net); + + if (!this->summand_coefficient) { + this->summand_coefficient = new std::vector<ExprtkWrapper*>(0); + } + this->summand_coefficient->push_back(new ExprtkWrapper(expression_string)); } - this->summand_coefficient->push_back( new ExprtkWrapper( expression_string ) ); -} -void NeuralNetworkSum::eval_single(std::vector<double> &input, std::vector<double> &output, std::vector<double> *custom_weights_and_biases) { - std::vector<double> mem_output(output.size()); - std::fill(output.begin(), output.end(), 0.0); + void NeuralNetworkSum::eval_single(std::vector<double>& input, + std::vector<double>& output, + std::vector<double>* custom_weights_and_biases) { + std::vector<double> mem_output(output.size()); + std::fill(output.begin(), + output.end(), + 0.0); + + NeuralNetwork* SUM; - NeuralNetwork *SUM; + for (size_t ni = 0; ni < this->summand->size(); ++ni) { + SUM = this->summand->at(ni); - for(size_t ni = 0; ni < this->summand->size(); ++ni){ - SUM = this->summand->at(ni); + if (SUM) { + this->summand->at(ni)->eval_single(input, + mem_output, + custom_weights_and_biases); - if( SUM ){ - this->summand->at(ni)->eval_single(input, mem_output, custom_weights_and_biases); + double alpha = this->summand_coefficient->at(ni)->eval(input); - double alpha = this->summand_coefficient->at(ni)->eval(input); + for (size_t j = 0; j < output.size(); ++j) { + output[j] += mem_output[j] * alpha; + } + } else { + //TODO assume the result can be a vector of doubles + double alpha = this->summand_coefficient->at(ni)->eval(input); - for(size_t j = 0; j < output.size(); ++j){ - output[j] += mem_output[j] * alpha; + for (size_t j = 0; j < output.size(); ++j) { + output[j] += alpha; + } } } - else{ - //TODO assume the result can be a vector of doubles - double alpha = this->summand_coefficient->at(ni)->eval(input); - for(size_t j = 0; j < output.size(); ++j){ - output[j] += alpha; + } + + void NeuralNetworkSum::add_to_gradient_single(std::vector<double>& input, + std::vector<double>& error_derivative, + double error_scaling, + std::vector<double>& gradient) { + + NeuralNetwork* SUM; + + for (size_t ni = 0; ni < this->summand->size(); ++ni) { + SUM = this->summand->at(ni); + + if (SUM) { + double alpha = this->summand_coefficient->at(ni)->eval(input); + SUM->add_to_gradient_single(input, + error_derivative, + alpha * error_scaling, + gradient); } } } -} + size_t NeuralNetworkSum::get_n_weights() { + //TODO insufficient solution, assumes the networks share weights + if (this->summand) { + return this->summand->at(0)->get_n_weights(); + } -size_t NeuralNetworkSum::get_n_weights(){ - //TODO insufficient solution, assumes the networks share weights - if(this->summand){ - return this->summand->at(0)->get_n_weights(); + return 0; } - return 0; -} + size_t NeuralNetworkSum::get_n_biases() { + //TODO insufficient solution, assumes the networks share weights + if (this->summand) { + return this->summand->at(0)->get_n_biases(); + } -size_t NeuralNetworkSum::get_n_biases(){ - //TODO insufficient solution, assumes the networks share weights - if(this->summand){ - return this->summand->at(0)->get_n_biases(); + return 0; } - return 0; -} + size_t NeuralNetworkSum::get_n_inputs() { + //TODO insufficient solution, assumes the networks share weights + if (this->summand) { + return this->summand->at(0)->get_n_inputs(); + } -size_t NeuralNetworkSum::get_n_inputs() { - //TODO insufficient solution, assumes the networks share weights - if(this->summand){ - return this->summand->at(0)->get_n_inputs(); + return 0; } - return 0; -} + size_t NeuralNetworkSum::get_n_neurons() { + //TODO insufficient solution, assumes the networks share weights + if (this->summand) { + return this->summand->at(0)->get_n_neurons(); + } -size_t NeuralNetworkSum::get_n_neurons() { - //TODO insufficient solution, assumes the networks share weights - if(this->summand){ - return this->summand->at(0)->get_n_neurons(); + return 0; } - return 0; -} + size_t NeuralNetworkSum::get_n_outputs() { + //TODO insufficient solution, assumes the networks share weights + if (this->summand) { + return this->summand->at(0)->get_n_outputs(); + } -size_t NeuralNetworkSum::get_n_outputs() { - //TODO insufficient solution, assumes the networks share weights - if(this->summand){ - return this->summand->at(0)->get_n_outputs(); + return 0; } - return 0; + std::vector<double>* NeuralNetworkSum::get_parameter_ptr_weights() { + if (this->summand) { + return this->summand->at(0)->get_parameter_ptr_weights(); + } + + return nullptr; + } + + std::vector<double>* NeuralNetworkSum::get_parameter_ptr_biases() { + if (this->summand) { + return this->summand->at(0)->get_parameter_ptr_biases(); + } + + return nullptr; + } + + void NeuralNetworkSum::eval_single_debug(std::vector<double>& input, + std::vector<double>& output, + std::vector<double>* custom_weights_and_biases) { + std::vector<double> mem_output(output.size()); + std::fill(output.begin(), + output.end(), + 0.0); + + NeuralNetwork* SUM; + + for (size_t ni = 0; ni < this->summand->size(); ++ni) { + SUM = this->summand->at(ni); + + if (SUM) { + this->summand->at(ni)->eval_single_debug(input, + mem_output, + custom_weights_and_biases); + + double alpha = this->summand_coefficient->at(ni)->eval(input); + + for (size_t j = 0; j < output.size(); ++j) { + output[j] += mem_output[j] * alpha; + } + } else { + //TODO assume the result can be a vector of doubles + double alpha = this->summand_coefficient->at(ni)->eval(input); + + for (size_t j = 0; j < output.size(); ++j) { + output[j] += alpha; + } + } + } + + } + + } \ No newline at end of file diff --git a/src/Network/NeuralNetworkSum.h b/src/Network/NeuralNetworkSum.h index 3ce90b748235c33e1893076752d341745e923fcb..0c9e55d7ce70ffcd5d7e8fe1ee7b66ca5cdbb687 100644 --- a/src/Network/NeuralNetworkSum.h +++ b/src/Network/NeuralNetworkSum.h @@ -9,63 +9,108 @@ #define INC_4NEURO_NEURALNETWORKSUM_H #include "../settings.h" - -#include "NeuralNetwork.h" - #include "../General/ExprtkWrapper.h" -class NeuralNetworkSum : public NeuralNetwork { -private: - friend class boost::serialization::access; - - std::vector<NeuralNetwork*> * summand; - std::vector<ExprtkWrapper*> * summand_coefficient; +#include "NeuralNetwork.h" - template <class Archive> - void serialize(Archive & ar, const unsigned int version) { - ar & boost::serialization::base_object<NeuralNetwork>(*this); - ar & this->summand; - ar & this->summand_coefficient; +namespace lib4neuro { + + class NeuralNetworkSum : public NeuralNetwork { + private: + std::vector<NeuralNetwork*>* summand; + std::vector<ExprtkWrapper*>* summand_coefficient; + + + public: + /** + * Struct used to access private properties from + * the serialization function + */ + struct access; + + LIB4NEURO_API NeuralNetworkSum(); + + LIB4NEURO_API virtual ~NeuralNetworkSum(); + + LIB4NEURO_API void add_network(NeuralNetwork* net, + std::string expression_string); + + /** + * + * @param input + * @param output + * @param custom_weights_and_biases + */ + LIB4NEURO_API void eval_single(std::vector<double>& input, + std::vector<double>& output, + std::vector<double>* custom_weights_and_biases = nullptr) override; + + /** + * + * @param input + * @param output + * @param custom_weights_and_biases + */ + LIB4NEURO_API void eval_single_debug(std::vector<double>& input, + std::vector<double>& output, + std::vector<double>* custom_weights_and_biases = nullptr) override; + + + /** + * + * @param error_derivative + * @param gradient + */ + LIB4NEURO_API void + add_to_gradient_single(std::vector<double>& input, + std::vector<double>& error_derivative, + double error_scaling, + std::vector<double>& gradient) override; + + /** + * + * @return + */ + LIB4NEURO_API size_t get_n_inputs() override; + + /** + * + * @return + */ + LIB4NEURO_API size_t get_n_outputs() override; + + /** + * + * @return + */ + LIB4NEURO_API size_t get_n_weights() override; + + /** + * + * @return + */ + LIB4NEURO_API size_t get_n_biases() override; + + /** + * + * @return + */ + LIB4NEURO_API size_t get_n_neurons() override; + /** + * + * @return + */ + //TODO only works if all the networks share the same parameters + LIB4NEURO_API std::vector<double>* get_parameter_ptr_weights() override; + + /** + * + * @return + */ + //TODO only works if all the networks share the same parameters + LIB4NEURO_API std::vector<double>* get_parameter_ptr_biases() override; }; -public: - LIB4NEURO_API NeuralNetworkSum( ); - LIB4NEURO_API virtual ~NeuralNetworkSum( ); - - LIB4NEURO_API void add_network( NeuralNetwork *net, std::string expression_string ); - - LIB4NEURO_API virtual void eval_single(std::vector<double> &input, std::vector<double> &output, std::vector<double> *custom_weights_and_biases = nullptr); - - /** - * - * @return - */ - LIB4NEURO_API virtual size_t get_n_inputs() override; - - /** - * - * @return - */ - LIB4NEURO_API size_t get_n_outputs() override; - - /** - * - * @return - */ - LIB4NEURO_API virtual size_t get_n_weights() override; - - /** - * - * @return - */ - LIB4NEURO_API virtual size_t get_n_biases() override; - - /** - * - * @return - */ - LIB4NEURO_API virtual size_t get_n_neurons() override; -}; - +} #endif //INC_4NEURO_NEURALNETWORKSUM_H diff --git a/src/Network/NeuralNetworkSumSerialization.h b/src/Network/NeuralNetworkSumSerialization.h new file mode 100644 index 0000000000000000000000000000000000000000..beb6fe9ad44d0c05f48263c64f557c9fcfee9fbd --- /dev/null +++ b/src/Network/NeuralNetworkSumSerialization.h @@ -0,0 +1,50 @@ + +#ifndef LIB4NEURO_NEURALNETWORKSUMSERIALIZATION_H +#define LIB4NEURO_NEURALNETWORKSUMSERIALIZATION_H + +#include <boost/serialization/base_object.hpp> +#include <boost/serialization/export.hpp> +#include <boost/archive/text_oarchive.hpp> +#include <boost/archive/text_iarchive.hpp> + +#include "NeuralNetworkSum.h" +#include "NeuralNetworkSerialization.h" + +BOOST_CLASS_EXPORT_KEY(lib4neuro::NeuralNetworkSum); + +namespace lib4neuro { + struct NeuralNetworkSum::access { + template<class Archive> + static void serialize(Archive& ar, + NeuralNetworkSum& n, + const unsigned int version) { + ar & boost::serialization::base_object<NeuralNetwork>(n); + ar & n.summand; + ar & n.summand_coefficient; + } + }; +} + +namespace boost { + namespace serialization { + + /** + * Serialization function + * @tparam Archive Boost library template + * @param ar Boost parameter - filled automatically during serialization! + * @param n NeuralNetworkSum instance + * @param version Boost parameter - filled automatically during serialization! + */ + template<class Archive> + void serialize(Archive& ar, + lib4neuro::NeuralNetworkSum& n, + const unsigned int version) { + lib4neuro::NeuralNetworkSum::access::serialize(ar, + n, + version); + } + + } // namespace serialization +} // namespace boost + +#endif //LIB4NEURO_NEURALNETWORKSUMSERIALIZATION_H diff --git a/src/Neuron/Neuron.cpp b/src/Neuron/Neuron.cpp index 15664cd953bd38f298d22f194d554b0eff82086c..3c7f3511740e85dfa590d7fc0e86049634b3902f 100644 --- a/src/Neuron/Neuron.cpp +++ b/src/Neuron/Neuron.cpp @@ -1,6 +1,16 @@ +#include "NeuronSerialization.h" -#include "Neuron.h" +BOOST_CLASS_EXPORT_IMPLEMENT(lib4neuro::Neuron); -Neuron::~Neuron() { + +namespace lib4neuro { + + Neuron::~Neuron() { + + } + + double Neuron::get_last_activation_value() { + return this->activation_val; + } } diff --git a/src/Neuron/Neuron.h b/src/Neuron/Neuron.h index 275f1ca7d96def5fc6dce0d569bcccf778a74f73..f3689d671af1074b9735d5dace40c4a06f1481fc 100644 --- a/src/Neuron/Neuron.h +++ b/src/Neuron/Neuron.h @@ -7,41 +7,63 @@ * @date 2017 - 2018 */ //TODO correct docs in this and all child classes - #ifndef NEURON_H_ - #define NEURON_H_ +#ifndef NEURON_H_ +#define NEURON_H_ - #include "../settings.h" - -#include <boost/serialization/base_object.hpp> +#include "../settings.h" #include <vector> -class IDifferentiable; - -/** - * Abstract class representing a general neuron - */ -class Neuron { -private: - friend class boost::serialization::access; - - template<class Archive> - void serialize(Archive & ar, const unsigned int version){}; - -public: +namespace lib4neuro { /** - * Destructor of the Neuron object - * this level deallocates the array 'activation_function_parameters' - * also deallocates the OUTGOING connections + * */ - LIB4NEURO_API virtual ~Neuron(); + enum NEURON_TYPE { + BINARY, + CONSTANT, + LINEAR, + LOGISTIC + }; /** - * Performs the activation function and returns the result - */ - LIB4NEURO_API virtual double activate( double x, double b ) = 0; - -}; /* end of Neuron class */ + * Abstract class representing a general neuron + */ + class Neuron { + + protected: + /** + * holds the last value of the activation function, used by this->activate + */ + double activation_val; + + public: + + /** + * Struct used to access private properties from + * the serialization function + */ + struct access; + + /** + * Destructor of the Neuron object + * this level deallocates the array 'activation_function_parameters' + * also deallocates the OUTGOING connections + */ + LIB4NEURO_API virtual ~Neuron(); + + /** + * Performs the activation function and returns the result + */ + LIB4NEURO_API virtual double activate(double x, + double b) = 0; + + /** + * returns the last value of the actual activation function output for this neuron + * @return + */ + LIB4NEURO_API virtual double get_last_activation_value(); + + }; /* end of Neuron class */ /** @@ -49,28 +71,38 @@ public: * 'activation_function_eval_derivative', 'get_partial_derivative' and * 'get_derivative' methods. */ -class IDifferentiable { - - /** - * Calculates the derivative with respect to the argument, ie the 'potential' - * @return f'(x), where 'f(x)' is the activation function and 'x' = 'potential' - */ - virtual double activation_function_eval_derivative( double x, double b ) = 0; - - /** - * Calculates the derivative with respect to the bias - * @return d/db f'(x), where 'f(x)' is the activation function, 'x' is the 'potential' - * and 'b' is the bias - */ - virtual double activation_function_eval_derivative_bias( double x, double b ) = 0; - - /** - * Returns a Neuron pointer object with activation function being the partial derivative of - * the activation function of this Neuron object with respect to the argument, i.e. 'potential' - * @return - */ - virtual Neuron* get_derivative( ) = 0; - -}; /* end of IDifferentiable class */ - - #endif /* NEURON_H_ */ \ No newline at end of file + class NeuronDifferentiable : public Neuron { + public: + + /** + * Struct used to access private properties from + * the serialization function + */ + struct access; + + /** + * Calculates the derivative with respect to the argument, ie the 'potential' + * @return f'(x), where 'f(x)' is the activation function and 'x' = 'potential' + */ + virtual double activation_function_eval_derivative(double x, + double b) = 0; + + /** + * Calculates the derivative with respect to the bias + * @return d/db f'(x), where 'f(x)' is the activation function, 'x' is the 'potential' + * and 'b' is the bias + */ + virtual double activation_function_eval_derivative_bias(double x, + double b) = 0; + + /** + * Returns a Neuron pointer object with activation function being the partial derivative of + * the activation function of this Neuron object with respect to the argument, i.e. 'potential' + * @return + */ + virtual Neuron* get_derivative() = 0; + }; + +} + +#endif /* NEURON_H_ */ diff --git a/src/Neuron/NeuronBiased.cpp b/src/Neuron/NeuronBiased.cpp new file mode 100644 index 0000000000000000000000000000000000000000..65fd468d79f49196c69887862ddaf69058e19d8b --- /dev/null +++ b/src/Neuron/NeuronBiased.cpp @@ -0,0 +1,37 @@ + +#include <boost/serialization/export.hpp> + +#include "NeuronBiased.h" +#include "NeuronConstant.h" + +#include "NeuronBiasedSerialization.h" + +BOOST_CLASS_EXPORT_IMPLEMENT(lib4neuro::NeuronBiased) + +namespace lib4neuro { + + NeuronBiased::NeuronBiased(double b) { + this->bias = b; + } + + double NeuronBiased::activate(double x, + double b) { + return x + this->bias; + } + + double NeuronBiased::activation_function_eval_derivative(double x, + double b) { + return 1.0; + } + + double NeuronBiased::activation_function_eval_derivative_bias(double x, + double b) { + return 0.0; + } + + Neuron* NeuronBiased::get_derivative() { + NeuronConstant* output = new NeuronConstant(1.0); + return output; + } + +}//end of namespace lib4neuro diff --git a/src/Neuron/NeuronBiased.h b/src/Neuron/NeuronBiased.h new file mode 100644 index 0000000000000000000000000000000000000000..6525f71b148952e463ee3b952a53a3a7a34873e9 --- /dev/null +++ b/src/Neuron/NeuronBiased.h @@ -0,0 +1,66 @@ +/** + * DESCRIPTION OF THE FILE + * + * @author Michal KravÄenko + * @date 15.3.19 - + */ + +#ifndef LIB4NEURO_NEURONBIASED_H +#define LIB4NEURO_NEURONBIASED_H + +#include "Neuron.h" + +namespace lib4neuro { + class NeuronBiased : public NeuronDifferentiable { + + private: + + double bias; + + public: + + /** + * Struct used to access private properties from + * the serialization function + */ + struct access; + + /** + * Constructs the object of the Linear neuron with activation function + * f(x) = x + b + * @param[in] b Bias + */ + LIB4NEURO_API explicit NeuronBiased(double b = 0); + + /** + * Evaluates 'x + this->bias' and stores the result into the 'state' property + */ + LIB4NEURO_API double activate(double x, + double b) override; + + /** + * Calculates the partial derivative of the activation function + * f(x) = x + this->bias at point x + * @return Partial derivative of the activation function according to the + * 'bias' parameter. Returns 0.0 + */ + LIB4NEURO_API double activation_function_eval_derivative_bias(double x, + double b) override; + + /** + * Calculates d/dx of (x + this->bias) at point x + * @return 1.0 + */ + LIB4NEURO_API double activation_function_eval_derivative(double x, + double b) override; + + /** + * Returns a pointer to a Neuron with derivative as its activation function + * @return + */ + LIB4NEURO_API Neuron* get_derivative() override; + }; + +}//end of namespace lib4neuro + +#endif //LIB4NEURO_NEURONBIASED_H diff --git a/src/Neuron/NeuronBiasedSerialization.h b/src/Neuron/NeuronBiasedSerialization.h new file mode 100644 index 0000000000000000000000000000000000000000..291047ff766223bfb759beaa715b5331e6b05b0d --- /dev/null +++ b/src/Neuron/NeuronBiasedSerialization.h @@ -0,0 +1,47 @@ +#ifndef LIB4NEURO_NEURONBIASEDSERIALIZATION_H +#define LIB4NEURO_NEURONBIASEDSERIALIZATION_H + +#include <boost/serialization/base_object.hpp> +#include <boost/archive/text_oarchive.hpp> +#include <boost/archive/text_iarchive.hpp> +#include <boost/serialization/export.hpp> + +#include "NeuronSerialization.h" +#include "NeuronBiased.h" + +BOOST_CLASS_EXPORT_KEY(lib4neuro::NeuronBiased); + +struct lib4neuro::NeuronBiased::access { + template<class Archive> + static void serialize(Archive& ar, + lib4neuro::NeuronBiased& n, + const unsigned int version) { + ar & boost::serialization::base_object<lib4neuro::Neuron>(n); + ar & n.bias; + } +}; + +namespace boost { + namespace serialization { + + /** + * Serialization function + * @tparam Archive Boost library template + * @param ar Boost parameter - filled automatically during serialization! + * @param n NeuronBiased instance + * @param version Boost parameter - filled automatically during serialization! + */ + template<class Archive> + void serialize(Archive& ar, + lib4neuro::NeuronBiased& n, + const unsigned int version) { + lib4neuro::NeuronBiased::access::serialize(ar, + n, + version); + } + + } // namespace serialization +} // namespace boost + + +#endif //LIB4NEURO_NEURONBIASEDSERIALIZATION_H diff --git a/src/Neuron/NeuronBinary.cpp b/src/Neuron/NeuronBinary.cpp index 48ada48fc2b562e90abccad7941a7ceee13db712..390d60f5bcc3684d53c23747cbfead6abffe61cc 100644 --- a/src/Neuron/NeuronBinary.cpp +++ b/src/Neuron/NeuronBinary.cpp @@ -1,17 +1,24 @@ -// -// Created by fluffymoo on 11.6.18. -// -#include "NeuronBinary.h" +#include <boost/serialization/export.hpp> -NeuronBinary::NeuronBinary( ) {} +#include "NeuronSerialization.h" +#include "NeuronBinarySerialization.h" -double NeuronBinary::activate( double x, double b ) { +BOOST_CLASS_EXPORT_IMPLEMENT(lib4neuro::NeuronBinary) - if(x >= b){ - return 1.0; - } - else{ - return 0.0; +namespace lib4neuro { + NeuronBinary::NeuronBinary() {} + + double NeuronBinary::activate(double x, + double b) { + + if (x >= b) { + this->activation_val = 1.0; + } else { + this->activation_val = 0.0; + } + + return this->activation_val; } -} + +} \ No newline at end of file diff --git a/src/Neuron/NeuronBinary.h b/src/Neuron/NeuronBinary.h index 15891181ece3c961084b20413bda4b685da6889e..dac7f6e896d71c2ac9e2cb2e7410e5ea7517a49e 100644 --- a/src/Neuron/NeuronBinary.h +++ b/src/Neuron/NeuronBinary.h @@ -10,36 +10,38 @@ #ifndef INC_4NEURO_NEURONBINARY_H #define INC_4NEURO_NEURONBINARY_H -#include "../settings.h" - #include "Neuron.h" +namespace lib4neuro { + /** * Binary neuron class - uses unit-step as the activation function */ -class NeuronBinary:public Neuron { -private: - friend class boost::serialization::access; + class NeuronBinary : public Neuron { - template<class Archive> - void serialize(Archive & ar, const unsigned int version){ - ar & boost::serialization::base_object<Neuron>(*this); - }; + public: -public: + /** + * Struct used to access private properties from + * the serialization function + */ + struct access; - /** - * Default constructor for the binary Neuron - * @param[in] threshold Denotes when the neuron is activated - * When neuron potential exceeds 'threshold' value it becomes excited - */ - LIB4NEURO_API explicit NeuronBinary( ); + /** + * Default constructor for the binary Neuron + * @param[in] threshold Denotes when the neuron is activated + * When neuron potential exceeds 'threshold' value it becomes excited + */ + LIB4NEURO_API explicit NeuronBinary(); - /** - * Performs the activation function and stores the result into the 'state' property - */ - LIB4NEURO_API double activate( double x, double b ) override; + /** + * Performs the activation function and stores the result into the 'state' property + */ + LIB4NEURO_API double activate(double x, + double b) override; + + }; -}; +} #endif //INC_4NEURO_NEURONBINARY_H diff --git a/src/Neuron/NeuronBinarySerialization.h b/src/Neuron/NeuronBinarySerialization.h new file mode 100644 index 0000000000000000000000000000000000000000..3506f03903ecf774129a66aa15b598032bec8341 --- /dev/null +++ b/src/Neuron/NeuronBinarySerialization.h @@ -0,0 +1,47 @@ + +#ifndef LIB4NEURO_NEURON_BINARY_SERIALIZATION_H +#define LIB4NEURO_NEURON_BINARY_SERIALIZATION_H + +#include <boost/serialization/base_object.hpp> +#include <boost/archive/text_oarchive.hpp> +#include <boost/archive/text_iarchive.hpp> +#include <boost/serialization/export.hpp> + +#include "NeuronSerialization.h" +#include "NeuronBinary.h" + +BOOST_CLASS_EXPORT_KEY(lib4neuro::NeuronBinary); + +struct lib4neuro::NeuronBinary::access { + template<class Archive> + static void serialize(Archive& ar, + lib4neuro::NeuronBinary& n, + const unsigned int version) { + ar & boost::serialization::base_object<lib4neuro::Neuron>(n); + } +}; + +namespace boost { + namespace serialization { + + /** + * Serialization function + * @tparam Archive Boost library template + * @param ar Boost parameter - filled automatically during serialization! + * @param n NeuronBinary instance + * @param version Boost parameter - filled automatically during serialization! + */ + template<class Archive> + void serialize(Archive& ar, + lib4neuro::NeuronBinary& n, + const unsigned int version) { + lib4neuro::NeuronBinary::access::serialize(ar, + n, + version); + } + + } // namespace serialization +} // namespace boost + + +#endif //LIB4NEURO_NEURON_BINARY_SERIALIZATION_H diff --git a/src/Neuron/NeuronConstant.cpp b/src/Neuron/NeuronConstant.cpp index 1b349c2f913dfe1ea3c09dcf5765fe28254cd555..f342d532018f3715f226f097568af16f2fb11c5f 100644 --- a/src/Neuron/NeuronConstant.cpp +++ b/src/Neuron/NeuronConstant.cpp @@ -5,25 +5,38 @@ * @date 8.8.18 - */ -#include "NeuronConstant.h" +#include <boost/serialization/export.hpp> -NeuronConstant::NeuronConstant( double c ) { - this->p = c; -} +#include "NeuronConstantSerialization.h" -double NeuronConstant::activate( double x, double b ) { - return this->p; -} -double NeuronConstant::activation_function_eval_derivative_bias( double x, double b ) { - return 0.0; -} +BOOST_CLASS_EXPORT_IMPLEMENT(lib4neuro::NeuronConstant); -double NeuronConstant::activation_function_eval_derivative( double x, double b ) { - return 0.0; -} +namespace lib4neuro { + + NeuronConstant::NeuronConstant(double c) { + this->p = c; + } + + double NeuronConstant::activate(double x, + double b) { + this->activation_val = this->p; + return this->activation_val; + } + + double NeuronConstant::activation_function_eval_derivative_bias(double x, + double b) { + return 0.0; + } + + double NeuronConstant::activation_function_eval_derivative(double x, + double b) { + return 0.0; + } + + Neuron* NeuronConstant::get_derivative() { + NeuronConstant* output = new NeuronConstant(); + return output; + } -Neuron* NeuronConstant::get_derivative() { - NeuronConstant* output = new NeuronConstant( ); - return output; } \ No newline at end of file diff --git a/src/Neuron/NeuronConstant.h b/src/Neuron/NeuronConstant.h index 215cf2a182c335b90853261b16ae1d5076893490..fa745fa079557ba6eba580731c843282c7965578 100644 --- a/src/Neuron/NeuronConstant.h +++ b/src/Neuron/NeuronConstant.h @@ -8,56 +8,58 @@ #ifndef INC_4NEURO_NEURONCONSTANT_H #define INC_4NEURO_NEURONCONSTANT_H -#include "../settings.h" - #include "Neuron.h" -class NeuronConstant: public Neuron, public IDifferentiable { -private: - friend class boost::serialization::access; +namespace lib4neuro { + + class NeuronConstant : public NeuronDifferentiable { + private: + double p = 0.0; + + public: + + /** + * Struct used to access private properties from + * the serialization function + */ + struct access; + + /** + * Constructs the object of the Linear neuron with activation function + * f(x) = c + * @param[in] c Constant value + */ + LIB4NEURO_API explicit NeuronConstant(double c = 0.0); + + /** + * Evaluates and returns 'c' + */ + LIB4NEURO_API double activate(double x, + double b) override; - double p = 0.0; + /** + * Calculates the partial derivative of the activation function + * f(x) = c at point x + * @return Partial derivative of the activation function according to the + * 'bias' parameter. Returns 0.0 + */ + LIB4NEURO_API double activation_function_eval_derivative_bias(double x, + double b) override; - template<class Archive> - void serialize(Archive & ar, const unsigned int version){ - ar & boost::serialization::base_object<Neuron>(*this); + /** + * Calculates d/dx of (c) at point x + * @return 0.0 + */ + LIB4NEURO_API double activation_function_eval_derivative(double x, + double b) override; - ar & this->p; + /** + * Returns a pointer to a Neuron with derivative as its activation function + * @return + */ + LIB4NEURO_API Neuron* get_derivative() override; }; -public: - - /** - * Constructs the object of the Linear neuron with activation function - * f(x) = c - * @param[in] c Constant value - */ - LIB4NEURO_API explicit NeuronConstant( double c = 0.0 ); - - /** - * Evaluates and returns 'c' - */ - LIB4NEURO_API double activate( double x, double b ) override; - - /** - * Calculates the partial derivative of the activation function - * f(x) = c at point x - * @return Partial derivative of the activation function according to the - * 'bias' parameter. Returns 0.0 - */ - LIB4NEURO_API double activation_function_eval_derivative_bias( double x, double b ) override; - - /** - * Calculates d/dx of (c) at point x - * @return 0.0 - */ - LIB4NEURO_API double activation_function_eval_derivative( double x, double b ) override; - - /** - * Returns a pointer to a Neuron with derivative as its activation function - * @return - */ - LIB4NEURO_API Neuron* get_derivative( ) override; -}; +} #endif //INC_4NEURO_NEURONCONSTANT_H diff --git a/src/Neuron/NeuronConstantSerialization.h b/src/Neuron/NeuronConstantSerialization.h new file mode 100644 index 0000000000000000000000000000000000000000..d9d52bf97431545c4baab3f5ef1d94ec4f0b4047 --- /dev/null +++ b/src/Neuron/NeuronConstantSerialization.h @@ -0,0 +1,51 @@ + +#ifndef LIB4NEURO_NEURON_CONSTANT_SERIALIZATION_H +#define LIB4NEURO_NEURON_CONSTANT_SERIALIZATION_H + +#include <boost/serialization/base_object.hpp> +#include <boost/archive/text_oarchive.hpp> +#include <boost/archive/text_iarchive.hpp> +#include <boost/serialization/export.hpp> + +#include "NeuronConstant.h" +#include "NeuronSerialization.h" + +BOOST_CLASS_EXPORT_KEY(lib4neuro::NeuronConstant); + +namespace lib4neuro { + struct NeuronConstant::access { + template<class Archive> + static void serialize(Archive& ar, + NeuronConstant& n, + const unsigned int version) { + ar & boost::serialization::base_object<Neuron>(n); + ar & n.p; + } + }; +} + +namespace boost { + namespace serialization { + + /** + * Serialization function + * @tparam Archive Boost library template + * @param ar Boost parameter - filled automatically during serialization! + * @param n NeuronConstant instance + * @param version Boost parameter - filled automatically during serialization! + */ + template<class Archive> + void serialize(Archive& ar, + lib4neuro::NeuronConstant& n, + const unsigned int version) { + lib4neuro::NeuronConstant::access::serialize(ar, + n, + version); + } + + } // namespace serialization +} // namespace boost + + + +#endif //LIB4NEURO_NEURON_SERIALIZATION_H diff --git a/src/Neuron/NeuronLinear.cpp b/src/Neuron/NeuronLinear.cpp index afb73c6ac102d5b8e725cbceac2c3c38075cc887..7b35d66aec0c8e6f0aff5460c55850d38c6e2819 100644 --- a/src/Neuron/NeuronLinear.cpp +++ b/src/Neuron/NeuronLinear.cpp @@ -1,32 +1,35 @@ -// -// Created by fluffymoo on 11.6.18. -// - -#include "NeuronLinear.h" +#include <boost/serialization/export.hpp> +#include "NeuronLinear.h" +#include "NeuronConstant.h" +#include "NeuronSerialization.h" +#include "NeuronLinearSerialization.h" -NeuronLinear::NeuronLinear( ) {} +BOOST_CLASS_EXPORT_IMPLEMENT(lib4neuro::NeuronLinear); -double NeuronLinear::activate( double x, double b ) { +namespace lib4neuro { + NeuronLinear::NeuronLinear() {} - return x + b; -} + double NeuronLinear::activate(double x, + double b) { + this->activation_val = x + b; + return this->activation_val; + } -double NeuronLinear::activation_function_eval_derivative_bias( double x, double b ) { - return 1.0; -} + double NeuronLinear::activation_function_eval_derivative_bias(double x, + double b) { + return 1.0; + } -double NeuronLinear::activation_function_eval_derivative( double x, double b ) { - return 1.0; -} + double NeuronLinear::activation_function_eval_derivative(double x, + double b) { + return 1.0; + } -Neuron* NeuronLinear::get_derivative() { - NeuronConstant* output = new NeuronConstant( 1.0 ); - return output; -} + Neuron* NeuronLinear::get_derivative() { + NeuronConstant* output = new NeuronConstant(1.0); + return output; + } -//template<class Archive> -//void NeuronLinear::serialize(Archive & ar, const unsigned int version) { -// ar & boost::serialization::base_object<Neuron>(*this); -//} +} \ No newline at end of file diff --git a/src/Neuron/NeuronLinear.h b/src/Neuron/NeuronLinear.h index 390916f71706b7f60357e302ebc29739dbd46b50..c6ef7db7dfe4f1e0b9cd6f5a5bdf68b8535a3671 100644 --- a/src/Neuron/NeuronLinear.h +++ b/src/Neuron/NeuronLinear.h @@ -10,61 +10,61 @@ #ifndef INC_4NEURO_NEURONLINEAR_H #define INC_4NEURO_NEURONLINEAR_H -#include "../settings.h" - #include "Neuron.h" -#include "NeuronConstant.h" -#include <boost/serialization/base_object.hpp> +namespace lib4neuro { -/** - * Linear neuron class - uses activation function in the form f(x)=a*x + b, - * 'x' being the neuron's potential - */ -class NeuronLinear:public Neuron, public IDifferentiable { -private: - friend class boost::serialization::access; + /** + * Linear neuron class - uses activation function in the form f(x)=a*x + b, + * 'x' being the neuron's potential + */ + class NeuronLinear : public NeuronDifferentiable { - template<class Archive> - void serialize(Archive & ar, const unsigned int version){ - ar & boost::serialization::base_object<Neuron>(*this); - }; + public: -public: + /** + * Struct used to access private properties from + * the serialization function + */ + struct access; - /** - * Constructs the object of the Linear neuron with activation function - * f(x) = x + b - * @param[in] b Bias - */ - LIB4NEURO_API explicit NeuronLinear( ); + /** + * Constructs the object of the Linear neuron with activation function + * f(x) = x + b + * @param[in] b Bias + */ + LIB4NEURO_API explicit NeuronLinear(); - /** - * Evaluates 'x + b' and stores the result into the 'state' property - */ - LIB4NEURO_API double activate( double x, double b ) override; + /** + * Evaluates 'x + b' and stores the result into the 'state' property + */ + LIB4NEURO_API double activate(double x, + double b) override; - /** - * Calculates the partial derivative of the activation function - * f(x) = x + b at point x - * @return Partial derivative of the activation function according to the - * 'bias' parameter. Returns 1.0 - */ - LIB4NEURO_API double activation_function_eval_derivative_bias( double x, double b ) override; + /** + * Calculates the partial derivative of the activation function + * f(x) = x + b at point x + * @return Partial derivative of the activation function according to the + * 'bias' parameter. Returns 1.0 + */ + LIB4NEURO_API double activation_function_eval_derivative_bias(double x, + double b) override; - /** - * Calculates d/dx of (x + b) at point x - * @return 1.0 - */ - LIB4NEURO_API double activation_function_eval_derivative( double x, double b ) override; + /** + * Calculates d/dx of (x + b) at point x + * @return 1.0 + */ + LIB4NEURO_API double activation_function_eval_derivative(double x, + double b) override; - /** - * Returns a pointer to a Neuron with derivative as its activation function - * @return - */ - LIB4NEURO_API Neuron* get_derivative( ) override; + /** + * Returns a pointer to a Neuron with derivative as its activation function + * @return + */ + LIB4NEURO_API Neuron* get_derivative() override; -}; + }; +} #endif //INC_4NEURO_NEURONLINEAR_H diff --git a/src/Neuron/NeuronLinearSerialization.h b/src/Neuron/NeuronLinearSerialization.h new file mode 100644 index 0000000000000000000000000000000000000000..5d3cbef507e7cb06f88e564dab28e6629e55e634 --- /dev/null +++ b/src/Neuron/NeuronLinearSerialization.h @@ -0,0 +1,48 @@ + +#ifndef LIB4NEURO_NEURONLINEARSERIALIZATION_H +#define LIB4NEURO_NEURONLINEARSERIALIZATION_H + +#include <boost/serialization/base_object.hpp> +#include <boost/archive/text_oarchive.hpp> +#include <boost/archive/text_iarchive.hpp> +#include <boost/serialization/export.hpp> + +#include "NeuronLinear.h" +#include "NeuronSerialization.h" + +BOOST_CLASS_EXPORT_KEY(lib4neuro::NeuronLinear); + +namespace lib4neuro { + struct NeuronLinear::access { + template<class Archive> + static void serialize(Archive& ar, + NeuronLinear& n, + const unsigned int version) { + ar & boost::serialization::base_object<Neuron>(n); + } + }; +} + +namespace boost { + namespace serialization { + + /** + * Serialization function + * @tparam Archive Boost library template + * @param ar Boost parameter - filled automatically during serialization! + * @param n NeuronLinear instance + * @param version Boost parameter - filled automatically during serialization! + */ + template<class Archive> + void serialize(Archive& ar, + lib4neuro::NeuronLinear& n, + const unsigned int version) { + lib4neuro::NeuronLinear::access::serialize(ar, + n, + version); + } + + } // namespace serialization +} // namespace boost + +#endif //LIB4NEURO_NEURONLINEARSERIALIZATION_H diff --git a/src/Neuron/NeuronLogistic.cpp b/src/Neuron/NeuronLogistic.cpp index 6ca3f80006e07752dd6e85392f45941159b8c74d..ccac42af484f52d06c24d92d29be7d267a55185d 100644 --- a/src/Neuron/NeuronLogistic.cpp +++ b/src/Neuron/NeuronLogistic.cpp @@ -1,110 +1,143 @@ -// -// Created by fluffymoo on 11.6.18. -// +#include <boost/serialization/export.hpp> #include "NeuronLogistic.h" +#include "NeuronSerialization.h" +#include "NeuronLogisticSerialization.h" -NeuronLogistic_d2::NeuronLogistic_d2( ) {} +BOOST_CLASS_EXPORT_IMPLEMENT(lib4neuro::NeuronLogistic); +BOOST_CLASS_EXPORT_IMPLEMENT(lib4neuro::NeuronLogistic_d1); +BOOST_CLASS_EXPORT_IMPLEMENT(lib4neuro::NeuronLogistic_d2); -double NeuronLogistic_d2::activate( double x, double b ) { - //(e^(b + x) (e^b - e^x))/(e^b + e^x)^3 +namespace lib4neuro { - double ex = std::pow(E, x); - double eb = std::pow(E, b); - double denom = (eb + ex); + NeuronLogistic_d2::NeuronLogistic_d2() {} - return (eb*ex*(eb - ex))/(denom*denom*denom); -} + double NeuronLogistic_d2::activate(double x, + double b) { + //(e^(b + x) (e^b - e^x))/(e^b + e^x)^3 -double NeuronLogistic_d2::activation_function_eval_derivative_bias( double x, double b ) { - //-(e^(b + x) (-4 e^(b + x) + e^(2 b) + e^(2 x)))/(e^b + e^x)^4 + double ex = std::pow(lib4neuro::E, + x); + double eb = std::pow(E, + b); + double denom = (eb + ex); - double eb = std::pow(E, b); - double ex = std::pow(E, x); - double ebex = eb * ex; - double denom = (eb + ex); + this->activation_val = (eb * ex * (eb - ex)) / (denom * denom * denom); + return this->activation_val; + } - return -(ebex*(-4*ebex + eb*eb +ex*ex))/(denom*denom*denom*denom); -} + double NeuronLogistic_d2::activation_function_eval_derivative_bias(double x, + double b) { + //-(e^(b + x) (-4 e^(b + x) + e^(2 b) + e^(2 x)))/(e^b + e^x)^4 -double NeuronLogistic_d2::activation_function_eval_derivative( double x, double b ) { - //(e^(b + x) (-4 e^(b + x) + e^(2 b) + e^(2 x)))/(e^b + e^x)^4 - return -this->activation_function_eval_derivative_bias( x, b ); -} + double eb = std::pow(E, + b); + double ex = std::pow(E, + x); + double ebex = eb * ex; + double denom = (eb + ex); -NeuronLogistic* NeuronLogistic_d2::get_derivative() { - //TODO maybe not the best way - return nullptr; -} + return -(ebex * (-4 * ebex + eb * eb + ex * ex)) / (denom * denom * denom * denom); + } -NeuronLogistic_d1::NeuronLogistic_d1( ) {} + double NeuronLogistic_d2::activation_function_eval_derivative(double x, + double b) { + //(e^(b + x) (-4 e^(b + x) + e^(2 b) + e^(2 x)))/(e^b + e^x)^4 + return -this->activation_function_eval_derivative_bias(x, + b); + } + NeuronLogistic* NeuronLogistic_d2::get_derivative() { + //TODO maybe not the best way + return nullptr; + } -double NeuronLogistic_d1::activate( double x, double b ) { - //e^(b - x)/(e^(b - x) + 1)^2 + NeuronLogistic_d1::NeuronLogistic_d1() {} - double ex = std::pow(E, x); - double eb = std::pow(E, b); - double d = (eb/ex); - double denom = (d + 1); - return d/(denom*denom); -} + double NeuronLogistic_d1::activate(double x, + double b) { + //e^(b - x)/(e^(b - x) + 1)^2 -double NeuronLogistic_d1::activation_function_eval_derivative_bias( double x, double b ) { - //(e^(b + x) (e^x - e^b))/(e^b + e^x)^3 + double ex = std::pow(E, + x); + double eb = std::pow(E, + b); + double d = (eb / ex); + double denom = (d + 1); - double ex = std::pow(E, x); - double eb = std::pow(E, b); - double denom = (eb + ex); + this->activation_val = d / (denom * denom); + return this->activation_val; + } - return (eb*ex* (ex - eb))/(denom*denom*denom); -} + double NeuronLogistic_d1::activation_function_eval_derivative_bias(double x, + double b) { + //(e^(b + x) (e^x - e^b))/(e^b + e^x)^3 -double NeuronLogistic_d1::activation_function_eval_derivative( double x, double b ) { - //(e^(b + x) (e^b - e^x))/(e^b + e^x)^3 - return -this->activation_function_eval_derivative_bias( x, b ); -} + double ex = std::pow(E, + x); + double eb = std::pow(E, + b); + double denom = (eb + ex); -NeuronLogistic* NeuronLogistic_d1::get_derivative( ) { - //(e^(b + x) (e^b - e^x))/(e^b + e^x)^3 - NeuronLogistic_d2* output = nullptr; + return (eb * ex * (ex - eb)) / (denom * denom * denom); + } - output = new NeuronLogistic_d2( ); + double NeuronLogistic_d1::activation_function_eval_derivative(double x, + double b) { + //(e^(b + x) (e^b - e^x))/(e^b + e^x)^3 + return -this->activation_function_eval_derivative_bias(x, + b); + } - return output; -} + NeuronLogistic* NeuronLogistic_d1::get_derivative() { + //(e^(b + x) (e^b - e^x))/(e^b + e^x)^3 + NeuronLogistic_d2* output = nullptr; -NeuronLogistic::NeuronLogistic( ) {} + output = new NeuronLogistic_d2(); -double NeuronLogistic::activate( double x, double b ) { - //(1 + e^(-x + b))^(-1) + return output; + } - double ex = std::pow(E, b - x); - return 1.0 / (1.0 + ex); -} + NeuronLogistic::NeuronLogistic() {} -double NeuronLogistic::activation_function_eval_derivative_bias( double x, double b ) { - //-e^(b - x)/(e^(b - x) + 1)^2 - double ex = std::pow(E, b - x); - double denom = (ex + 1); + double NeuronLogistic::activate(double x, + double b) { + //(1 + e^(-x + b))^(-1) - return -ex/(denom*denom); -} + double ex = std::pow(E, + b - x); + this->activation_val = 1.0 / (1.0 + ex); + return this->activation_val; + } -double NeuronLogistic::activation_function_eval_derivative( double x, double b ) { - //e^(b - x)/(e^(b - x) + 1)^2 - return -this->activation_function_eval_derivative_bias( x, b ); + double NeuronLogistic::activation_function_eval_derivative_bias(double x, + double b) { + double ex = std::pow(E, + b - x); + double denom = (ex + 1); + double res = -ex / (denom * denom); + + return res; + } -} -NeuronLogistic* NeuronLogistic::get_derivative( ) { + double NeuronLogistic::activation_function_eval_derivative(double x, + double b) { + //e^(b - x)/(e^(b - x) + 1)^2 + return -this->activation_function_eval_derivative_bias(x, + b); - NeuronLogistic_d1 *output = nullptr; - output = new NeuronLogistic_d1( ); + } - return output; + NeuronLogistic* NeuronLogistic::get_derivative() { -} \ No newline at end of file + NeuronLogistic_d1* output = nullptr; + output = new NeuronLogistic_d1(); + + return output; + } + +} diff --git a/src/Neuron/NeuronLogistic.h b/src/Neuron/NeuronLogistic.h index 950063efa06ce86a95b99ea54c36b2361b3a51a1..ca9386891c283b577154dec146d4fb9f9354099a 100644 --- a/src/Neuron/NeuronLogistic.h +++ b/src/Neuron/NeuronLogistic.h @@ -10,150 +10,150 @@ #ifndef INC_4NEURO_NEURONLOGISTIC_H #define INC_4NEURO_NEURONLOGISTIC_H -#include "../settings.h" - #include <cmath> -#include "Neuron.h" -#include "../constants.h" +#include "../settings.h" +#include "Neuron.h" -class NeuronLogistic:public Neuron, public IDifferentiable { - friend class boost::serialization::access; - -protected: - template<class Archive> - void serialize(Archive & ar, const unsigned int version){ - //TODO separate implementation to NeuronLogistic.cpp! - ar & boost::serialization::base_object<Neuron>(*this); - }; - -public: - /** - * Constructs the object of the Logistic neuron with activation function - * f(x) = (1 + e^(-x + b))^(-1) - */ - LIB4NEURO_API explicit NeuronLogistic( ); - - /** - * Evaluates '(1 + e^(-x + b))^(-1)' and stores the result into the 'state' property - */ - LIB4NEURO_API virtual double activate( double x, double b ) override; - - /** - * Calculates the partial derivative of the activation function - * f(x) = (1 + e^(-x + b))^(-1) - * @return Partial derivative of the activation function according to the - * bias, returns: -e^(b - x)/(e^(b - x) + 1)^2 - */ - LIB4NEURO_API virtual double activation_function_eval_derivative_bias( double x, double b ) override; - /** - * Calculates d/dx of (1 + e^(-x + b))^(-1) - * @return e^(b - x)/(e^(b - x) + 1)^2 - */ - LIB4NEURO_API virtual double activation_function_eval_derivative( double x, double b ) override; - - /** - * Returns a pointer to a Neuron with derivative as its activation function - * @return - */ - LIB4NEURO_API virtual NeuronLogistic* get_derivative( ) override; -}; - - -class NeuronLogistic_d1:public NeuronLogistic { -private: - friend class boost::serialization::access; - - template<class Archive> - void serialize(Archive & ar, const unsigned int version){ - //TODO separate implementation to Neuronogistic_d1.cpp! - ar & boost::serialization::base_object<Neuron>(*this); +namespace lib4neuro { + class NeuronLogistic : public NeuronDifferentiable { + + public: + + /** + * Struct used to access private properties from + * the serialization function + */ + struct access; + + /** + * Constructs the object of the Logistic neuron with activation function + * f(x) = (1 + e^(-x + b))^(-1) + */ + LIB4NEURO_API explicit NeuronLogistic(); + + /** + * Evaluates '(1 + e^(-x + b))^(-1)' and stores the result into the 'state' property + */ + LIB4NEURO_API virtual double activate(double x, + double b) override; + + /** + * Calculates the partial derivative of the activation function + * f(x) = (1 + e^(-x + b))^(-1) + * @return Partial derivative of the activation function according to the + * bias, returns: -e^(b - x)/(e^(b - x) + 1)^2 + */ + LIB4NEURO_API virtual double activation_function_eval_derivative_bias(double x, + double b) override; + /** + * Calculates d/dx of (1 + e^(-x + b))^(-1) + * @return e^(b - x)/(e^(b - x) + 1)^2 + */ + LIB4NEURO_API virtual double activation_function_eval_derivative(double x, + double b) override; + + /** + * Returns a pointer to a Neuron with derivative as its activation function + * @return + */ + LIB4NEURO_API virtual NeuronLogistic* get_derivative() override; }; -public: - - /** - * Constructs the object of the Logistic neuron with activation function - * f(x) = e^(b - x)/(e^(b - x) + 1)^2 - * @param[in] b Bias - */ - LIB4NEURO_API explicit NeuronLogistic_d1( ); - - /** - * Evaluates 'e^(b - x)/(e^(b - x) + 1)^2' and returns the result - */ - LIB4NEURO_API virtual double activate( double x, double b ) override; - - /** - * Calculates the partial derivative of the activation function - * f(x) = e^(b - x)/(e^(b - x) + 1)^2 - * @return Partial derivative of the activation function according to the - * bias, returns: (e^(b + x) (e^x - e^b))/(e^b + e^x)^3 - */ - LIB4NEURO_API virtual double activation_function_eval_derivative_bias( double x, double b ) override; - - /** - * Calculates d/dx of e^(b - x)*(1 + e^(b - x))^(-2) - * @return (e^(b + x) (e^b - e^x))/(e^b + e^x)^3 - */ - LIB4NEURO_API virtual double activation_function_eval_derivative( double x, double b ) override; - /** - * Returns a pointer to a Neuron with derivative as its activation function - * @return - */ - LIB4NEURO_API virtual NeuronLogistic* get_derivative( ) override; -}; - - - - - -class NeuronLogistic_d2:public NeuronLogistic_d1 { -private: - friend class boost::serialization::access; - - template<class Archive> - void serialize(Archive & ar, const unsigned int version){ - //TODO separate implementation to NeuronLogistic_d1.cpp! - ar & boost::serialization::base_object<Neuron>(*this); + class NeuronLogistic_d1 : public NeuronLogistic { + + public: + + /** + * Struct used to access private properties from + * the serialization function + */ + struct access; + + /** + * Constructs the object of the Logistic neuron with activation function + * f(x) = e^(b - x)/(e^(b - x) + 1)^2 + * @param[in] b Bias + */ + LIB4NEURO_API explicit NeuronLogistic_d1(); + + /** + * Evaluates 'e^(b - x)/(e^(b - x) + 1)^2' and returns the result + */ + LIB4NEURO_API virtual double activate(double x, + double b) override; + + /** + * Calculates the partial derivative of the activation function + * f(x) = e^(b - x)/(e^(b - x) + 1)^2 + * @return Partial derivative of the activation function according to the + * bias, returns: (e^(b + x) (e^x - e^b))/(e^b + e^x)^3 + */ + LIB4NEURO_API virtual double activation_function_eval_derivative_bias(double x, + double b) override; + + /** + * Calculates d/dx of e^(b - x)*(1 + e^(b - x))^(-2) + * @return (e^(b + x) (e^b - e^x))/(e^b + e^x)^3 + */ + LIB4NEURO_API virtual double activation_function_eval_derivative(double x, + double b) override; + + /** + * Returns a pointer to a Neuron with derivative as its activation function + * @return + */ + LIB4NEURO_API virtual NeuronLogistic* get_derivative() override; }; -public: - /** - * Constructs the object of the Logistic neuron with activation function - * f(x) = (e^(b + x) (e^b - e^x))/(e^b + e^x)^3 - */ - LIB4NEURO_API explicit NeuronLogistic_d2( ); - - /** - * Evaluates '(e^(b + x) (e^b - e^x))/(e^b + e^x)^3' and returns the result - */ - LIB4NEURO_API virtual double activate( double x, double b ) override; - - /** - * Calculates the partial derivative of the activation function - * f(x) = (e^(b + x) (e^b - e^x))/(e^b + e^x)^3 - * @return Partial derivative of the activation function according to the - * bias, returns: -(e^(b + x) (-4 e^(b + x) + e^(2 b) + e^(2 x)))/(e^b + e^x)^4 - */ - LIB4NEURO_API virtual double activation_function_eval_derivative_bias( double x, double b ) override; - - /** - * Calculates d/dx of (e^(b + x) (e^b - e^x))/(e^b + e^x)^3 - * @return (e^(b + x) (-4 e^(b + x) + e^(2 b) + e^(2 x)))/(e^b + e^x)^4 - */ - LIB4NEURO_API virtual double activation_function_eval_derivative( double x, double b ) override; - - /** - * - * @return - */ - LIB4NEURO_API virtual NeuronLogistic* get_derivative( ) override; - -}; + class NeuronLogistic_d2 : public NeuronLogistic_d1 { + + public: + + /** + * Struct used to access private properties from + * the serialization function + */ + struct access; + + /** + * Constructs the object of the Logistic neuron with activation function + * f(x) = (e^(b + x) (e^b - e^x))/(e^b + e^x)^3 + */ + LIB4NEURO_API explicit NeuronLogistic_d2(); + + /** + * Evaluates '(e^(b + x) (e^b - e^x))/(e^b + e^x)^3' and returns the result + */ + LIB4NEURO_API virtual double activate(double x, + double b) override; + + /** + * Calculates the partial derivative of the activation function + * f(x) = (e^(b + x) (e^b - e^x))/(e^b + e^x)^3 + * @return Partial derivative of the activation function according to the + * bias, returns: -(e^(b + x) (-4 e^(b + x) + e^(2 b) + e^(2 x)))/(e^b + e^x)^4 + */ + LIB4NEURO_API virtual double activation_function_eval_derivative_bias(double x, + double b) override; + + /** + * Calculates d/dx of (e^(b + x) (e^b - e^x))/(e^b + e^x)^3 + * @return (e^(b + x) (-4 e^(b + x) + e^(2 b) + e^(2 x)))/(e^b + e^x)^4 + */ + LIB4NEURO_API virtual double activation_function_eval_derivative(double x, + double b) override; + + /** + * + * @return + */ + LIB4NEURO_API virtual NeuronLogistic* get_derivative() override; + }; +} #endif //INC_4NEURO_NEURONLOGISTIC_H diff --git a/src/Neuron/NeuronLogisticSerialization.h b/src/Neuron/NeuronLogisticSerialization.h new file mode 100644 index 0000000000000000000000000000000000000000..0e5f8cb9ce6f991f9c7b6a35443405510d578551 --- /dev/null +++ b/src/Neuron/NeuronLogisticSerialization.h @@ -0,0 +1,102 @@ + +#ifndef LIB4NEURO_NEURONLOGISTICSERIALIZATION_H +#define LIB4NEURO_NEURONLOGISTICSERIALIZATION_H + +#include <boost/serialization/base_object.hpp> +#include <boost/archive/text_oarchive.hpp> +#include <boost/archive/text_iarchive.hpp> +#include <boost/serialization/export.hpp> + +#include "../constants.h" +#include "NeuronLogistic.h" +#include "NeuronSerialization.h" + +BOOST_CLASS_EXPORT_KEY(lib4neuro::NeuronLogistic); +BOOST_CLASS_EXPORT_KEY(lib4neuro::NeuronLogistic_d1); +BOOST_CLASS_EXPORT_KEY(lib4neuro::NeuronLogistic_d2); + +namespace lib4neuro { + struct NeuronLogistic::access { + template<class Archive> + static void serialize(Archive& ar, + NeuronLogistic& n, + const unsigned int version) { + ar & boost::serialization::base_object<Neuron>(n); + } + }; + + struct NeuronLogistic_d1::access { + template<class Archive> + static void serialize(Archive& ar, + NeuronLogistic_d1& n, + const unsigned int version) { + ar & boost::serialization::base_object<NeuronLogistic>(n); + } + }; + + struct NeuronLogistic_d2::access { + template<class Archive> + static void serialize(Archive& ar, + NeuronLogistic_d2& n, + const unsigned int version) { + ar & boost::serialization::base_object<NeuronLogistic_d1>(n); + } + }; + +} + +namespace boost { + namespace serialization { + + /** + * Serialization function + * @tparam Archive Boost library template + * @param ar Boost parameter - filled automatically during serialization! + * @param n NeuronLogistic instance + * @param version Boost parameter - filled automatically during serialization! + */ + template<class Archive> + void serialize(Archive& ar, + lib4neuro::NeuronLogistic& n, + const unsigned int version) { + lib4neuro::NeuronLogistic::access::serialize(ar, + n, + version); + } + + /** + * Serialization function + * @tparam Archive Boost library template + * @param ar Boost parameter - filled automatically during serialization! + * @param n NeuronLogistic_d1 instance + * @param version Boost parameter - filled automatically during serialization! + */ + template<class Archive> + void serialize(Archive& ar, + lib4neuro::NeuronLogistic_d1& n, + const unsigned int version) { + lib4neuro::NeuronLogistic_d1::access::serialize(ar, + n, + version); + } + + /** + * Serialization function + * @tparam Archive Boost library template + * @param ar Boost parameter - filled automatically during serialization! + * @param n NeuronLogistic_d2 instance + * @param version Boost parameter - filled automatically during serialization! + */ + template<class Archive> + void serialize(Archive& ar, + lib4neuro::NeuronLogistic_d2& n, + const unsigned int version) { + lib4neuro::NeuronLogistic_d2::access::serialize(ar, + n, + version); + } + + } // namespace serialization +} // namespace boost + +#endif //LIB4NEURO_NEURONLOGISTICSERIALIZATION_H diff --git a/src/Neuron/NeuronRectifier.cpp b/src/Neuron/NeuronRectifier.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ef64823e5402f253249249704001c0ebc4bc3afd --- /dev/null +++ b/src/Neuron/NeuronRectifier.cpp @@ -0,0 +1,35 @@ + +#include <boost/serialization/export.hpp> + +#include "NeuronRectifier.h" +#include "NeuronRectifierSerialization.h" + +BOOST_CLASS_EXPORT_IMPLEMENT(lib4neuro::NeuronRectifier); + +namespace lib4neuro { + NeuronRectifier::NeuronRectifier() {} + + double NeuronRectifier::activate(double x, + double b) { + this->activation_val = std::max(0, + x + b); + } + + double NeuronRectifier::activation_function_eval_derivative(double x, + double b) { + // f'(0) = 0 for the purposes of training + return ((x + b) > 0) ? 1 : 0; + } + + double NeuronRectifier::activation_function_eval_derivative(double x, + double b) { + // f'(0) = 0 for the purposes of training + return ((x + b) > 0) ? 1 : 0; + } + + Neuron* NeuronRectifier::get_derivative() { + NeuronConstant* output = new NeuronConstant(); + } + + +} \ No newline at end of file diff --git a/src/Neuron/NeuronRectifier.h b/src/Neuron/NeuronRectifier.h new file mode 100644 index 0000000000000000000000000000000000000000..f7014de2eb0f046e3a12b25e3dfbde2eb3daef06 --- /dev/null +++ b/src/Neuron/NeuronRectifier.h @@ -0,0 +1,32 @@ + +#include "Neuron.h" + +#ifndef LIB4NEURO_NEURONRECTIFIER_H +#define LIB4NEURO_NEURONRECTIFIER_H + +namespace lib4neuro { + + /** + * Rectifier linear unit neuron class - uses activation function in the form f(x) = max(0, x), + * 'x' being the neuron's potential + */ + class NeuronRectifier : NeuronDifferentiable { + public: + struct access; + + LIB4NEURO_API explicit NeuronRectifier(); + + LIB4NEURO_API double activate(double x, + double b) override; + + LIB4NEURO_API double activation_function_eval_derivative_bias(double x, + double b) override; + + LIB4NEURO_API double activation_function_eval_derivative(double x, + double b) override; + + LIB4NEURO_API Neuron* get_derivative() override; + }; +} + +#endif //LIB4NEURO_NEURONRECTIFIER_H diff --git a/src/Neuron/NeuronRectifierSerialization.h b/src/Neuron/NeuronRectifierSerialization.h new file mode 100644 index 0000000000000000000000000000000000000000..929dc7af78804d86ade12137a3b196c39dadaf9a --- /dev/null +++ b/src/Neuron/NeuronRectifierSerialization.h @@ -0,0 +1,48 @@ + +#ifndef LIB4NEURO_NEURONRECTIFIERSERIALIZATION_H +#define LIB4NEURO_NEURONRECTIFIERSERIALIZATION_H + +#include <boost/serialization/base_object.hpp> +#include <boost/archive/text_oarchive.hpp> +#include <boost/archive/text_iarchive.hpp> +#include <boost/serialization/export.hpp> + +#include "NeuronRectifier.h" +#include "NeuronSerialization.h" + +BOOST_CLASS_EXPORT_KEY(lib4neuro::NeuronRectifier); + +namespace lib4neuro { + struct NeuronRectifier::access { + template<class Archive> + static void serialize(Archive& ar, + Neuronrectifier& n, + const unsigned int version) { + ar & boost::serialization::base_object<Neuron>(n); + } + }; +} + +namespace boost { + namespace serialization { + + /** + * Serialization function + * @tparam Archive Boost library template + * @param ar Boost parameter - filled automatically during serialization! + * @param n NeuronRectifier instance + * @param version Boost parameter - filled automatically during serialization! + */ + template<class Archive> + void serialize(Archive& ar, + lib4neuro::NeuronRectifier& n, + const unsigned int version) { + lib4neuro::NeuronRectifier::access::serialize(ar, + n, + version); + } + + } // namespace serialization +} // namespace boost + +#endif //LIB4NEURO_NEURONRECTIFIERSERIALIZATION_H diff --git a/src/Neuron/NeuronSerialization.h b/src/Neuron/NeuronSerialization.h new file mode 100644 index 0000000000000000000000000000000000000000..53ff06ab622bd0628ed698229a38d9d3ee01e6bb --- /dev/null +++ b/src/Neuron/NeuronSerialization.h @@ -0,0 +1,74 @@ + +#ifndef LIB4NEURO_NEURON_SERIALIZATION_H +#define LIB4NEURO_NEURON_SERIALIZATION_H + +#include <boost/archive/text_oarchive.hpp> +#include <boost/archive/text_iarchive.hpp> +#include <boost/serialization/assume_abstract.hpp> +#include <boost/serialization/export.hpp> + +#include "Neuron.h" + +BOOST_SERIALIZATION_ASSUME_ABSTRACT(lib4neuro::Neuron); +BOOST_CLASS_EXPORT_KEY(lib4neuro::Neuron); + +BOOST_SERIALIZATION_ASSUME_ABSTRACT(lib4neuro::NeuronDifferentiable); +BOOST_CLASS_EXPORT_KEY(lib4neuro::NeuronDifferentiable); + +namespace lib4neuro { + struct Neuron::access { + template<class Archive> + static void serialize(Archive& ar, + Neuron& n, + const unsigned int version) {} + }; + + struct NeuronDifferentiable::access { + template<class Archive> + static void serialize(Archive& ar, + NeuronDifferentiable& n, + const unsigned int version) { + ar & boost::serialization::base_object<Neuron>(n); + } + }; +} + +namespace boost { + namespace serialization { + + /** + * Serialization function + * @tparam Archive Boost library template + * @param ar Boost parameter - filled automatically during serialization! + * @param n Neuron instance + * @param version Boost parameter - filled automatically during serialization! + */ + template<class Archive> + void serialize(Archive& ar, + lib4neuro::Neuron& n, + const unsigned int version) { + lib4neuro::Neuron::access::serialize(ar, + n, + version); + } + + /** + * Serialization function + * @tparam Archive Boost library template + * @param ar Boost parameter - filled automatically during serialization! + * @param n NeuronDifferentiable instance + * @param version Boost parameter - filled automatically during serialization! + */ + template<class Archive> + void serialize(Archive& ar, + lib4neuro::NeuronDifferentiable& n, + const unsigned int version) { + lib4neuro::NeuronDifferentiable::access::serialize(ar, + n, + version); + } + + } // namespace serialization +} // namespace boost + +#endif //LIB4NEURO_NEURON_SERIALIZATION_H diff --git a/src/NormalizationStrategy/NormalizationStrategy.cpp b/src/NormalizationStrategy/NormalizationStrategy.cpp new file mode 100644 index 0000000000000000000000000000000000000000..23463ccc77c29129bc17d381af8fa54395490d25 --- /dev/null +++ b/src/NormalizationStrategy/NormalizationStrategy.cpp @@ -0,0 +1,50 @@ + +#include <cmath> +#include <stdexcept> +#include <boost/serialization/export.hpp> + +#include "NormalizationStrategy.h" +#include "NormalizationStrategySerialization.h" +#include "exceptions.h" + +BOOST_CLASS_EXPORT_IMPLEMENT(NormalizationStrategy) +BOOST_CLASS_EXPORT_IMPLEMENT(DoubleUnitStrategy) + +//NormalizationStrategy::~NormalizationStrategy() = def + +double NormalizationStrategy::get_max_value() { + return this->max_min_inp_val.at(0); +} + +double NormalizationStrategy::get_min_value() { + return this->max_min_inp_val.at(1); +} + +DoubleUnitStrategy::DoubleUnitStrategy() {} + +DoubleUnitStrategy::~DoubleUnitStrategy() {} + +double DoubleUnitStrategy::normalize(double n, + double max, + double min) { + if (this->max_min_inp_val.empty()) { + this->max_min_inp_val.emplace_back(max); + this->max_min_inp_val.emplace_back(min); + } else { + this->max_min_inp_val.at(0) = max; + this->max_min_inp_val.at(1) = min; + } + +// return 2 * (n - min) / (max - min) - 1; + return n / (this->get_max_value() - this->get_min_value()); +} + +double DoubleUnitStrategy::de_normalize(double n) { + if (this->max_min_inp_val.empty()) { + THROW_RUNTIME_ERROR("Data were not normalized, so de-normalization cannot progress!"); + } + + //return 0.5 * ((1 + n) * (this->get_max_value() - this->get_min_value())) + this->get_min_value(); + return n * (this->get_max_value() - this->get_min_value()); +} + diff --git a/src/NormalizationStrategy/NormalizationStrategy.h b/src/NormalizationStrategy/NormalizationStrategy.h new file mode 100644 index 0000000000000000000000000000000000000000..ae961f6e70edeb593e5acb9cfd6da051264036ae --- /dev/null +++ b/src/NormalizationStrategy/NormalizationStrategy.h @@ -0,0 +1,99 @@ + +#ifndef LIB4NEURO_NORMALIZATIONSTRATEGY_H +#define LIB4NEURO_NORMALIZATIONSTRATEGY_H + +#include <limits> +#include <vector> + +/** + * + */ +class NormalizationStrategy { +protected: + + /** + * Maximum (index 0) and minimum (index 1) input value + */ + std::vector<double> max_min_inp_val; + +public: + + /** + * + */ + struct access; + + virtual ~NormalizationStrategy() = default; + + /** + * + * @param n + * @param max + * @param min + * @return + */ + virtual double normalize(double n, + double max, + double min) = 0; + + /** + * + * @param n + * @param max + * @param min + * @return + */ + virtual double de_normalize(double n) = 0; + + /** + * + * @return + */ + double get_max_value(); + + /** + * + * @return + */ + double get_min_value(); +}; + +/** + * + */ +class DoubleUnitStrategy : public NormalizationStrategy { +public: + + /** + * + */ + struct access; + + /** + * + */ + DoubleUnitStrategy(); + + ~DoubleUnitStrategy() override; + + /** + * Normalizes the input value to the interval [-1,1] + * + * @param n + * @param max + * @param min + * @return + */ + double normalize(double n, + double max, + double min) override; + + /** + * + * @param n + * @return + */ + double de_normalize(double n) override; +}; + +#endif //LIB4NEURO_NORMALIZATIONSTRATEGY_H diff --git a/src/NormalizationStrategy/NormalizationStrategySerialization.h b/src/NormalizationStrategy/NormalizationStrategySerialization.h new file mode 100644 index 0000000000000000000000000000000000000000..d8258b2f85503e32dbd6ce08cd9e416440721c3d --- /dev/null +++ b/src/NormalizationStrategy/NormalizationStrategySerialization.h @@ -0,0 +1,72 @@ + +#ifndef LIB4NEURO_NORMALIZATIONSTRATEGYSERIALIZATION_H +#define LIB4NEURO_NORMALIZATIONSTRATEGYSERIALIZATION_H + +#include <boost/serialization/base_object.hpp> +#include <boost/archive/text_oarchive.hpp> +#include <boost/archive/text_iarchive.hpp> +#include <boost/serialization/export.hpp> +#include <boost/serialization/vector.hpp> + +#include "NormalizationStrategy.h" + +BOOST_SERIALIZATION_ASSUME_ABSTRACT(NormalizationStrategy) +BOOST_CLASS_EXPORT_KEY(NormalizationStrategy) +BOOST_CLASS_EXPORT_KEY(DoubleUnitStrategy) + +struct NormalizationStrategy::access { + template<class Archive> + static void serialize(Archive& ar, + NormalizationStrategy& ns, + const unsigned int version) { + ar & ns.max_min_inp_val; + } +}; + +struct DoubleUnitStrategy::access { + template<class Archive> + static void serialize(Archive& ar, + DoubleUnitStrategy& s, + const unsigned int version) { + ar & boost::serialization::base_object<NormalizationStrategy>(s); + } +}; + +namespace boost { + namespace serialization { + + /** + * Serialization function + * @tparam Archive Boost library template + * @param ar Boost parameter - filled automatically during serialization! + * @param ns NormalizationStrategy instance + * @param version Boost parameter - filled automatically during serialization! + */ + template<class Archive> + void serialize(Archive& ar, + NormalizationStrategy& ns, + const unsigned int version) { + NormalizationStrategy::access::serialize(ar, + ns, + version); + } + + /** + * Serialization function + * @tparam Archive Boost library template + * @param ar Boost parameter - filled automatically during serialization! + * @param s DoubleUnitStrategy instance + * @param version Boost parameter - filled automatically during serialization! + */ + template<class Archive> + void serialize(Archive& ar, + DoubleUnitStrategy& s, + const unsigned int version) { + DoubleUnitStrategy::access::serialize(ar, + s, + version); + } + } // namespace serialization +} // namespace boost + +#endif //LIB4NEURO_NORMALIZATIONSTRATEGYSERIALIZATION_H diff --git a/src/Solvers/DESolver.cpp b/src/Solvers/DESolver.cpp index 7889202723aa495cd16ad20f08146a6f7c78fa9f..b76065d248fbc70330ca5c9eab3d0555e2312e87 100644 --- a/src/Solvers/DESolver.cpp +++ b/src/Solvers/DESolver.cpp @@ -5,412 +5,471 @@ * @date 22.7.18 - */ +#include <boost/format.hpp> + +#include "message.h" #include "DESolver.h" +#include "exceptions.h" //TODO add support for multiple unknown functions -MultiIndex::MultiIndex(size_t dimension) { - this->dim = dimension; - this->partial_derivatives_degrees.resize( this->dim ); - std::fill( this->partial_derivatives_degrees.begin(), this->partial_derivatives_degrees.end(), 0 ); -} - -void MultiIndex::set_partial_derivative(size_t index, size_t value) { - this->partial_derivatives_degrees.at( index ) = value; -} - -std::vector<size_t>* MultiIndex::get_partial_derivatives_degrees() { - return &this->partial_derivatives_degrees; -} - -bool MultiIndex::operator<(const MultiIndex &rhs) const { - if(dim < rhs.dim){ return true; } - else if(dim > rhs.dim){ return false; } - - for(size_t i = 0; i < dim; ++i){ - if(partial_derivatives_degrees[i] < rhs.partial_derivatives_degrees[i]){ - return true; - } - else if(partial_derivatives_degrees[i] > rhs.partial_derivatives_degrees[i]){ - return false; - } +namespace lib4neuro { + MultiIndex::MultiIndex(size_t dimension) { + this->dim = dimension; + this->partial_derivatives_degrees.resize(this->dim); + std::fill(this->partial_derivatives_degrees.begin(), + this->partial_derivatives_degrees.end(), + 0); } - return false; -} - -std::string MultiIndex::to_string( )const { - std::string output; - char buff[ 255 ]; - for( size_t i = 0; i < this->dim - 1; ++i){ - sprintf(buff, "%d, ", (int)this->partial_derivatives_degrees[i]); - output.append( buff ); + void MultiIndex::set_partial_derivative(size_t index, + size_t value) { + this->partial_derivatives_degrees.at(index) = value; } - sprintf(buff, "%d", (int)this->partial_derivatives_degrees[this->dim - 1]); - output.append( buff ); - - return output; -} - -size_t MultiIndex::get_degree() const{ - size_t output = 0; - for( auto i: this->partial_derivatives_degrees ){ - output += i; + std::vector<size_t>* MultiIndex::get_partial_derivatives_degrees() { + return &this->partial_derivatives_degrees; } - return output; -} + bool MultiIndex::operator<(const MultiIndex& rhs) const { + if (dim < rhs.dim) { return true; } + else if (dim > rhs.dim) { return false; } - -DESolver::DESolver( size_t n_equations, size_t n_inputs, size_t m ) { - - if( m <= 0 || n_inputs <= 0 || n_equations <= 0 ){ - throw std::invalid_argument("Parameters 'm', 'n_equations', 'n_inputs' and 'n_outputs' must be greater than zero!"); + for (size_t i = 0; i < dim; ++i) { + if (partial_derivatives_degrees[i] < rhs.partial_derivatives_degrees[i]) { + return true; + } else if (partial_derivatives_degrees[i] > rhs.partial_derivatives_degrees[i]) { + return false; + } + } + return false; } - printf("Differential Equation Solver with %d equations\n--------------------------------------------------------------------------\n", (int)n_equations); - - printf("Constructing NN structure representing the solution [%d input neurons][%d inner neurons]...\n", (int)n_inputs, (int)m); - this->dim_i = n_inputs; - this->dim_inn= m; - this->n_equations = n_equations; + std::string MultiIndex::to_string() const { + std::string output; + char buff[255]; - this->solution = new NeuralNetwork( ); - - this->solution_inner_neurons = new std::vector<NeuronLogistic*>(0); - this->solution_inner_neurons->reserve( m ); - - /* input neurons */ - std::vector<size_t> input_set( this->dim_i ); - size_t idx; - for( size_t i = 0; i < this->dim_i; ++i ){ - NeuronLinear *input_i = new NeuronLinear( ); //f(x) = x - idx = this->solution->add_neuron( input_i, BIAS_TYPE::NO_BIAS ); - input_set[i] = idx; - } - this->solution->specify_input_neurons( input_set ); - size_t first_input_neuron = input_set[0]; - - /* output neuron */ - std::vector<size_t> output_set( 1 ); - idx = this->solution->add_neuron( new NeuronLinear( ), BIAS_TYPE::NO_BIAS );//f(x) = x - output_set[0] = idx; - this->solution->specify_output_neurons( output_set ); - size_t first_output_neuron = idx; - - /* inner neurons */ - size_t first_inner_neuron = 0; - for(size_t i = 0; i < this->dim_inn; ++i){ - NeuronLogistic *inner_i = new NeuronLogistic( ); //f(x) = 1.0 / (1.0 + e^(-x)) - this->solution_inner_neurons->push_back( inner_i ); - idx = this->solution->add_neuron( inner_i, BIAS_TYPE::NEXT_BIAS ); - - if(i == 0){ - first_inner_neuron = idx; + for (size_t i = 0; i < this->dim - 1; ++i) { + sprintf(buff, + "%d, ", + (int) this->partial_derivatives_degrees[i]); + output.append(buff); } + sprintf(buff, + "%d", + (int) this->partial_derivatives_degrees[this->dim - 1]); + output.append(buff); + + return output; } + size_t MultiIndex::get_degree() const { + size_t output = 0; - /* connections between input neurons and inner neurons */ - size_t weight_idx; - for(size_t i = 0; i < this->dim_i; ++i){ - for(size_t j = 0; j < this->dim_inn; ++j){ - weight_idx = this->solution->add_connection_simple(first_input_neuron + i, first_inner_neuron + j, SIMPLE_CONNECTION_TYPE::NEXT_WEIGHT ); - printf(" adding a connection between input neuron %2d[%2d] and inner neuron %2d[%2d], weight index %3d\n", (int)i, (int)(first_input_neuron + i), (int)j, (int)(first_inner_neuron + j), (int)weight_idx); + for (auto i: this->partial_derivatives_degrees) { + output += i; } - } - /* connections between inner neurons and output neurons */ - for(size_t i = 0; i < this->dim_inn; ++i){ - weight_idx = this->solution->add_connection_simple(first_inner_neuron + i, first_output_neuron, SIMPLE_CONNECTION_TYPE::NEXT_WEIGHT ); - printf(" adding a connection between inner neuron %2d[%2d] and output neuron %2d[%2d], weight index %3d\n", (int)i, (int)(first_inner_neuron + i), 0, (int)(first_output_neuron ), (int)weight_idx); + return output; } - MultiIndex initial_mi(this->dim_i); - this->map_multiindices2nn[initial_mi] = this->solution; + DESolver::DESolver(size_t n_equations, + size_t n_inputs, + size_t m) { - this->differential_equations = new std::vector<NeuralNetworkSum*>(0); - this->differential_equations->reserve(this->n_equations); + if (m <= 0 || n_inputs <= 0 || n_equations <= 0) { + THROW_INVALID_ARGUMENT_ERROR("Parameters 'm', 'n_equations', 'n_inputs' and 'n_outputs' must be greater than zero!"); + } + printf("Differential Equation Solver with %d equations\n--------------------------------------------------------------------------\n", + (int) n_equations); + + printf("Constructing NN structure representing the solution [%d input neurons][%d inner neurons]...\n", + (int) n_inputs, + (int) m); + + this->dim_i = n_inputs; + this->dim_inn = m; + this->n_equations = n_equations; + this->errors_functions_types.resize(n_equations); + this->errors_functions_data_sets.resize(n_equations); + + + + /* input neurons */ + std::vector<size_t> input_set(this->dim_i); + size_t idx; + for (size_t i = 0; i < this->dim_i; ++i) { + std::shared_ptr<Neuron> new_neuron; + new_neuron.reset(new NeuronLinear()); + idx = this->solution->add_neuron(new_neuron, + BIAS_TYPE::NO_BIAS); + input_set[i] = idx; + } + this->solution->specify_input_neurons(input_set); + size_t first_input_neuron = input_set[0]; + + /* output neuron */ + std::vector<size_t> output_set(1); + std::shared_ptr<Neuron> new_neuron; + new_neuron.reset(new NeuronLinear()); + idx = this->solution->add_neuron(new_neuron, + BIAS_TYPE::NO_BIAS);//f(x) = x + output_set[0] = idx; + this->solution->specify_output_neurons(output_set); + size_t first_output_neuron = idx; + + /* inner neurons */ + size_t first_inner_neuron = 0; + for (size_t i = 0; i < this->dim_inn; ++i) { + std::shared_ptr<NeuronLogistic> new_neuron2; + new_neuron2.reset(new NeuronLogistic()); + this->solution_inner_neurons.push_back(new_neuron2); + idx = this->solution->add_neuron(new_neuron2, + BIAS_TYPE::NEXT_BIAS); + + if (i == 0) { + first_inner_neuron = idx; + } + } - for( unsigned int i = 0; i < this->n_equations; ++i ){ - NeuralNetworkSum *new_sum = new NeuralNetworkSum(); - this->differential_equations->push_back(new_sum); - } + /* connections between input neurons and inner neurons */ + size_t weight_idx; + for (size_t i = 0; i < this->dim_i; ++i) { + for (size_t j = 0; j < this->dim_inn; ++j) { + weight_idx = this->solution->add_connection_simple(first_input_neuron + i, + first_inner_neuron + j, + SIMPLE_CONNECTION_TYPE::NEXT_WEIGHT); + printf(" adding a connection between input neuron %2d[%2d] and inner neuron %2d[%2d], weight index %3d\n", + (int) i, + (int) (first_input_neuron + i), + (int) j, + (int) (first_inner_neuron + j), + (int) weight_idx); + } + } - this->errors_functions_types = new std::vector<ErrorFunctionType >(this->n_equations); - this->errors_functions_data_sets = new std::vector<DataSet*>(this->n_equations); + /* connections between inner neurons and output neurons */ + for (size_t i = 0; i < this->dim_inn; ++i) { + weight_idx = this->solution->add_connection_simple(first_inner_neuron + i, + first_output_neuron, + SIMPLE_CONNECTION_TYPE::NEXT_WEIGHT); + printf(" adding a connection between inner neuron %2d[%2d] and output neuron %2d[%2d], weight index %3d\n", + (int) i, + (int) (first_inner_neuron + i), + 0, + (int) (first_output_neuron), + (int) weight_idx); + } - printf("done\n"); + MultiIndex initial_mi(this->dim_i); -} + this->map_multiindices2nn[initial_mi] = this->solution; -DESolver::~DESolver() { + this->differential_equations.reserve(this->n_equations); - if( this->solution_inner_neurons ){ - delete this->solution_inner_neurons; - this->solution_inner_neurons = nullptr; - } + for (unsigned int i = 0; i < this->n_equations; ++i) { + std::shared_ptr<NeuralNetworkSum> new_sum; + new_sum.reset(new NeuralNetworkSum()); + this->differential_equations.push_back(new_sum); + } - if( this->errors_functions_types ){ - delete this->errors_functions_types; - this->errors_functions_types = nullptr; - } + printf("done\n"); - if( this->errors_functions_data_sets ){ - delete this->errors_functions_data_sets; - this->errors_functions_data_sets = nullptr; } - if(this->differential_equations){ - for(auto nns: *this->differential_equations){ - delete nns; - } - delete this->differential_equations; - this->differential_equations = nullptr; - } + DESolver::~DESolver() { - for(auto nn: this->map_multiindices2nn){ - NeuralNetwork * n_to_delete = nn.second; - delete n_to_delete; } -} - //TODO more efficient representation of the functions (large portion of the structure is the same for all partial derivatives) -void DESolver::add_to_differential_equation( size_t equation_idx, MultiIndex &alpha, std::string expression_string ) { + void DESolver::add_to_differential_equation(size_t equation_idx, + MultiIndex& alpha, + std::string expression_string) { - if( equation_idx >= this->n_equations ){ - throw std::invalid_argument( "The provided equation index is too large!" ); - } + if (equation_idx >= this->n_equations) { + THROW_INVALID_ARGUMENT_ERROR("The provided equation index is too large!"); + } - size_t derivative_degree = alpha.get_degree( ); + size_t derivative_degree = alpha.get_degree(); - if( derivative_degree > 2 ){ - throw std::invalid_argument("The supplied multi-index represents partial derivative of order higher than 2! (Valid degree is at most 2)\n"); - } + if (derivative_degree > 2) { + THROW_INVALID_ARGUMENT_ERROR("The supplied multi-index represents partial derivative of order higher than 2! (Valid degree is at most 2)\n"); + } - /* retrieve indices of the variables according to which we perform the derivations ( applicable to any order, not just 2 or less )*/ - std::vector<size_t> partial_derivative_indices; - partial_derivative_indices.reserve(derivative_degree); - for( size_t i = 0; i < alpha.get_partial_derivatives_degrees()->size( ); ++i ){ - size_t degree = alpha.get_partial_derivatives_degrees()->at( i ); + /* retrieve indices of the variables according to which we perform the derivations ( applicable to any order, not just 2 or less )*/ + std::vector<size_t> partial_derivative_indices; + partial_derivative_indices.reserve(derivative_degree); + for (size_t i = 0; i < alpha.get_partial_derivatives_degrees()->size(); ++i) { + size_t degree = alpha.get_partial_derivatives_degrees()->at(i); - while( degree > 0 ){ + while (degree > 0) { - partial_derivative_indices.push_back( i ); - degree--; + partial_derivative_indices.push_back(i); + degree--; + } } - } - NeuralNetwork *new_net = nullptr; - /* we check whether the new multi-index is already present */ - if(map_multiindices2nn.find( alpha ) != map_multiindices2nn.end()){ - new_net = map_multiindices2nn[ alpha ]; - this->differential_equations->at( equation_idx )->add_network( new_net, expression_string ); - printf("\nAdding an existing partial derivative (multi-index: %s) to equation %d with coefficient %s\n", alpha.to_string().c_str(), (int)equation_idx, expression_string.c_str()); - return; - } - printf("\nAdding a new partial derivative (multi-index: %s) to equation %d with coefficient %s\n", alpha.to_string().c_str(), (int)equation_idx, expression_string.c_str()); - - /* we need to construct a new neural network */ - new_net = new NeuralNetwork( ); - new_net->set_parameter_space_pointers( *this->solution ); - - /* input neurons */ - std::vector<size_t> input_set( this->dim_i ); - size_t idx; - for( size_t i = 0; i < this->dim_i; ++i ){ - NeuronLinear *input_i = new NeuronLinear( ); //f(x) = x - idx = new_net->add_neuron( input_i, BIAS_TYPE::NO_BIAS ); - input_set[i] = idx; - } - new_net->specify_input_neurons( input_set ); - size_t first_input_neuron = input_set[0]; + std::shared_ptr<NeuralNetwork> new_net; + /* we check whether the new multi-index is already present */ + if (map_multiindices2nn.find(alpha) != map_multiindices2nn.end()) { + new_net = map_multiindices2nn[alpha]; + this->differential_equations.at(equation_idx)->add_network(new_net.get(), + expression_string); + printf("\nAdding an existing partial derivative (multi-index: %s) to equation %d with coefficient %s\n", + alpha.to_string().c_str(), + (int) equation_idx, + expression_string.c_str()); + return; + } + printf("\nAdding a new partial derivative (multi-index: %s) to equation %d with coefficient %s\n", + alpha.to_string().c_str(), + (int) equation_idx, + expression_string.c_str()); + + /* we need to construct a new neural network */ + new_net.reset(new NeuralNetwork()); + new_net->set_parameter_space_pointers(*this->solution); + + /* input neurons */ + std::vector<size_t> input_set(this->dim_i); + size_t idx; + for (size_t i = 0; i < this->dim_i; ++i) { + std::shared_ptr<Neuron> new_neuron; + new_neuron.reset(new NeuronLinear()); + idx = new_net->add_neuron(new_neuron, + BIAS_TYPE::NO_BIAS); + input_set[i] = idx; + } + new_net->specify_input_neurons(input_set); + size_t first_input_neuron = input_set[0]; - /* output neurons */ - std::vector<size_t> output_set( 1 ); - idx = new_net->add_neuron( new NeuronLinear( ), BIAS_TYPE::NO_BIAS );//f(x) = x - output_set[0] = idx; - new_net->specify_output_neurons( output_set ); - size_t first_output_neuron = idx; + /* output neurons */ + std::vector<size_t> output_set(1); + std::shared_ptr<Neuron> new_neuron; + new_neuron.reset(new NeuronLinear()); + idx = new_net->add_neuron(new_neuron, + BIAS_TYPE::NO_BIAS);//f(x) = x + output_set[0] = idx; + new_net->specify_output_neurons(output_set); + size_t first_output_neuron = idx; - /* the new partial derivative has degree of at least one */ - size_t first_inner_neuron = 0; - NeuronLogistic *n_ptr = nullptr, *n_ptr2 = nullptr; - for( size_t i = 0; i < this->dim_inn; ++i ){ - n_ptr = this->solution_inner_neurons->at( i ); + /* the new partial derivative has degree of at least one */ + size_t first_inner_neuron = 0; + std::shared_ptr<NeuronLogistic> n_ptr; + std::shared_ptr<NeuronLogistic> n_ptr2; + for (size_t i = 0; i < this->dim_inn; ++i) { + n_ptr = this->solution_inner_neurons.at(i); - for( size_t j = 0; j < derivative_degree; ++j){ - n_ptr2 = n_ptr; + for (size_t j = 0; j < derivative_degree; ++j) { + n_ptr2 = n_ptr; - n_ptr = n_ptr->get_derivative( ); + n_ptr = std::shared_ptr<NeuronLogistic>(n_ptr->get_derivative()); - if(j > 0){ - delete n_ptr2; - n_ptr2 = nullptr; } + idx = new_net->add_neuron(n_ptr, + BIAS_TYPE::EXISTING_BIAS, + this->solution->get_neuron_bias_index(i + this->dim_i + 1)); + if (i == 0) { + first_inner_neuron = idx; + } } - idx = new_net->add_neuron( n_ptr, BIAS_TYPE::EXISTING_BIAS, this->solution->get_neuron_bias_index( i + this->dim_i + 1 ) ); - if(i == 0){ - first_inner_neuron = idx; + /* identity neurons serving as a 'glue'*/ + size_t first_glue_neuron = idx + 1; + for (size_t i = 0; i < derivative_degree * this->dim_inn; ++i) { + std::shared_ptr<Neuron> new_neuron; + new_neuron.reset(new NeuronLinear()); + idx = new_net->add_neuron(new_neuron, + BIAS_TYPE::NO_BIAS); //f(x) = x } - } - - /* identity neurons serving as a 'glue'*/ - size_t first_glue_neuron = idx + 1; - for(size_t i = 0; i < derivative_degree * this->dim_inn; ++i){ - idx = new_net->add_neuron( new NeuronLinear( ), BIAS_TYPE::NO_BIAS ); //f(x) = x - } - /* connections between input neurons and inner neurons */ - size_t connection_idx = 0; - for(size_t i = 0; i < this->dim_i; ++i){ - for(size_t j = 0; j < this->dim_inn; ++j){ - printf(" adding a connection between input neuron %2d[%2d] and inner neuron %2d[%2d], connection index: %3d\n", (int)i, (int)(first_input_neuron + i), (int)j, (int)(first_inner_neuron + j), (int)connection_idx); - new_net->add_existing_connection(first_input_neuron + i, first_inner_neuron + j, connection_idx, *this->solution ); + /* connections between input neurons and inner neurons */ + size_t connection_idx = 0; + for (size_t i = 0; i < this->dim_i; ++i) { + for (size_t j = 0; j < this->dim_inn; ++j) { + printf(" adding a connection between input neuron %2d[%2d] and inner neuron %2d[%2d], connection index: %3d\n", + (int) i, + (int) (first_input_neuron + i), + (int) j, + (int) (first_inner_neuron + j), + (int) connection_idx); + new_net->add_existing_connection(first_input_neuron + i, + first_inner_neuron + j, + connection_idx, + *this->solution); + connection_idx++; + } + } + printf("----------------------------------------------------------------------------------------------------\n"); + + /* connections between inner neurons and the first set of 'glueing' neurons */ + for (size_t i = 0; i < this->dim_inn; ++i) { + printf(" adding a connection between inner neuron %2d[%2d] and glue neuron %2d[%2d], connection index: %3d\n", + (int) i, + (int) (first_inner_neuron + i), + (int) i, + (int) (first_glue_neuron + i), + (int) connection_idx); + new_net->add_existing_connection(first_inner_neuron + i, + first_glue_neuron + i, + connection_idx, + *this->solution); connection_idx++; } - } - printf("----------------------------------------------------------------------------------------------------\n"); - - /* connections between inner neurons and the first set of 'glueing' neurons */ - for(size_t i = 0; i < this->dim_inn; ++i){ - printf(" adding a connection between inner neuron %2d[%2d] and glue neuron %2d[%2d], connection index: %3d\n", (int)i, (int)(first_inner_neuron + i), (int)i, (int)(first_glue_neuron + i), (int)connection_idx); - new_net->add_existing_connection(first_inner_neuron + i, first_glue_neuron + i, connection_idx, *this->solution ); - connection_idx++; - } - printf("----------------------------------------------------------------------------------------------------\n"); + printf("----------------------------------------------------------------------------------------------------\n"); + + size_t pd_idx; + /* connections between glueing neurons */ + for (size_t di = 0; di < derivative_degree - 1; ++di) { + pd_idx = partial_derivative_indices[di];/* partial derivative index */ + for (size_t i = 0; i < this->dim_inn; ++i) { + connection_idx = pd_idx * this->dim_inn + i; + printf(" adding a connection between glue neuron %2d[%2d] and glue neuron %2d[%2d], connection index: %3d\n", + (int) (i + (di) * this->dim_inn), + (int) (first_glue_neuron + i + (di) * this->dim_inn), + (int) (i + (di + 1) * this->dim_inn), + (int) (first_glue_neuron + i + (di + 1) * this->dim_inn), + (int) connection_idx); + new_net->add_existing_connection(first_glue_neuron + i + (di) * this->dim_inn, + first_glue_neuron + i + (di + 1) * this->dim_inn, + connection_idx, + *this->solution); + } + } + printf("----------------------------------------------------------------------------------------------------\n"); - size_t pd_idx; - /* connections between glueing neurons */ - for(size_t di = 0; di < derivative_degree - 1; ++di){ - pd_idx = partial_derivative_indices[di];/* partial derivative index */ - for(size_t i = 0; i < this->dim_inn; ++i){ + /* connection between the layer of glueing neurons toward the output neuron */ + pd_idx = partial_derivative_indices[derivative_degree - 1];/* partial derivative index */ + for (size_t i = 0; i < this->dim_inn; ++i) { connection_idx = pd_idx * this->dim_inn + i; - printf(" adding a connection between glue neuron %2d[%2d] and glue neuron %2d[%2d], connection index: %3d\n", (int)(i + (di)*this->dim_inn), (int)(first_glue_neuron + i + (di)*this->dim_inn), (int)(i + (di + 1)*this->dim_inn), (int)(first_glue_neuron + i + (di + 1)*this->dim_inn), (int)connection_idx); - new_net->add_existing_connection(first_glue_neuron + i + (di)*this->dim_inn, first_glue_neuron + i + (di + 1)*this->dim_inn, connection_idx, *this->solution ); + printf(" adding a connection between glue neuron %2d[%2d] and output neuron %2d[%2d], connection index: %3d\n", + (int) (i + (derivative_degree - 1) * this->dim_inn), + (int) (first_glue_neuron + i + (derivative_degree - 1) * this->dim_inn), + 0, + (int) (first_output_neuron), + (int) connection_idx); + new_net->add_existing_connection(first_glue_neuron + i + (derivative_degree - 1) * this->dim_inn, + first_output_neuron, + connection_idx, + *this->solution); } - } - printf("----------------------------------------------------------------------------------------------------\n"); - - /* connection between the layer of glueing neurons toward the output neuron */ - pd_idx = partial_derivative_indices[derivative_degree - 1];/* partial derivative index */ - for(size_t i = 0; i < this->dim_inn; ++i){ - connection_idx = pd_idx * this->dim_inn + i; - printf(" adding a connection between glue neuron %2d[%2d] and output neuron %2d[%2d], connection index: %3d\n", (int)(i + (derivative_degree - 1)*this->dim_inn), (int)(first_glue_neuron + i + (derivative_degree - 1)*this->dim_inn), 0, (int)(first_output_neuron), (int)connection_idx); - new_net->add_existing_connection(first_glue_neuron + i + (derivative_degree - 1)*this->dim_inn, first_output_neuron, connection_idx, *this->solution ); + + map_multiindices2nn[alpha] = new_net; + + this->differential_equations.at(equation_idx)->add_network(new_net.get(), + expression_string); } - map_multiindices2nn[ alpha ] = new_net; - this->differential_equations->at( equation_idx )->add_network( new_net, expression_string ); -} + void DESolver::add_to_differential_equation(size_t equation_idx, + std::string expression_string) { + printf("Adding a known function '%s' to equation %d\n", + expression_string.c_str(), + (int) equation_idx); + this->differential_equations.at(equation_idx)->add_network(nullptr, + expression_string); -void DESolver::add_to_differential_equation( size_t equation_idx, std::string expression_string ) { + } - printf("Adding a known function '%s' to equation %d\n", expression_string.c_str( ), (int)equation_idx ); - this->differential_equations->at( equation_idx )->add_network( nullptr, expression_string ); -} + void DESolver::set_error_function(size_t equation_idx, + ErrorFunctionType F, + DataSet& conditions) { + if (equation_idx >= this->n_equations) { + THROW_INVALID_ARGUMENT_ERROR("The parameter 'equation_idx' is too large! It exceeds the number of differential equations."); + } + this->errors_functions_types.at(equation_idx) = F; -void DESolver::set_error_function(size_t equation_idx, ErrorFunctionType F, DataSet *conditions) { - if( equation_idx >= this->n_equations ){ - throw std::invalid_argument( "The parameter 'equation_idx' is too large! It exceeds the number of differential equations." ); + this->errors_functions_data_sets.at(equation_idx) = conditions; } - this->errors_functions_types->at( equation_idx ) = F; - this->errors_functions_data_sets->at( equation_idx ) = conditions; -} - //TODO instead use general method with Optimizer as its argument (create hierarchy of optimizers) -void DESolver::solve( ILearningMethods &learning_method ) { - - NeuralNetwork *nn; - DataSet *ds; - - /* DEFINITION OF THE GLOBAL ERROR FUNCTION */ - ErrorSum total_error; - for(size_t i = 0; i < this->n_equations; ++i ){ - nn = this->differential_equations->at( i ); - ds = this->errors_functions_data_sets->at( i ); - if( ds ){ - if( this->errors_functions_types->at( i ) == ErrorFunctionType::ErrorFuncMSE ){ - total_error.add_error_function( new MSE( nn, ds ), 1.0 ); - } - else{ + void DESolver::solve(LearningMethod& learning_method) { + + + /* DEFINITION OF THE GLOBAL ERROR FUNCTION */ + ErrorSum total_error; + for (size_t i = 0; i < this->n_equations; ++i) { + if (this->errors_functions_types.at(i) == ErrorFunctionType::ErrorFuncMSE) { + total_error.add_error_function(new MSE(this->differential_equations.at(i).get(), + &this->errors_functions_data_sets.at(i)), + 1.0); + } else { //default - total_error.add_error_function( new MSE( nn, ds ), 1.0 ); + total_error.add_error_function(new MSE(this->differential_equations.at(i).get(), + &this->errors_functions_data_sets.at(i)), + 1.0); } } - else{ - total_error.add_error_function( nullptr, 1.0 ); - } - } - this->solution->randomize_weights(); - this->solution->randomize_biases(); + printf("error before optimization: %f\n", + total_error.eval(nullptr)); - learning_method.optimize( total_error ); + learning_method.optimize(total_error); + std::vector<double> params = *learning_method.get_parameters(); + this->solution->copy_parameter_space(¶ms); - this->solution->copy_parameter_space( learning_method.get_parameters( ) ); -} + printf("error after optimization: %f\n", + total_error.eval(nullptr)); + } -NeuralNetwork* DESolver::get_solution( MultiIndex &alpha ) { - return this->map_multiindices2nn[ alpha ]; -} + void DESolver::randomize_parameters() { -double DESolver::eval_equation( size_t equation_idx, std::vector<double> *weight_and_biases, std::vector<double> &input ) { - std::vector<double> output(1); + MultiIndex alpha(this->dim_i); + this->map_multiindices2nn[alpha]->randomize_parameters(); - this->differential_equations->at( equation_idx )->eval_single( input, output, weight_and_biases ); + } + + NeuralNetwork* DESolver::get_solution(MultiIndex& alpha) { + return this->map_multiindices2nn[alpha].get(); + } -// printf("Input: "); -// for( auto e: input ){ -// printf("%f, ", e); -// } -// printf("\nOutput: "); -// for( auto e: output ){ -// printf("%f, ", e); -// } -// printf("\n"); + double + DESolver::eval_equation(size_t equation_idx, + std::shared_ptr<std::vector<double>> weight_and_biases, + std::vector<double>& input) { + std::vector<double> output(1); - return output[0]; -} + this->differential_equations.at(equation_idx)->eval_single(input, + output, + weight_and_biases.get()); -double DESolver::eval_total_error(std::vector<double> &weights_and_biases) { - NeuralNetwork *nn; - DataSet *ds; + return output[0]; + } + + double DESolver::eval_total_error(std::vector<double>& weights_and_biases) { - /* DEFINITION OF THE PARTIAL ERROR FUNCTIONS */ - std::vector<ErrorFunction*> error_functions( this->n_equations ); - for(size_t i = 0; i < this->n_equations; ++i ){ - nn = this->differential_equations->at( i ); - ds = this->errors_functions_data_sets->at( i ); - if( this->errors_functions_types->at( i ) == ErrorFunctionType::ErrorFuncMSE ){ - error_functions[i] = new MSE( nn, ds ); + ///* DEFINITION OF THE PARTIAL ERROR FUNCTIONS */ + std::vector<ErrorFunction*> error_functions(this->n_equations); + for (size_t i = 0; i < this->n_equations; ++i) { + if (this->errors_functions_types.at(i) == ErrorFunctionType::ErrorFuncMSE) { + error_functions[i] = new MSE(this->differential_equations.at(i).get(), + &this->errors_functions_data_sets.at(i)); + } else { + //default + error_functions[i] = new MSE(this->differential_equations.at(i).get(), + &this->errors_functions_data_sets.at(i)); + } } - else{ - //default - error_functions[i] = new MSE( nn, ds ); + + /* DEFINITION OF THE GLOBAL ERROR FUNCTION */ + ErrorSum total_error; + for (size_t i = 0; i < this->n_equations; ++i) { + total_error.add_error_function(error_functions[i], + 1.0); } - } - /* DEFINITION OF THE GLOBAL ERROR FUNCTION */ - ErrorSum total_error; - for(size_t i = 0; i < this->n_equations; ++i ) { - total_error.add_error_function( error_functions[i], 1.0 ); + //return total_error.eval(&weights_and_biases); + return 64; } - return total_error.eval( &weights_and_biases ); } \ No newline at end of file diff --git a/src/Solvers/DESolver.h b/src/Solvers/DESolver.h index 68a3f978c09143b05feaa7c8287dbb5ff4173961..b1f4781c8cafe19b5f60d1949a0b36ea35c12ec3 100644 --- a/src/Solvers/DESolver.h +++ b/src/Solvers/DESolver.h @@ -5,17 +5,17 @@ * @date 22.7.18 - */ - //TODO incorporate uncertainities as coefficients in NeuralNetworkSum or ErrorSum - //TODO add support for multiple unknown functions to be found - //TODO add export capability? - //TODO restructure of the learning methods to have a common parent to be used as a parameter in the solvers +//TODO incorporate uncertainities as coefficients in NeuralNetworkSum or ErrorSum +//TODO add support for multiple unknown functions to be found +//TODO add export capability? +//TODO restructure of the learning methods to have a common parent to be used as a parameter in the solvers #ifndef INC_4NEURO_PDESOLVER_H #define INC_4NEURO_PDESOLVER_H -#include "../settings.h" - #include <map> + +#include "../settings.h" #include "../DataSet/DataSet.h" #include "../Network/NeuralNetwork.h" #include "../Network/NeuralNetworkSum.h" @@ -23,146 +23,167 @@ #include "../Neuron/NeuronLinear.h" #include "../Neuron/NeuronLogistic.h" #include "../LearningMethods/ParticleSwarm.h" +#include "../LearningMethods/GradientDescent.h" + +namespace lib4neuro { /** * class representing a multi-index of partial derivatives */ -class MultiIndex{ -private: - /** - * total number of variables - */ - size_t dim; - - /** - * a vector containing degrees of partial derivatives with respect to each variable - */ - std::vector<size_t> partial_derivatives_degrees; - -public: - /** - * - * @param dimension - */ - LIB4NEURO_API MultiIndex(size_t dimension); - - - /** - * - * @param index - * @param value - */ - LIB4NEURO_API void set_partial_derivative(size_t index, size_t value); - - /** - * - * @return - */ - LIB4NEURO_API std::vector<size_t>* get_partial_derivatives_degrees( ) ; - - - /** - * - * @param rhs - * @return - */ - LIB4NEURO_API bool operator <(const MultiIndex& rhs) const; - - /** - * - * @return - */ - LIB4NEURO_API std::string to_string( ) const ; - - /** - * - * @return - */ - LIB4NEURO_API size_t get_degree( ) const ; -}; - - - -class DESolver { -private: - - /* Mapping between used multiindices of partial derivatices and already defined NNs (in order to not define - * the same NN multiple times )*/ - std::map<MultiIndex, NeuralNetwork*> map_multiindices2nn; - - /* A list of the differential equations */ - std::vector<NeuralNetworkSum*> * differential_equations = nullptr; - - /* Error functions for differential equations */ - std::vector<ErrorFunctionType> * errors_functions_types = nullptr; - std::vector<DataSet*> * errors_functions_data_sets = nullptr; - - /* NN as the unknown function */ - NeuralNetwork * solution = nullptr; - - /* auxilliary variables */ - std::vector<NeuronLogistic*> *solution_inner_neurons = nullptr; - size_t dim_i = 0, dim_inn = 0, n_equations = 0; - -public: - /** - * The attempted solution will contain 'm' inner neurons between the input neurons and the output neuron - * @param n_equations - * @param n_inputs - * @param m - */ - LIB4NEURO_API DESolver( size_t n_equations, size_t n_inputs, size_t m ); - - /** - * default destructor - */ - LIB4NEURO_API ~DESolver( ); - - /** - * Adds a new summand multiplied by 'beta' into the 'equation_idx'-th differential equation - * @param equation_idx - * @param alpha - * @param beta - */ - LIB4NEURO_API void add_to_differential_equation( size_t equation_idx, MultiIndex &alpha, std::string expression_string ); - - - /** - * Adds a known function to the equation - * @param equation_idx - * @param expression_string - */ - LIB4NEURO_API void add_to_differential_equation( size_t equation_idx, std::string expression_string ); - - /** - * Sets the error function for the differential equation with the corresponding index - * @param equation_idx - * @param F - * @param conditions - */ - LIB4NEURO_API void set_error_function(size_t equation_idx, ErrorFunctionType F, DataSet *conditions); - - - - LIB4NEURO_API void solve( ILearningMethods &learning_method ); - - /** - * returns the pointer to the object representing the given partial derivative of the solution - * @return - */ - LIB4NEURO_API NeuralNetwork* get_solution( MultiIndex &alpha ); - - /** - * For testing purposes only - */ - LIB4NEURO_API double eval_equation( size_t equation_idx, std::vector<double> *weights_and_biases, std::vector<double> &input ); - - /** - * For testing purposes only - * @return - */ - LIB4NEURO_API double eval_total_error( std::vector<double> &weights_and_biases ); -}; - - + class MultiIndex { + private: + /** + * total number of variables + */ + size_t dim; + + /** + * a vector containing degrees of partial derivatives with respect to each variable + */ + std::vector<size_t> partial_derivatives_degrees; + + public: + /** + * + * @param dimension + */ + LIB4NEURO_API MultiIndex(size_t dimension); + + + /** + * + * @param index + * @param value + */ + LIB4NEURO_API void set_partial_derivative(size_t index, + size_t value); + + /** + * + * @return + */ + LIB4NEURO_API std::vector<size_t>* get_partial_derivatives_degrees(); + + + /** + * + * @param rhs + * @return + */ + LIB4NEURO_API bool operator<(const MultiIndex& rhs) const; + + /** + * + * @return + */ + LIB4NEURO_API std::string to_string() const; + + /** + * + * @return + */ + LIB4NEURO_API size_t get_degree() const; + }; + + + class DESolver { + private: + + /* Mapping between used multiindices of partial derivatices and already defined NNs (in order to not define + * the same NN multiple times )*/ + std::map<MultiIndex, std::shared_ptr<NeuralNetwork>> map_multiindices2nn; + + /* A list of the differential equations */ + std::vector<std::shared_ptr<NeuralNetworkSum>> differential_equations; // = nullptr; + + /* Error functions for differential equations */ + std::vector<ErrorFunctionType> errors_functions_types; // = nullptr; + std::vector<DataSet> errors_functions_data_sets; // = nullptr; + + /* NN as the unknown function */ + std::shared_ptr<NeuralNetwork> solution = std::make_shared<NeuralNetwork>(NeuralNetwork()); + + /* auxilliary variables */ + std::vector<std::shared_ptr<NeuronLogistic>> solution_inner_neurons; // = nullptr; + size_t dim_i = 0, dim_inn = 0, n_equations = 0; + + public: + /** + * The attempted solution will contain 'm' inner neurons between the input neurons and the output neuron + * @param n_equations + * @param n_inputs + * @param m + */ + LIB4NEURO_API DESolver(size_t n_equations, + size_t n_inputs, + size_t m); + + /** + * default destructor + */ + LIB4NEURO_API ~DESolver(); + + /** + * Adds a new summand multiplied by 'beta' into the 'equation_idx'-th differential equation + * @param equation_idx + * @param alpha + * @param beta + */ + LIB4NEURO_API void + add_to_differential_equation(size_t equation_idx, + MultiIndex& alpha, + std::string expression_string); + + + /** + * Adds a known function to the equation + * @param equation_idx + * @param expression_string + */ + LIB4NEURO_API void add_to_differential_equation(size_t equation_idx, + std::string expression_string); + + /** + * Sets the error function for the differential equation with the corresponding index + * @param equation_idx + * @param F + * @param conditions + */ + LIB4NEURO_API void set_error_function(size_t equation_idx, + ErrorFunctionType F, + DataSet& conditions); + + /** + * + * @param learning_method + */ + LIB4NEURO_API void solve(LearningMethod& learning_method); + + /** + * + */ + LIB4NEURO_API void randomize_parameters(); + + /** + * returns the pointer to the object representing the given partial derivative of the solution + * @return + */ + LIB4NEURO_API NeuralNetwork* get_solution(MultiIndex& alpha); + + /** + * For testing purposes only + */ + LIB4NEURO_API double + eval_equation(size_t equation_idx, + std::shared_ptr<std::vector<double>> weights_and_biases, + std::vector<double>& input); + + /** + * For testing purposes only + * @return + */ + LIB4NEURO_API double eval_total_error(std::vector<double>& weights_and_biases); + }; + +} #endif //INC_4NEURO_PDESOLVER_H diff --git a/src/boost_test_lib_dummy.cpp b/src/boost_test_lib_dummy.cpp index 38992727ad6b9faae7f56db4c68e411528906c9d..aca82fe70c78662804c243759fc74dc3329755ac 100644 --- a/src/boost_test_lib_dummy.cpp +++ b/src/boost_test_lib_dummy.cpp @@ -1,10 +1,7 @@ -// -// Created by David on 11.07.2018. -// +#ifndef BOOST_TEST_MODULE +#define BOOST_TEST_MODULE unit_test +#include <boost/test/included/unit_test.hpp> -#ifndef BOOST_TEST_MODULE - #define BOOST_TEST_MODULE unit_test - #include <boost/test/included/unit_test.hpp> #endif diff --git a/src/constants.h b/src/constants.h index 8c2674a51a351abbe3a8d2a8b52e46ca213f7a96..f8bfa80cbf5afadafd0836a14a5d287e8a3ec041 100644 --- a/src/constants.h +++ b/src/constants.h @@ -1,11 +1,11 @@ -// -// Created by fluffymoo on 11.6.18. -// #ifndef INC_4NEURO_CONSTANTS_H #define INC_4NEURO_CONSTANTS_H -#define E 2.7182818284590 -#define PI 3.14159265358979323846 +namespace lib4neuro{ + const double E = 2.7182818284590; + const double PI = 3.14159265358979323846; +} #endif //INC_4NEURO_CONSTANTS_H + diff --git a/src/examples/CMakeLists.txt b/src/examples/CMakeLists.txt index 6706c0a05e43a53fad7044f2c54d72bca19b8457..1a7fae388435cd5ca1a3ed7a000925c6fe68e739 100644 --- a/src/examples/CMakeLists.txt +++ b/src/examples/CMakeLists.txt @@ -2,36 +2,40 @@ # EXAMPLES # ############ -add_executable(seminar seminar.cpp) -target_link_libraries(seminar lib4neuro) +ADD_EXECUTABLE(seminar seminar.cpp) +TARGET_LINK_LIBRARIES(seminar PUBLIC lib4neuro) -add_executable(test_cases main.cpp) -target_link_libraries(test_cases PUBLIC lib4neuro) +ADD_EXECUTABLE(dev_sandbox dev_sandbox.cpp) +TARGET_LINK_LIBRARIES(dev_sandbox PUBLIC lib4neuro) -add_executable(net_test_1 net_test_1.cpp) -target_link_libraries(net_test_1 PUBLIC lib4neuro) +ADD_EXECUTABLE(net_test_1 net_test_1.cpp) +TARGET_LINK_LIBRARIES(net_test_1 PUBLIC lib4neuro) -add_executable(net_test_2 net_test_2.cpp) -target_link_libraries(net_test_2 PUBLIC lib4neuro) +ADD_EXECUTABLE(net_test_2 net_test_2.cpp) +TARGET_LINK_LIBRARIES(net_test_2 PUBLIC lib4neuro) -add_executable(net_test_3 net_test_3.cpp) -target_link_libraries(net_test_3 PUBLIC lib4neuro) +ADD_EXECUTABLE(net_test_3 net_test_3.cpp) +TARGET_LINK_LIBRARIES(net_test_3 PUBLIC lib4neuro) -add_executable(net_test_ode_1 net_test_ode_1.cpp) -target_link_libraries(net_test_ode_1 PUBLIC lib4neuro) +ADD_EXECUTABLE(net_test_ode_1 net_test_ode_1.cpp) +TARGET_LINK_LIBRARIES(net_test_ode_1 PUBLIC lib4neuro) -add_executable(net_test_pde_1 net_test_pde_1.cpp) -target_link_libraries(net_test_pde_1 PUBLIC lib4neuro) +ADD_EXECUTABLE(net_test_pde_1 net_test_pde_1.cpp) +TARGET_LINK_LIBRARIES(net_test_pde_1 PUBLIC lib4neuro) -add_executable(network_serialization network_serialization.cpp) -target_link_libraries(network_serialization PUBLIC lib4neuro) +ADD_EXECUTABLE(network_serialization network_serialization.cpp) +TARGET_LINK_LIBRARIES(network_serialization PUBLIC lib4neuro) -add_executable(test_harmonic_oscilator net_test_harmonic_oscilator.cpp) -target_link_libraries(test_harmonic_oscilator PUBLIC lib4neuro) +ADD_EXECUTABLE(test_harmonic_oscilator net_test_harmonic_oscilator.cpp) +TARGET_LINK_LIBRARIES(test_harmonic_oscilator PUBLIC lib4neuro) +ADD_EXECUTABLE(x2_fitting x2_fitting.cpp) +TARGET_LINK_LIBRARIES(x2_fitting PUBLIC lib4neuro) -set_target_properties( - test_cases +SET(EXAMPLES_OUTPUT_DIR ${PROJECT_BINARY_DIR}/examples) + +SET_TARGET_PROPERTIES( + dev_sandbox net_test_1 net_test_2 net_test_3 @@ -40,9 +44,71 @@ set_target_properties( network_serialization test_harmonic_oscilator seminar + x2_fitting PROPERTIES - ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib/" - LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib" - RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bin/examples" + ARCHIVE_OUTPUT_DIRECTORY $<1:${EXAMPLES_OUTPUT_DIR}> + LIBRARY_OUTPUT_DIRECTORY $<1:${EXAMPLES_OUTPUT_DIR}> + RUNTIME_OUTPUT_DIRECTORY $<1:${EXAMPLES_OUTPUT_DIR}> +) + +TARGET_INCLUDE_DIRECTORIES( + dev_sandbox + PRIVATE + ${ROOT_DIR}/include +) + +TARGET_INCLUDE_DIRECTORIES( + net_test_1 + PRIVATE + ${ROOT_DIR}/include +) + +TARGET_INCLUDE_DIRECTORIES( + net_test_2 + PRIVATE + ${ROOT_DIR}/include +) + +TARGET_INCLUDE_DIRECTORIES( + net_test_3 + PRIVATE + ${ROOT_DIR}/include + ${Boost_INCLUDE_DIRS} +) + +TARGET_INCLUDE_DIRECTORIES( + net_test_ode_1 + PRIVATE + ${ROOT_DIR}/include +) + +TARGET_INCLUDE_DIRECTORIES( + net_test_pde_1 + PRIVATE + ${ROOT_DIR}/include +) + +TARGET_INCLUDE_DIRECTORIES( + network_serialization + PRIVATE + ${ROOT_DIR}/include +) + +TARGET_INCLUDE_DIRECTORIES( + test_harmonic_oscilator + PRIVATE + ${ROOT_DIR}/include +) + +TARGET_INCLUDE_DIRECTORIES( + seminar + PRIVATE + ${ROOT_DIR}/include +) + +TARGET_INCLUDE_DIRECTORIES( + x2_fitting + PRIVATE + ${ROOT_DIR}/include ) diff --git a/src/examples/dev_sandbox.cpp b/src/examples/dev_sandbox.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fe257beb44fe568746e3eb0c8693be155281221a --- /dev/null +++ b/src/examples/dev_sandbox.cpp @@ -0,0 +1,41 @@ +/** + * This file serves for testing of various examples, have fun! + * + * @author Michal KravÄenko + * @date 14.6.18 - + */ + +#include <iostream> +#include <cstdio> +#include <fstream> +#include <vector> +#include <utility> +#include <algorithm> +#include <assert.h> + +#include "4neuro.h" + +int main(int argc, + char** argv) { + + std::vector<std::pair<std::vector<double>, std::vector<double>>> data_vec; + std::vector<double> inp, out; + + for (int i = 0; i < 3; i++) { + inp.push_back(i); + out.push_back(i + 4); + } + + data_vec.emplace_back(std::make_pair(inp, + out)); + + lib4neuro::DataSet DataSet(&data_vec); + int elements = DataSet.get_n_elements(); + std::string filename = "/home/martin/4Neuro/build/unit-tests/testDataSet"; + DataSet.store_text(filename); + + //Test of correct file creations + lib4neuro::DataSet newDataSet(filename); + + return 0; +} diff --git a/src/examples/main.cpp b/src/examples/main.cpp deleted file mode 100644 index c2d3a38cf22ae01b7795437630e918a7fa605c10..0000000000000000000000000000000000000000 --- a/src/examples/main.cpp +++ /dev/null @@ -1,24 +0,0 @@ -/** - * This file serves for testing of various examples, have fun! - * - * @author Michal KravÄenko - * @date 14.6.18 - - */ - -#include <iostream> -#include <cstdio> -#include <fstream> -#include <vector> -#include <utility> -#include <algorithm> - -#include "4neuro.h" - - -int main(int argc, char** argv){ - MSG_INFO("INFO MESSAGE!"); - - MSG_DEBUG("DEBUG MESSAGE"); - - return 0; -} diff --git a/src/examples/net_test_1.cpp b/src/examples/net_test_1.cpp index 0cd91a097b7a6bf05167a7d71e8d71153fd5ca71..0816d644912b1d24f2069d4704ac1dbde5f6a96c 100644 --- a/src/examples/net_test_1.cpp +++ b/src/examples/net_test_1.cpp @@ -2,60 +2,135 @@ * Basic example using particle swarm method to train the network */ -// -// Created by martin on 7/16/18. -// #include <vector> +#include <iostream> #include "4neuro.h" +void optimize_via_particle_swarm(l4n::NeuralNetwork& net, + l4n::ErrorFunction& ef) { + + /* TRAINING METHOD SETUP */ + std::vector<double> domain_bounds(2 * (net.get_n_weights() + net.get_n_biases())); + + for (size_t i = 0; i < domain_bounds.size() / 2; ++i) { + domain_bounds[2 * i] = -10; + domain_bounds[2 * i + 1] = 10; + } + + double c1 = 1.7; + double c2 = 1.7; + double w = 0.7; + size_t n_particles = 50; + size_t iter_max = 10; + + /* if the maximal velocity from the previous step is less than 'gamma' times the current maximal velocity, then one + * terminating criterion is met */ + double gamma = 0.5; + + /* if 'delta' times 'n' particles are in the centroid neighborhood given by the radius 'epsilon', then the second + * terminating criterion is met ('n' is the total number of particles) */ + double epsilon = 0.02; + double delta = 0.7; + + l4n::ParticleSwarm swarm_01( + &domain_bounds, + c1, + c2, + w, + gamma, + epsilon, + delta, + n_particles, + iter_max + ); + swarm_01.optimize(ef); + + net.copy_parameter_space(swarm_01.get_parameters()); + + /* ERROR CALCULATION */ + std::cout << "Run finished! Error of the network[Particle swarm]: " << ef.eval(nullptr) << std::endl; + std::cout + << "***********************************************************************************************************************" + << std::endl; +} + +void optimize_via_gradient_descent(l4n::NeuralNetwork& net, + l4n::ErrorFunction& ef) { + + std::cout + << "***********************************************************************************************************************" + << std::endl; + l4n::GradientDescentBB gd(1e-6, + 1000); + + gd.optimize(ef); + + net.copy_parameter_space(gd.get_parameters()); + + /* ERROR CALCULATION */ + std::cout << "Run finished! Error of the network[Gradient descent]: " << ef.eval(nullptr) << std::endl; +} + int main() { - std::cout << "Running lib4neuro example 1: Basic use of the particle swarm method to train a simple network with few linear neurons" << std::endl; - std::cout << "***********************************************************************************************************************" <<std::endl; + std::cout + << "Running lib4neuro example 1: Basic use of the particle swarm or gradient method to train a simple network with few linear neurons" + << std::endl; + std::cout + << "***********************************************************************************************************************" + << std::endl; std::cout << "The code attempts to find an approximate solution to the system of equations below:" << std::endl; std::cout << "0 * w1 + 1 * w2 = 0.50" << std::endl; std::cout << "1 * w1 + 0.5*w2 = 0.75" << std::endl; - std::cout << "***********************************************************************************************************************" <<std::endl; + std::cout + << "***********************************************************************************************************************" + << std::endl; /* TRAIN DATA DEFINITION */ std::vector<std::pair<std::vector<double>, std::vector<double>>> data_vec; - std::vector<double> inp, out; + std::vector<double> inp, out; inp = {0, 1}; out = {0.5}; - data_vec.emplace_back(std::make_pair(inp, out)); + data_vec.emplace_back(std::make_pair(inp, + out)); inp = {1, 0.5}; out = {0.75}; - data_vec.emplace_back(std::make_pair(inp, out)); + data_vec.emplace_back(std::make_pair(inp, + out)); - DataSet ds(&data_vec); + l4n::DataSet ds(&data_vec); /* NETWORK DEFINITION */ - NeuralNetwork net; + l4n::NeuralNetwork net; /* Input neurons */ - NeuronLinear *i1 = new NeuronLinear( ); //f(x) = x - NeuronLinear *i2 = new NeuronLinear( ); //f(x) = x + std::shared_ptr<l4n::NeuronLinear> i1 = std::make_shared<l4n::NeuronLinear>(); + std::shared_ptr<l4n::NeuronLinear> i2 = std::make_shared<l4n::NeuronLinear>(); /* Output neuron */ - NeuronLinear *o1 = new NeuronLinear( ); //f(x) = x - - + std::shared_ptr<l4n::NeuronLinear> o1 = std::make_shared<l4n::NeuronLinear>(); /* Adding neurons to the net */ - size_t idx1 = net.add_neuron(i1, BIAS_TYPE::NO_BIAS); - size_t idx2 = net.add_neuron(i2, BIAS_TYPE::NO_BIAS); - size_t idx3 = net.add_neuron(o1, BIAS_TYPE::NO_BIAS); -// + size_t idx1 = net.add_neuron(i1, + l4n::BIAS_TYPE::NO_BIAS); + size_t idx2 = net.add_neuron(i2, + l4n::BIAS_TYPE::NO_BIAS); + size_t idx3 = net.add_neuron(o1, + l4n::BIAS_TYPE::NO_BIAS); /* Adding connections */ - net.add_connection_simple(idx1, idx3, SIMPLE_CONNECTION_TYPE::NEXT_WEIGHT); - net.add_connection_simple(idx2, idx3, SIMPLE_CONNECTION_TYPE::NEXT_WEIGHT); + net.add_connection_simple(idx1, + idx3, + l4n::SIMPLE_CONNECTION_TYPE::NEXT_WEIGHT); + net.add_connection_simple(idx2, + idx3, + l4n::SIMPLE_CONNECTION_TYPE::NEXT_WEIGHT); + - //net.randomize_weights(); /* specification of the input/output neurons */ std::vector<size_t> net_input_neurons_indices(2); @@ -67,65 +142,26 @@ int main() { net.specify_input_neurons(net_input_neurons_indices); net.specify_output_neurons(net_output_neurons_indices); - /* ERROR FUNCTION SPECIFICATION */ - MSE mse(&net, &ds); - - /* TRAINING METHOD SETUP */ - std::vector<double> domain_bounds(2 * (net.get_n_weights() + net.get_n_biases())); - - for(size_t i = 0; i < domain_bounds.size() / 2; ++i){ - domain_bounds[2 * i] = -10; - domain_bounds[2 * i + 1] = 10; - } - - double c1 = 1.7; - double c2 = 1.7; - double w = 0.7; - size_t n_particles = 50; - size_t iter_max = 1000; - /* if the maximal velocity from the previous step is less than 'gamma' times the current maximal velocity, then one - * terminating criterion is met */ - double gamma = 0.5; - - /* if 'delta' times 'n' particles are in the centroid neighborhood given by the radius 'epsilon', then the second - * terminating criterion is met ('n' is the total number of particles) */ - double epsilon = 0.02; - double delta = 0.7; - - ParticleSwarm swarm_01( - &domain_bounds, - c1, - c2, - w, - gamma, - epsilon, - delta, - n_particles, - iter_max - ); - swarm_01.optimize( mse ); - - std::vector<double> *parameters = swarm_01.get_parameters(); - net.copy_parameter_space(parameters); + /* ERROR FUNCTION SPECIFICATION */ + l4n::MSE mse(&net, + &ds); - printf("w1 = %10.7f\n", parameters->at( 0 )); - printf("w2 = %10.7f\n", parameters->at( 1 )); - std::cout << "***********************************************************************************************************************" <<std::endl; - /* ERROR CALCULATION */ - double error = 0.0; - inp = {0, 1}; - net.eval_single( inp, out ); - error += (0.5 - out[0]) * (0.5 - out[0]); - std::cout << "x = (0, 1), expected output: 0.50, real output: " << out[0] << std::endl; + /* PARTICLE SWARM LEARNING */ + net.randomize_parameters(); + optimize_via_particle_swarm(net, + mse); - inp = {1, 0.5}; - net.eval_single( inp, out ); - error += (0.75 - out[0]) * (0.75 - out[0]); - std::cout << "x = (1, 0.5), expected output: 0.75, real output: " << out[0] << std::endl; - std::cout << "Run finished! Error of the network: " << 0.5 * error << std::endl; + /* GRADIENT DESCENT LEARNING */ + net.randomize_parameters(); + optimize_via_gradient_descent(net, + mse); + /* Normalize data to prevent 'nan' results */ + net.randomize_parameters(); + optimize_via_gradient_descent(net, + mse); return 0; } diff --git a/src/examples/net_test_2.cpp b/src/examples/net_test_2.cpp index 72f1dfd769c7cacba59440c25b8926f23b75876d..345bbb88818b8dc82df3dd2b980bee77cd7995f7 100644 --- a/src/examples/net_test_2.cpp +++ b/src/examples/net_test_2.cpp @@ -2,68 +2,146 @@ * Example of a neural network with reused edge weights */ -// -// Created by Michal on 7/17/18. -// #include <vector> #include "4neuro.h" +void optimize_via_particle_swarm(l4n::NeuralNetwork& net, + l4n::ErrorFunction& ef) { + + /* TRAINING METHOD SETUP */ + std::vector<double> domain_bounds(2 * (net.get_n_weights() + net.get_n_biases())); + + for (size_t i = 0; i < domain_bounds.size() / 2; ++i) { + domain_bounds[2 * i] = -10; + domain_bounds[2 * i + 1] = 10; + } + + double c1 = 1.7; + double c2 = 1.7; + double w = 0.7; + size_t n_particles = 50; + size_t iter_max = 10; + + /* if the maximal velocity from the previous step is less than 'gamma' times the current maximal velocity, then one + * terminating criterion is met */ + double gamma = 0.5; + + /* if 'delta' times 'n' particles are in the centroid neighborhood given by the radius 'epsilon', then the second + * terminating criterion is met ('n' is the total number of particles) */ + double epsilon = 0.02; + double delta = 0.7; + + l4n::ParticleSwarm swarm_01( + &domain_bounds, + c1, + c2, + w, + gamma, + epsilon, + delta, + n_particles, + iter_max + ); + swarm_01.optimize(ef); + + net.copy_parameter_space(swarm_01.get_parameters()); + + std::cout << "Run finished! Error of the network[Particle swarm]: " << ef.eval(nullptr) << std::endl; + std::cout + << "***********************************************************************************************************************" + << std::endl; +} + +void optimize_via_gradient_descent(l4n::NeuralNetwork& net, + l4n::ErrorFunction& ef) { + + l4n::GradientDescentBB gd(1e-6, + 1000); + + gd.optimize(ef); + + net.copy_parameter_space(gd.get_parameters()); + + /* ERROR CALCULATION */ + std::cout << "Run finished! Error of the network[Gradient descent]: " << ef.eval(nullptr) << std::endl; + std::cout + << "***********************************************************************************************************************" + << std::endl; +} + int main() { - std::cout << "Running lib4neuro example 2: Basic use of the particle swarm method to train a network with five linear neurons and repeating edge weights" << std::endl; - std::cout << "********************************************************************************************************************************************" <<std::endl; + std::cout + << "Running lib4neuro example 2: Basic use of the particle swarm method to train a network with five linear neurons and repeating edge weights" + << std::endl; + std::cout + << "********************************************************************************************************************************************" + << std::endl; std::cout << "The code attempts to find an approximate solution to the system of equations below:" << std::endl; std::cout << " 0 * w1 + 1 * w2 = 0.50 + b1" << std::endl; std::cout << " 1 * w1 + 0.5*w2 = 0.75 + b1" << std::endl; std::cout << "(1.25 + b2) * w2 = 0.63 + b3" << std::endl; - std::cout << "***********************************************************************************************************************" <<std::endl; + std::cout + << "***********************************************************************************************************************" + << std::endl; /* TRAIN DATA DEFINITION */ std::vector<std::pair<std::vector<double>, std::vector<double>>> data_vec; - std::vector<double> inp, out; + std::vector<double> inp, out; inp = {0, 1, 0}; out = {0.5, 0}; - data_vec.emplace_back(std::make_pair(inp, out)); + data_vec.emplace_back(std::make_pair(inp, + out)); inp = {1, 0.5, 0}; out = {0.75, 0}; - data_vec.emplace_back(std::make_pair(inp, out)); + data_vec.emplace_back(std::make_pair(inp, + out)); inp = {0, 0, 1.25}; out = {0, 0.63}; - data_vec.emplace_back(std::make_pair(inp, out)); - DataSet ds(&data_vec); + data_vec.emplace_back(std::make_pair(inp, + out)); + l4n::DataSet ds(&data_vec); /* NETWORK DEFINITION */ - NeuralNetwork net; + l4n::NeuralNetwork net; /* Input neurons */ - NeuronLinear *i1 = new NeuronLinear( ); //f(x) = x - NeuronLinear *i2 = new NeuronLinear( ); //f(x) = x + std::shared_ptr<l4n::NeuronLinear> i1 = std::make_shared<l4n::NeuronLinear>(); + std::shared_ptr<l4n::NeuronLinear> i2 = std::make_shared<l4n::NeuronLinear>(); - NeuronLinear *i3 = new NeuronLinear( ); //f(x) = x + std::shared_ptr<l4n::NeuronLinear> i3 = std::make_shared<l4n::NeuronLinear>(); /* Output neurons */ - NeuronLinear *o1 = new NeuronLinear( ); //f(x) = x - NeuronLinear *o2 = new NeuronLinear( ); //f(x) = x - - + std::shared_ptr<l4n::NeuronLinear> o1 = std::make_shared<l4n::NeuronLinear>(); + std::shared_ptr<l4n::NeuronLinear> o2 = std::make_shared<l4n::NeuronLinear>(); /* Adding neurons to the nets */ - size_t idx1 = net.add_neuron(i1, BIAS_TYPE::NO_BIAS); - size_t idx2 = net.add_neuron(i2, BIAS_TYPE::NO_BIAS); - size_t idx3 = net.add_neuron(o1, BIAS_TYPE::NEXT_BIAS); - size_t idx4 = net.add_neuron(i3, BIAS_TYPE::NEXT_BIAS); - size_t idx5 = net.add_neuron(o2, BIAS_TYPE::NEXT_BIAS); + size_t idx1 = net.add_neuron(i1, + l4n::BIAS_TYPE::NO_BIAS); + size_t idx2 = net.add_neuron(i2, + l4n::BIAS_TYPE::NO_BIAS); + size_t idx3 = net.add_neuron(o1, + l4n::BIAS_TYPE::NEXT_BIAS); + size_t idx4 = net.add_neuron(i3, + l4n::BIAS_TYPE::NEXT_BIAS); + size_t idx5 = net.add_neuron(o2, + l4n::BIAS_TYPE::NEXT_BIAS); /* Adding connections */ - net.add_connection_simple(idx1, idx3, SIMPLE_CONNECTION_TYPE::NEXT_WEIGHT); // weight index 0 - net.add_connection_simple(idx2, idx3, SIMPLE_CONNECTION_TYPE::NEXT_WEIGHT); // weight index 1 - net.add_connection_simple(idx4, idx5, SIMPLE_CONNECTION_TYPE::EXISTING_WEIGHT, 0); // AGAIN weight index 0 - same weight! - - net.randomize_weights(); + net.add_connection_simple(idx1, + idx3, + l4n::SIMPLE_CONNECTION_TYPE::NEXT_WEIGHT); // weight index 0 + net.add_connection_simple(idx2, + idx3, + l4n::SIMPLE_CONNECTION_TYPE::NEXT_WEIGHT); // weight index 1 + net.add_connection_simple(idx4, + idx5, + l4n::SIMPLE_CONNECTION_TYPE::EXISTING_WEIGHT, + 0); // AGAIN weight index 0 - same weight! /* specification of the input/output neurons */ std::vector<size_t> net_input_neurons_indices(3); @@ -78,83 +156,20 @@ int main() { net.specify_input_neurons(net_input_neurons_indices); net.specify_output_neurons(net_output_neurons_indices); - - - /* COMPLEX ERROR FUNCTION SPECIFICATION */ - MSE mse(&net, &ds); - -// double weights[2] = {-0.18012411, -0.17793740}; -// double weights[2] = {1, 1}; - -// printf("evaluation of error at point (%f, %f) => %f\n", weights[0], weights[1], mse.eval(weights)); - - /* TRAINING METHOD SETUP */ - std::vector<double> domain_bounds(2 * (net.get_n_weights() + net.get_n_biases())); - - for(size_t i = 0; i < domain_bounds.size() / 2; ++i){ - domain_bounds[2 * i] = -10; - domain_bounds[2 * i + 1] = 10; - } - - double c1 = 1.7; - double c2 = 1.7; - double w = 0.7; - size_t n_particles = 50; - size_t iter_max = 1000; - - /* if the maximal velocity from the previous step is less than 'gamma' times the current maximal velocity, then one - * terminating criterion is met */ - double gamma = 0.5; + l4n::MSE mse(&net, + &ds); - /* if 'delta' times 'n' particles are in the centroid neighborhood given by the radius 'epsilon', then the second - * terminating criterion is met ('n' is the total number of particles) */ - double epsilon = 0.02; - double delta = 0.7; - - ParticleSwarm swarm_01( - &domain_bounds, - c1, - c2, - w, - gamma, - epsilon, - delta, - n_particles, - iter_max - ); - swarm_01.optimize( mse ); - - std::vector<double> *parameters = swarm_01.get_parameters(); - net.copy_parameter_space(parameters); - - printf("w1 = %10.7f\n", parameters->at( 0 )); - printf("w2 = %10.7f\n", parameters->at( 1 )); - printf("b1 = %10.7f\n", parameters->at( 2 )); - printf("b2 = %10.7f\n", parameters->at( 3 )); - printf("b3 = %10.7f\n", parameters->at( 4 )); - std::cout << "***********************************************************************************************************************" <<std::endl; - - /* ERROR CALCULATION */ - double error = 0.0; - inp = {0, 1, 0}; - net.eval_single( inp, out ); - error += (0.5 - out[0]) * (0.5 - out[0]) + (0.0 - out[1]) * (0.0 - out[1]); - printf("x = (%4.2f, %4.2f, %4.2f), expected output: (%4.2f, %4.2f), real output: (%10.7f, %10.7f)\n", inp[0], inp[1], inp[2], 0.5, 0.0, out[0], out[1]); - - inp = {1, 0.5, 0}; - net.eval_single( inp, out ); - error += (0.75 - out[0]) * (0.75 - out[0]) + (0.0 - out[1]) * (0.0 - out[1]); - printf("x = (%4.2f, %4.2f, %4.2f), expected output: (%4.2f, %4.2f), real output: (%10.7f, %10.7f)\n", inp[0], inp[1], inp[2], 0.75, 0.0, out[0], out[1]); - - inp = {0, 0, 1.25}; - net.eval_single( inp, out ); - error += (0.0 - out[0]) * (0.0 - out[0]) + (0.63 - out[1]) * (0.63 - out[1]); - printf("x = (%4.2f, %4.2f, %4.2f), expected output: (%4.2f, %4.2f), real output: (%10.7f, %10.7f)\n", inp[0], inp[1], inp[2], 0.0, 0.63, out[0], out[1]); - - std::cout << "Run finished! Error of the network: " << error / 3.0 << std::endl; + /* PARTICLE SWARM LEARNING */ + net.randomize_weights(); + optimize_via_particle_swarm(net, + mse); + /* GRADIENT DESCENT LEARNING */ + net.randomize_weights(); + optimize_via_gradient_descent(net, + mse); return 0; -} \ No newline at end of file +} diff --git a/src/examples/net_test_3.cpp b/src/examples/net_test_3.cpp index 69d18546f183f21b6d68d3b13adc1b2987b1be14..d9bc6a7f9d56212fced8b0e0203fbed21ca3b47d 100644 --- a/src/examples/net_test_3.cpp +++ b/src/examples/net_test_3.cpp @@ -1,160 +1,141 @@ /** - * Example of a set of neural networks sharing some edge weights - * The system of equations associated with the net in this example is not regular - * minimizes the function: [(2y+0.5)^2 + (2x+y+0.25)^2] / 2 + [(4.5x + 0.37)^2] / 1 - * minimum [0.010024714] at (x, y) = (-333/4370, -9593/43700) = (-0.076201373, -0.219519451) + * Example testing the correctness of back-propagation implementation * */ -// -// Created by martin on 7/16/18. -// - +#include <iostream> +#include <cstdio> +#include <fstream> #include <vector> +#include <utility> +#include <algorithm> +#include <assert.h> +#include <ctime> -#include "4neuro.h" - -int main() { - std::cout << "Running lib4neuro example 3: Use of the particle swarm method to train a set of networks sharing some edge weights" << std::endl; - std::cout << "********************************************************************************************************************" <<std::endl; - - /* TRAIN DATA DEFINITION */ - std::vector<std::pair<std::vector<double>, std::vector<double>>> data_vec_01, data_vec_02; - std::vector<double> inp, out; - - inp = {0, 1}; - out = {0.5}; - data_vec_01.emplace_back(std::make_pair(inp, out)); - - inp = {1, 0.5}; - out = {0.75}; - data_vec_01.emplace_back(std::make_pair(inp, out)); - - DataSet ds_01(&data_vec_01); - - - inp = {1.25}; - out = {0.63}; - data_vec_02.emplace_back(std::make_pair(inp, out)); - DataSet ds_02(&data_vec_02); - - /* NETWORK DEFINITION */ - NeuralNetwork net; - - /* Input neurons */ - NeuronLinear *i1 = new NeuronLinear(); //f(x) = x - NeuronLinear *i2 = new NeuronLinear(); //f(x) = x +#include <4neuro.h> - NeuronLinear *i3 = new NeuronLinear( ); //f(x) = x +#include <boost/random/mersenne_twister.hpp> +#include <boost/random/uniform_int_distribution.hpp> +#include <boost/random/uniform_real_distribution.hpp> - /* Output neurons */ - NeuronLinear *o1 = new NeuronLinear( ); //f(x) = x - NeuronLinear *o2 = new NeuronLinear( ); //f(x) = x +double get_difference(std::vector<double>& a, + std::vector<double>& b) { + double out = 0.0, m; - /* Adding neurons to the nets */ - size_t idx1 = net.add_neuron(i1, BIAS_TYPE::NO_BIAS); - size_t idx2 = net.add_neuron(i2, BIAS_TYPE::NO_BIAS); - size_t idx3 = net.add_neuron(o1, BIAS_TYPE::NEXT_BIAS); - size_t idx4 = net.add_neuron(i3, BIAS_TYPE::NEXT_BIAS); - size_t idx5 = net.add_neuron(o2, BIAS_TYPE::NEXT_BIAS); + for (size_t i = 0; i < a.size(); ++i) { - /* Adding connections */ - net.add_connection_simple(idx1, idx3, SIMPLE_CONNECTION_TYPE::NEXT_WEIGHT); // weight index 0 - net.add_connection_simple(idx2, idx3, SIMPLE_CONNECTION_TYPE::NEXT_WEIGHT); // weight index 1 - net.add_connection_simple(idx4, idx5, SIMPLE_CONNECTION_TYPE::EXISTING_WEIGHT, 0); // AGAIN weight index 0 - same weight! - - net.randomize_weights(); - - /* specification of the input/output neurons */ - std::vector<size_t> net_input_neurons_indices(3); - std::vector<size_t> net_output_neurons_indices(2); - net_input_neurons_indices[0] = idx1; - net_input_neurons_indices[1] = idx2; - net_input_neurons_indices[2] = idx4; - - net_output_neurons_indices[0] = idx3; - net_output_neurons_indices[1] = idx5; - - net.specify_input_neurons(net_input_neurons_indices); - net.specify_output_neurons(net_output_neurons_indices); + m = a[i] - b[i]; + out += m * m; + } - /* CONSTRUCTION OF SUBNETWORKS */ - //TODO subnetworks retain the number of weights, could be optimized to include only the used weights - //TODO this example is not working properly, subnet method is not implemented - std::vector<size_t> subnet_01_input_neurons, subnet_01_output_neurons; - std::vector<size_t> subnet_02_input_neurons, subnet_02_output_neurons; + return std::sqrt(out); - subnet_01_input_neurons.push_back(idx1); - subnet_01_input_neurons.push_back(idx2); - subnet_01_output_neurons.push_back(idx3); - NeuralNetwork *subnet_01 = net.get_subnet( subnet_01_input_neurons, subnet_01_output_neurons ); +} - subnet_02_input_neurons.push_back(idx4); - subnet_02_output_neurons.push_back(idx5); - NeuralNetwork *subnet_02 = net.get_subnet( subnet_02_input_neurons, subnet_02_output_neurons ); - if(subnet_01 && subnet_02){ - /* COMPLEX ERROR FUNCTION SPECIFICATION */ - MSE mse_01(subnet_01, &ds_01); - MSE mse_02(subnet_02, &ds_02); +void calculate_gradient_analytical(std::vector<double>& input, + std::vector<double>& parameter_biases, + std::vector<double>& parameter_weights, + size_t n_hidden_neurons, + std::vector<double>& gradient_analytical) { - ErrorSum mse_sum; - mse_sum.add_error_function( &mse_01 ); - mse_sum.add_error_function( &mse_02 ); + double a, b, y, x = input[0]; + for (size_t i = 0; i < n_hidden_neurons; ++i) { + a = parameter_weights[i]; + b = parameter_biases[i]; + y = parameter_weights[n_hidden_neurons + i]; - /* TRAINING METHOD SETUP */ - std::vector<double> domain_bounds(2 * (net.get_n_weights() + net.get_n_biases())); + gradient_analytical[i] += y * x * std::exp(b - a * x) / ((1 + std::exp(b - a * x)) * (1 + std::exp(b - a * x))); + gradient_analytical[n_hidden_neurons + i] += 1.0 / ((1 + std::exp(b - a * x))); + gradient_analytical[2 * n_hidden_neurons + i] -= + y * std::exp(b - a * x) / ((1 + std::exp(b - a * x)) * (1 + std::exp(b - a * x))); + } - for(size_t i = 0; i < domain_bounds.size() / 2; ++i){ - domain_bounds[2 * i] = -10; - domain_bounds[2 * i + 1] = 10; +} + +int main(int argc, + char** argv) { + + int n_tests = 2; + int n_hidden_neurons = 2; + try { + /* Numbers of neurons in layers (including input and output layers) */ + std::vector<unsigned int> neuron_numbers_in_layers(3); + neuron_numbers_in_layers[0] = neuron_numbers_in_layers[2] = 1; + neuron_numbers_in_layers[1] = n_hidden_neurons; + + /* Fully connected feed-forward network with linear activation functions for input and output */ + /* layers and the specified activation fns for the hidden ones (each entry = layer)*/ + std::vector<l4n::NEURON_TYPE> hidden_type_v = {l4n::NEURON_TYPE::LOGISTIC, l4n::NEURON_TYPE::LOGISTIC, + l4n::NEURON_TYPE::LOGISTIC, l4n::NEURON_TYPE::LOGISTIC, + l4n::NEURON_TYPE::LOGISTIC}; // hidden_type_v = {l4n::NEURON_TYPE::LOGISTIC, l4n::NEURON_TYPE::LINEAR} + l4n::FullyConnectedFFN nn1(&neuron_numbers_in_layers, + &hidden_type_v); + nn1.randomize_parameters(); + + boost::random::mt19937 gen(std::time(0)); + boost::random::uniform_real_distribution<> dist(-1, + 1); + + size_t n_parameters = nn1.get_n_weights() + nn1.get_n_biases(); + std::vector<double> gradient_backprogation(n_parameters); + std::vector<double> gradient_analytical(n_parameters); + std::vector<double>* parameter_biases = nn1.get_parameter_ptr_biases(); + std::vector<double>* parameter_weights = nn1.get_parameter_ptr_weights(); + std::vector<double> error_derivative = {1}; + + size_t n_good = 0, n_bad = 0; + + for (int i = 0; i < n_tests; ++i) { + + std::vector<double> input(1); + std::vector<double> output(1); + + input[0] = dist(gen); + output[0] = 0; + + + std::fill(gradient_backprogation.begin(), + gradient_backprogation.end(), + 0); + std::fill(gradient_analytical.begin(), + gradient_analytical.end(), + 0); + + nn1.eval_single(input, + output); + + calculate_gradient_analytical(input, + *parameter_biases, + *parameter_weights, + n_hidden_neurons, + gradient_analytical); + nn1.add_to_gradient_single(input, + error_derivative, + 1, + gradient_backprogation); + + double diff = get_difference(gradient_backprogation, + gradient_analytical); + + if (diff < 1e-6) { + n_good++; + } else { + n_bad++; + } } - double c1 = 1.7; - double c2 = 1.7; - double w = 0.7; - size_t n_particles = 50; - size_t iter_max = 1000; - - /* if the maximal velocity from the previous step is less than 'gamma' times the current maximal velocity, then one - * terminating criterion is met */ - double gamma = 0.5; - - /* if 'delta' times 'n' particles are in the centroid neighborhood given by the radius 'epsilon', then the second - * terminating criterion is met ('n' is the total number of particles) */ - double epsilon = 0.02; - double delta = 0.7; - - ParticleSwarm swarm_01( - &domain_bounds, - c1, - c2, - w, - gamma, - epsilon, - delta, - n_particles, - iter_max - ); - swarm_01.optimize( mse_sum ); + std::cout << "Good gradients: " << n_good << ", Bad gradients: " << n_bad << std::endl; - } - else{ - std::cout << "We apologize, this example is unfinished as we are in the process of developing methods for efficient subnetwork definition" << std::endl; - } + return 0; - if(subnet_01){ - delete subnet_01; - subnet_01 = nullptr; } - - if(subnet_02){ - delete subnet_02; - subnet_02 = nullptr; + catch (const std::exception& e) { + std::cerr << e.what() << std::endl; + exit(EXIT_FAILURE); } - return 0; -} \ No newline at end of file +} diff --git a/src/examples/net_test_harmonic_oscilator.cpp b/src/examples/net_test_harmonic_oscilator.cpp index 6eecc425d72ebb638b2051defaa52a134c371698..e216200c41ce37b349d7df789281f2cdc910f48c 100644 --- a/src/examples/net_test_harmonic_oscilator.cpp +++ b/src/examples/net_test_harmonic_oscilator.cpp @@ -11,30 +11,148 @@ #include <iostream> #include <fstream> -#include "../../include/4neuro.h" -#include "../Solvers/DESolver.h" +#include "4neuro.h" + +void export_solution(size_t n_test_points, + double te, + double ts, + l4n::DESolver& solver, + l4n::MultiIndex& alpha, + const std::string prefix) { + l4n::NeuralNetwork* solution = solver.get_solution(alpha); + + char buff[256]; + sprintf(buff, + "%sdata_1d_osc.txt", + prefix.c_str()); + std::string final_fn(buff); + + std::ofstream ofs(final_fn, + std::ofstream::out); + printf("Exporting files '%s': %7.3f%%\r", + final_fn.c_str(), + 0.0); + double frac = (te - ts) / (n_test_points - 1), x; + + std::vector<double> inp(1), out(1); + + for (size_t i = 0; i < n_test_points; ++i) { + x = frac * i + ts; -void test_harmonic_oscilator_fixed_E(double EE, double accuracy, size_t n_inner_neurons, size_t train_size, double ds, double de, size_t n_test_points, double ts, double te, size_t max_iters, size_t n_particles){ + inp[0] = x; + solution->eval_single(inp, + out); + ofs << i + 1 << " " << x << " " << out[0] << " " << std::endl; + + printf("Exporting files '%s': %7.3f%%\r", + final_fn.c_str(), + (100.0 * i) / (n_test_points - 1)); + std::cout.flush(); + } + printf("Exporting files '%s': %7.3f%%\n", + final_fn.c_str(), + 100.0); + std::cout.flush(); + ofs.close(); +} + +void optimize_via_particle_swarm(l4n::DESolver& solver, + l4n::MultiIndex& alpha, + size_t max_iters, + size_t n_particles) { + + printf("Solution via the particle swarm optimization!\n"); + std::vector<double> domain_bounds( + 2 * (solver.get_solution(alpha)->get_n_biases() + solver.get_solution(alpha)->get_n_weights())); + + for (size_t i = 0; i < domain_bounds.size() / 2; ++i) { + domain_bounds[2 * i] = -10; + domain_bounds[2 * i + 1] = 10; + } + + double c1 = 1.7; + double c2 = 1.7; + double w = 0.700; + + /* if the maximal velocity from the previous step is less than 'gamma' times the current maximal velocity, then one + * terminating criterion is met */ + double gamma = 0.5; + + /* if 'delta' times 'n' particles are in the centroid neighborhood given by the radius 'epsilon', then the second + * terminating criterion is met ('n' is the total number of particles) */ + double epsilon = 0.02; + double delta = 0.7; + + l4n::ParticleSwarm swarm( + &domain_bounds, + c1, + c2, + w, + gamma, + epsilon, + delta, + n_particles, + max_iters + ); + + solver.solve(swarm); +} + +void optimize_via_gradient_descent(l4n::DESolver& solver, + double accuracy) { + + printf("Solution via a gradient descent method!\n"); + l4n::GradientDescent gd(accuracy, + 1000); + + solver.randomize_parameters(); + solver.solve(gd); +} + +void test_harmonic_oscilator_fixed_E(double EE, + double accuracy, + size_t n_inner_neurons, + size_t train_size, + double ds, + double de, + size_t n_test_points, + double ts, + double te, + size_t max_iters, + size_t n_particles) { std::cout << "Finding a solution via the Particle Swarm Optimization" << std::endl; - std::cout << "********************************************************************************************************************************************" <<std::endl; + std::cout + << "********************************************************************************************************************************************" + << std::endl; /* SOLVER SETUP */ - size_t n_inputs = 1; - size_t n_equations = 1; - DESolver solver( n_equations, n_inputs, n_inner_neurons ); + size_t n_inputs = 1; + size_t n_equations = 1; + l4n::DESolver solver(n_equations, + n_inputs, + n_inner_neurons); /* SETUP OF THE EQUATIONS */ - MultiIndex alpha_0( n_inputs ); - MultiIndex alpha_2( n_inputs ); - alpha_2.set_partial_derivative(0, 2); + l4n::MultiIndex alpha_0(n_inputs); + l4n::MultiIndex alpha_2(n_inputs); + alpha_2.set_partial_derivative(0, + 2); /* the governing differential equation */ char buff[255]; - std::sprintf(buff, "%f", -EE); + std::sprintf(buff, + "%f", + -EE); std::string eigenvalue(buff); - solver.add_to_differential_equation( 0, alpha_2, "-1.0" ); - solver.add_to_differential_equation( 0, alpha_0, "x^2" ); - solver.add_to_differential_equation( 0, alpha_0, eigenvalue ); + solver.add_to_differential_equation(0, + alpha_2, + "-1.0"); + solver.add_to_differential_equation(0, + alpha_0, + "x^2"); + solver.add_to_differential_equation(0, + alpha_0, + eigenvalue); /* SETUP OF THE TRAINING DATA */ std::vector<double> inp, out; @@ -47,137 +165,77 @@ void test_harmonic_oscilator_fixed_E(double EE, double accuracy, size_t n_inner_ /* ISOTROPIC TRAIN SET */ frac = (d1_e - d1_s) / (train_size - 1); - for(unsigned int i = 0; i < train_size; ++i){ + for (unsigned int i = 0; i < train_size; ++i) { inp = {frac * i + d1_s}; out = {0.0}; - data_vec_g.emplace_back(std::make_pair(inp, out)); + data_vec_g.emplace_back(std::make_pair(inp, + out)); } -// inp = {0.0}; -// out = {1.0}; -// data_vec_g.emplace_back(std::make_pair(inp, out)); + inp = {0.0}; + out = {1.0}; + data_vec_g.emplace_back(std::make_pair(inp, + out)); - DataSet ds_00(&data_vec_g); + l4n::DataSet ds_00(&data_vec_g); /* Placing the conditions into the solver */ - solver.set_error_function( 0, ErrorFunctionType::ErrorFuncMSE, &ds_00 ); - - /* TRAINING METHOD SETUP */ - size_t total_dim = solver.get_solution( alpha_0 )->get_n_biases() + solver.get_solution( alpha_0 )->get_n_weights(); - std::vector<double> domain_bounds( 2 * total_dim ); - - for(size_t i = 0; i < domain_bounds.size() / 2; ++i){ - domain_bounds[2 * i] = -10; - domain_bounds[2 * i + 1] = 10; - } - - double c1 = 1.7; - double c2 = 1.7; - double w = 0.700; - - /* if the maximal velocity from the previous step is less than 'gamma' times the current maximal velocity, then one - * terminating criterion is met */ - double gamma = 0.5; - - /* if 'delta' times 'n' particles are in the centroid neighborhood given by the radius 'epsilon', then the second - * terminating criterion is met ('n' is the total number of particles) */ - double epsilon = 0.02; - double delta = 0.7; - - ParticleSwarm swarm( - &domain_bounds, - c1, - c2, - w, - gamma, - epsilon, - delta, - n_particles, - max_iters - ); - solver.solve( swarm ); - - NeuralNetwork *solution = solver.get_solution( alpha_0 ); - std::vector<double> parameters(total_dim);//w1, a1, b1, w2, a2, b2, ... , wm, am, bm - std::vector<double> *weight_params = solution->get_parameter_ptr_weights(); - std::vector<double> *biases_params = solution->get_parameter_ptr_biases(); - for(size_t i = 0; i < n_inner_neurons; ++i){ - parameters[3 * i] = weight_params->at(i); - parameters[3 * i + 1] = weight_params->at(i + n_inner_neurons); - parameters[3 * i + 2] = biases_params->at(i); - - printf("Path %3d. w%d = %15.8f, b%d = %15.8f, a%d = %15.8f\n", (int)(i + 1), (int)(i + 1), parameters[3 * i], (int)(i + 1), parameters[3 * i + 2], (int)(i + 1), parameters[3 * i + 1]); - } - - /* ISOTROPIC TEST SET FOR BOUNDARY CONDITIONS */ - /* first boundary condition & its error */ - std::ofstream ofs("data_1d_osc.txt", std::ofstream::out); - printf("Exporting files 'data_1d_osc.txt': %7.3f%%\r", 0.0); - frac = (te - ts) / (n_test_points - 1); - - for(size_t i = 0; i < n_test_points; ++i){ - double x = frac * i + ts; - - inp[0] = x; - solution->eval_single(inp, out); - ofs << i + 1 << " " << x << " " << out[0] << " " << std::endl; - - printf("Exporting files 'data_1d_osc.txt': %7.3f%%\r", (100.0 * i) / (n_test_points - 1)); - std::cout.flush(); - } - printf("Exporting files 'data_1d_osc.txt': %7.3f%%\n", 100.0); - std::cout.flush(); - ofs.close(); - - inp[0] = -1.0; - solution->eval_single(inp, out); - printf("y(-1) = %f\n", out[0]); - inp[0] = 0.0; - solution->eval_single(inp, out); - printf("y( 0) = %f\n", out[0]); - inp[0] = 1.0; - solution->eval_single(inp, out); - printf("y( 1) = %f\n", out[0]); - std::cout << "********************************************************************************************************************************************" <<std::endl; + solver.set_error_function(0, + l4n::ErrorFunctionType::ErrorFuncMSE, + ds_00); + + /* PARTICLE SWARM TRAINING METHOD SETUP */ + size_t total_dim = (2 + n_inputs) * n_inner_neurons; + + optimize_via_gradient_descent(solver, + accuracy); + export_solution(n_test_points, + te, + ts, + solver, + alpha_0, + "gradient_"); } int main() { std::cout << "Running lib4neuro harmonic Oscilator example 1" << std::endl; - std::cout << "********************************************************************************************************************************************" <<std::endl; + std::cout + << "********************************************************************************************************************************************" + << std::endl; std::cout << " Governing equation: -y''(x) + x^2 * y(x) = E * y(x)" << std::endl; - std::cout << "********************************************************************************************************************************************" <<std::endl; - std::cout << "Expressing solution as y(x) = sum over [a_i / (1 + exp(bi - wxi*x ))], i in [1, n], where n is the number of hidden neurons" <<std::endl; - std::cout << "********************************************************************************************************************************************" <<std::endl; - - double EE = -1.0; + std::cout + << "********************************************************************************************************************************************" + << std::endl; + std::cout + << "Expressing solution as y(x) = sum over [a_i / (1 + exp(bi - wxi*x ))], i in [1, n], where n is the number of hidden neurons" + << std::endl; + std::cout + << "********************************************************************************************************************************************" + << std::endl; + + double EE = -1.0; unsigned int n_inner_neurons = 2; - unsigned int train_size = 10; - double accuracy = 1e-3; - double ds = -5.0; - double de = 5.0; + unsigned int train_size = 10; + double accuracy = 1e-3; + double ds = -5.0; + double de = 5.0; unsigned int test_size = 300; - double ts = -6.0; - double te = 6.0; + double ts = -6.0; + double te = 6.0; size_t particle_swarm_max_iters = 1000; - size_t n_particles = 100; - test_harmonic_oscilator_fixed_E(EE, accuracy, n_inner_neurons, train_size, ds, de, test_size, ts, te, particle_swarm_max_iters, n_particles); - -// std::string expression_string = "-x"; -// std::string expression_string_1 = "1.0"; -// ExprtkWrapper f(expression_string); -// ExprtkWrapper f1(expression_string_1); -// -// -// f1.eval(); -// -// std::vector<double> inp(1); -// -// inp = {150}; -// double result = f.eval(inp); -// -// f1.eval(); -// inp = {15}; -// result = f.eval(inp); + size_t n_particles = 100; + test_harmonic_oscilator_fixed_E(EE, + accuracy, + n_inner_neurons, + train_size, + ds, + de, + test_size, + ts, + te, + particle_swarm_max_iters, + n_particles); + return 0; -} \ No newline at end of file +} diff --git a/src/examples/net_test_ode_1.cpp b/src/examples/net_test_ode_1.cpp index 7f8deda3a7e0da2799374b17d4dbf05d86beae4a..28c48ec46e1c058e4694b0ffa702d7ed0d683077 100644 --- a/src/examples/net_test_ode_1.cpp +++ b/src/examples/net_test_ode_1.cpp @@ -18,711 +18,306 @@ #include <random> #include <iostream> - +#include <chrono> #include "4neuro.h" -double eval_f(double x){ - return std::pow(E, -2.0 * x) * (3.0 * x + 1.0); -} - -double eval_df(double x){ - return std::pow(E, -2.0 * x) * (1.0 - 6.0 * x); -} - -double eval_ddf(double x){ - return 4.0 * std::pow(E, -2.0 * x) * (3.0 * x - 2.0); -} - -double eval_approx_f(double x, size_t n_inner_neurons, std::vector<double> ¶meters){ - double value= 0.0, wi, ai, bi, ei, ei1; - for(size_t i = 0; i < n_inner_neurons; ++i){ - - wi = parameters[3 * i]; - ai = parameters[3 * i + 1]; - bi = parameters[3 * i + 2]; - - ei = std::pow(E, bi - wi * x); - ei1 = ei + 1.0; +void optimize_via_particle_swarm(l4n::DESolver& solver, + l4n::MultiIndex& alpha, + size_t max_iters, + size_t n_particles) { - value += ai / (ei1); - } - return value; -} - -double eval_approx_df(double x, size_t n_inner_neurons, std::vector<double> ¶meters){ - double value= 0.0, wi, ai, bi, ei, ei1; - for(size_t i = 0; i < n_inner_neurons; ++i){ - - wi = parameters[3 * i]; - ai = parameters[3 * i + 1]; - bi = parameters[3 * i + 2]; - - ei = std::pow(E, bi - wi * x); - ei1 = ei + 1.0; - - value += ai * wi * ei / (ei1 * ei1); - } - - return value; -} + printf("Solution via the particle swarm optimization!\n"); + std::vector<double> domain_bounds( + 2 * (solver.get_solution(alpha)->get_n_biases() + solver.get_solution(alpha)->get_n_weights())); -double eval_approx_ddf(double x, size_t n_inner_neurons, std::vector<double> ¶meters){ - double value= 0.0, wi, ai, bi, ewx, eb; - - for(size_t i = 0; i < n_inner_neurons; ++i){ - - wi = parameters[3 * i]; - ai = parameters[3 * i + 1]; - bi = parameters[3 * i + 2]; - - - eb = std::pow(E, bi); - ewx = std::pow(E, wi * x); - - value += -(ai*wi*wi*eb*ewx*(ewx - eb))/((eb + ewx)*(eb + ewx)*(eb + ewx)); + for (size_t i = 0; i < domain_bounds.size() / 2; ++i) { + domain_bounds[2 * i] = -10; + domain_bounds[2 * i + 1] = 10; } - return value; -} - -//NN partial derivative (wi): (ai * x * e^(bi - wi * x)) * (e^(bi - wi * x) + 1)^(-2) -double eval_approx_dw_f(double x, size_t neuron_idx, std::vector<double> ¶meters){ - double wi, ai, bi, ei, ei1; - wi = parameters[3 * neuron_idx]; - ai = parameters[3 * neuron_idx + 1]; - bi = parameters[3 * neuron_idx + 2]; - - ei = std::pow(E, bi - wi * x); - ei1 = ei + 1.0; - - return (ai * x * ei) / (ei1 * ei1); -} - -//dNN partial derivative (wi): -(a w x e^(b - w x))/(e^(b - w x) + 1)^2 + (2 a w x e^(2 b - 2 w x))/(e^(b - w x) + 1)^3 + (a e^(b - w x))/(e^(b - w x) + 1)^2 -double eval_approx_dw_df(double x, size_t neuron_idx, std::vector<double> ¶meters){ - - double wi, ai, bi, ei, ei1; - - wi = parameters[3 * neuron_idx]; - ai = parameters[3 * neuron_idx + 1]; - bi = parameters[3 * neuron_idx + 2]; - - ei = std::pow(E, bi - wi * x); - ei1 = ei + 1.0; - - return -(ai * wi * x * ei)/(ei1 * ei1) + (2.0*ai*wi*x*ei*ei)/(ei1 * ei1 * ei1) + (ai* ei)/(ei1 * ei1); -} - -//ddNN partial derivative (wi): -(a w^2 x e^(b + 2 w x))/(e^b + e^(w x))^3 - (a w^2 x e^(b + w x) (e^(w x) - e^b))/(e^b + e^(w x))^3 + (3 a w^2 x e^(b + 2 w x) (e^(w x) - e^b))/(e^b + e^(w x))^4 - (2 a w e^(b + w x) (e^(w x) - e^b))/(e^b + e^(w x))^3 -double eval_approx_dw_ddf(double x, size_t neuron_idx, std::vector<double> ¶meters){ - double wi, ai, bi, eb, ewx; - - wi = parameters[3 * neuron_idx]; - ai = parameters[3 * neuron_idx + 1]; - bi = parameters[3 * neuron_idx + 2]; - - eb = std::pow(E, bi); - ewx = std::pow(E, wi * x); - - return -(ai*wi*wi* x * eb*ewx*ewx)/((eb + ewx)*(eb + ewx)*(eb + ewx)) - (ai*wi*wi*x*eb*ewx*(ewx - eb))/((eb + ewx)*(eb + ewx)*(eb + ewx)) + (3*ai*wi*wi*x*eb*ewx*ewx*(ewx - eb))/((eb + ewx)*(eb + ewx)*(eb + ewx)*(eb + ewx)) - (2*ai*wi*eb*ewx*(ewx - eb))/((eb + ewx)*(eb + ewx)*(eb + ewx)); -} - -//NN partial derivative (ai): (1 + e^(-x * wi + bi))^(-1) -double eval_approx_da_f(double x, size_t neuron_idx, std::vector<double> ¶meters){ - double wi, bi, ei, ei1; - - wi = parameters[3 * neuron_idx]; - bi = parameters[3 * neuron_idx + 2]; - - ei = std::pow(E, bi - wi * x); - ei1 = ei + 1.0; - - return 1.0 / ei1; -} - -//dNN partial derivative (ai): (w e^(b - w x))/(e^(b - w x) + 1)^2 -double eval_approx_da_df(double x, size_t neuron_idx, std::vector<double> ¶meters){ - double wi, bi, ei, ei1; - - wi = parameters[3 * neuron_idx]; - bi = parameters[3 * neuron_idx + 2]; - - ei = std::pow(E, bi - wi * x); - ei1 = ei + 1.0; - - return (wi*ei)/(ei1 * ei1); -} - -//ddNN partial derivative (ai): -(w^2 e^(b + w x) (e^(w x) - e^b))/(e^b + e^(w x))^3 -double eval_approx_da_ddf(double x, size_t neuron_idx, std::vector<double> ¶meters){ - double wi, bi, eip, ewx, eb; - - wi = parameters[3 * neuron_idx]; - bi = parameters[3 * neuron_idx + 2]; - - eip = std::pow(E, bi + wi * x); - eb = std::pow(E, bi); - ewx = std::pow(E, wi * x); - - return -(wi*wi*eip*(ewx - eb))/((eb + ewx)*(eb + ewx)*(eb + ewx)); -} - -//NN partial derivative (bi): -(ai * e^(bi - wi * x)) * (e^(bi - wi * x) + 1)^(-2) -double eval_approx_db_f(double x, size_t neuron_idx, std::vector<double> ¶meters){ - double wi, bi, ei, ai, ei1; - wi = parameters[3 * neuron_idx]; - ai = parameters[3 * neuron_idx + 1]; - bi = parameters[3 * neuron_idx + 2]; - - ei = std::pow(E, bi - wi * x); - ei1 = ei + 1.0; - - return -(ai * ei)/(ei1 * ei1); -} - -//dNN partial derivative (bi): (a w e^(b + w x) (e^(w x) - e^b))/(e^b + e^(w x))^3 -double eval_approx_db_df(double x, size_t neuron_idx, std::vector<double> ¶meters){ - double wi, bi, ai, ewx, eb; - - wi = parameters[3 * neuron_idx]; - ai = parameters[3 * neuron_idx + 1]; - bi = parameters[3 * neuron_idx + 2]; - - eb = std::pow(E, bi); - ewx = std::pow(E, wi*x); - - return (ai* wi* eb*ewx* (ewx - eb))/((eb + ewx)*(eb + ewx)*(eb + ewx)); -} + double c1 = 1.7; + double c2 = 1.7; + double w = 0.700; -//ddNN partial derivative (bi): -(a w^2 e^(b + w x) (-4 e^(b + w x) + e^(2 b) + e^(2 w x)))/(e^b + e^(w x))^4 -double eval_approx_db_ddf(double x, size_t neuron_idx, std::vector<double> ¶meters){ - double wi, bi, ai, ewx, eb; + /* if the maximal velocity from the previous step is less than 'gamma' times the current maximal velocity, then one + * terminating criterion is met */ + double gamma = 0.5; - wi = parameters[3 * neuron_idx]; - ai = parameters[3 * neuron_idx + 1]; - bi = parameters[3 * neuron_idx + 2]; + /* if 'delta' times 'n' particles are in the centroid neighborhood given by the radius 'epsilon', then the second + * terminating criterion is met ('n' is the total number of particles) */ + double epsilon = 0.02; + double delta = 0.7; + + l4n::ParticleSwarm swarm( + &domain_bounds, + c1, + c2, + w, + gamma, + epsilon, + delta, + n_particles, + max_iters + ); - eb = std::pow(E, bi); - ewx = std::pow(E, wi*x); + solver.solve(swarm); - return -(ai* wi*wi* eb*ewx* (-4.0* eb*ewx + eb*eb + ewx*ewx))/((eb +ewx)*(eb +ewx)*(eb +ewx)*(eb +ewx)); } -double eval_error_function(std::vector<double> ¶meters, size_t n_inner_neurons, std::vector<double> test_points){ - - double output = 0.0, approx, frac = 1.0 / (test_points.size()); - - for(auto x: test_points){ - /* governing equation */ - approx = 4.0 * eval_approx_f(x, n_inner_neurons, parameters) + 4.0 * eval_approx_df(x, n_inner_neurons, parameters) + eval_approx_ddf(x, n_inner_neurons, parameters); - - output += (0.0 - approx) * (0.0 - approx) * frac; - - } - - /* BC */ - approx = eval_approx_f(0.0, n_inner_neurons, parameters); - output += (1.0 - approx) * (1.0 - approx); +void optimize_via_gradient_descent(l4n::DESolver& solver, + double accuracy) { + printf("Solution via a gradient descent method!\n"); + solver.randomize_parameters(); +// TODO does not work (poor design of netsum) +// l4n::LevenbergMarquardt leven(10000, 0, 1e-6, 1e-6, 1e-6); +// solver.solve(leven); - approx = eval_approx_df(0.0, n_inner_neurons, parameters); - output += (1.0 - approx) * (1.0 - approx); + l4n::GradientDescent gd(accuracy, + 1000, + 500000); + solver.solve(gd); - return output; } -void eval_step_size_simple(double &gamma, double val, double prev_val, double sk, double grad_norm, double grad_norm_prev){ +void export_solution(size_t n_test_points, + double te, + double ts, + l4n::DESolver& solver, + l4n::MultiIndex& alpha_0, + l4n::MultiIndex& alpha_1, + l4n::MultiIndex& alpha_2, + const std::string prefix) { + l4n::NeuralNetwork* solution = solver.get_solution(alpha_0); + l4n::NeuralNetwork* solution_d = solver.get_solution(alpha_1); + l4n::NeuralNetwork* solution_dd = solver.get_solution(alpha_2); - if(val > prev_val){ - gamma *= 0.99999; - } + /* ISOTROPIC TEST SET FOR BOUNDARY CONDITIONS */ + /* first boundary condition & its error */ - if(sk <= 1e-3 || grad_norm < grad_norm_prev){ - /* movement on a line */ - /* new slope is less steep, speed up */ - gamma *= 1.0005; - } - else if(grad_norm > grad_norm_prev){ - /* new slope is more steep, slow down*/ - gamma /= 1.0005; - } - else{ - gamma /= 1.005; - } -// gamma *= 0.999999; -} + char buff[256]; + sprintf(buff, + "%sdata_1d_ode1.txt", + prefix.c_str()); + std::string final_fn(buff); -void eval_step_size_mk( double &gamma, double beta, double &c, double grad_norm_prev, double grad_norm, double fi, double fim ){ + std::ofstream ofs(final_fn, + std::ofstream::out); + printf("Exporting files '%s': %7.3f%%\r", + final_fn.c_str(), + 0.0); + double frac = (te - ts) / (n_test_points - 1); - if( fi > fim ) - { - c /= 1.0000005; - } - else if( fi < fim ) - { - c *= 1.0000005; - } + std::vector<double> inp(1), out(1); - gamma *= std::pow( c, 1.0 - 2.0 * beta) * std::pow( grad_norm_prev / grad_norm, 1.0 / c ); - - -} + for (size_t i = 0; i < n_test_points; ++i) { + double x = frac * i + ts; + inp[0] = x; + solution->eval_single(inp, + out); + double F = out[0]; -double calculate_gradient( std::vector<double> &data_points, size_t n_inner_neurons, std::vector<double> *parameters, std::vector<double> *gradient ){ - size_t i, j; - double x, mem, derror, total_error, approx; + solution_d->eval_single(inp, + out); + double DF = out[0]; - size_t train_size = data_points.size(); + solution_dd->eval_single(inp, + out); + double DDF = out[0]; - /* error boundary condition: y(0) = 1 => e1 = (1 - y(0))^2 */ - x = 0.0; - mem = (1.0 - eval_approx_f(x, n_inner_neurons, *parameters)); - derror = 2.0 * mem; - total_error = mem * mem; - for(i = 0; i < n_inner_neurons; ++i){ - (*gradient)[3 * i] -= derror * eval_approx_dw_f(x, i, *parameters); - (*gradient)[3 * i + 1] -= derror * eval_approx_da_f(x, i, *parameters); - (*gradient)[3 * i + 2] -= derror * eval_approx_db_f(x, i, *parameters); - } + ofs << i + 1 << " " << x << " " << std::pow(l4n::E, + -2 * x) * (3 * x + 1) << " " << F << " " + << std::pow(l4n::E, + -2 * x) * (1 - 6 * x) << " " << DF << " " << 4 * std::pow(l4n::E, + -2 * x) * (3 * x - 2) + << " " << DDF << std::endl; - /* error boundary condition: y'(0) = 1 => e2 = (1 - y'(0))^2 */ - mem = (1.0 - eval_approx_df(x, n_inner_neurons, *parameters)); - derror = 2.0 * mem; - total_error += mem * mem; - for(i = 0; i < n_inner_neurons; ++i){ - (*gradient)[3 * i] -= derror * eval_approx_dw_df(x, i, *parameters); - (*gradient)[3 * i + 1] -= derror * eval_approx_da_df(x, i, *parameters); - (*gradient)[3 * i + 2] -= derror * eval_approx_db_df(x, i, *parameters); + printf("Exporting files '%s': %7.3f%%\r", + final_fn.c_str(), + (100.0 * i) / (n_test_points - 1)); + std::cout.flush(); } + printf("Exporting files '%s': %7.3f%%\r", + final_fn.c_str(), + 100.0); + std::cout.flush(); + ofs.close(); - for(j = 0; j < data_points.size(); ++j){ - x = data_points[j]; - /* error of the governing equation: y''(x) + 4y'(x) + 4y(x) = 0 => e3 = 1/n * (0 - y''(x) - 4y'(x) - 4y(x))^2 */ - approx= eval_approx_ddf(x, n_inner_neurons, *parameters) + 4.0 * eval_approx_df(x, n_inner_neurons, *parameters) + 4.0 * eval_approx_f(x, n_inner_neurons, *parameters); - mem = 0.0 - approx; - derror = 2.0 * mem / train_size; - for(i = 0; i < n_inner_neurons; ++i){ - (*gradient)[3 * i] -= derror * (eval_approx_dw_ddf(x, i, *parameters) + 4.0 * eval_approx_dw_df(x, i, *parameters) + 4.0 * eval_approx_dw_f(x, i, *parameters)); - (*gradient)[3 * i + 1] -= derror * (eval_approx_da_ddf(x, i, *parameters) + 4.0 * eval_approx_da_df(x, i, *parameters) + 4.0 * eval_approx_da_f(x, i, *parameters)); - (*gradient)[3 * i + 2] -= derror * (eval_approx_db_ddf(x, i, *parameters) + 4.0 * eval_approx_db_df(x, i, *parameters) + 4.0 * eval_approx_db_f(x, i, *parameters)); - } - total_error += mem * mem / train_size; - } + std::cout + << "********************************************************************************************************************************************" + << std::endl; - return total_error; } - -//void test_analytical_gradient_y(std::vector<double> &guess, double accuracy, size_t n_inner_neurons, size_t train_size, double d1_s, double d1_e, -// size_t test_size, double ts, double te) { -// -// std::cout << "Finding a solution via a Gradient Descent method with adaptive step-length" << std::endl; -// std::cout << "********************************************************************************************************************************************" <<std::endl; -// -// /* SETUP OF THE TRAINING DATA */ -// std::vector<double> inp, out; -// -// double frac, alpha, x; -// double grad_norm = accuracy * 10.0, mem, ai, bi, wi, error, derror, approx, xj, gamma, total_error, sk, sy, sx, sg, beta; -// double grad_norm_prev = grad_norm; -// size_t i, j, iter_idx = 0; -// -// /* TRAIN DATA FOR THE GOVERNING DE */ -// std::vector<double> data_points(train_size); -// -// /* ISOTROPIC TRAIN SET */ -// frac = (d1_e - d1_s) / (train_size - 1); -// for(unsigned int i = 0; i < train_size; ++i){ -// data_points[i] = frac * i; -// } -// -//// /* CHEBYSCHEV TRAIN SET */ -//// alpha = PI / (train_size ); -//// frac = 0.5 * (d1_e - d1_s); -//// for(i = 0; i < train_size; ++i){ -//// x = (std::cos(PI - alpha * i) + 1.0) * frac + d1_s; -//// data_points[i] = x; -//// } -// -//// DataSet ds(0.0, 4.0, train_size, 0.0); -// -// std::vector<double> *gradient_current = new std::vector<double>(3 * n_inner_neurons); -// std::vector<double> *gradient_prev = new std::vector<double>(3 * n_inner_neurons); -// std::vector<double> *params_current = new std::vector<double>(guess); -// std::vector<double> *params_prev = new std::vector<double>(guess); -// std::vector<double> *conjugate_direction_current = new std::vector<double>(3 * n_inner_neurons); -// std::vector<double> *conjugate_direction_prev = new std::vector<double>(3 * n_inner_neurons); -// -// std::vector<double> *ptr_mem; -// -// -// std::fill(gradient_current->begin(), gradient_current->end(), 0.0); -// std::fill(gradient_prev->begin(), gradient_prev->end(), 0.0); -// std::fill(conjugate_direction_current->begin(), conjugate_direction_current->end(), 0.0); -// std::fill(conjugate_direction_prev->begin(), conjugate_direction_prev->end(), 0.0); -// -// -// -//// for (i = 0; i < n_inner_neurons; ++i) { -//// wi = (*params_current)[3 * i]; -//// ai = (*params_current)[3 * i + 1]; -//// bi = (*params_current)[3 * i + 2]; -//// -//// printf("Path %3d. w = %15.8f, b = %15.8f, a = %15.8f\n", (int)(i + 1), wi, bi, ai); -//// } -// -// gamma = 1.0; -// double prev_val, val = 0.0, c = 2.0; -// while( grad_norm > accuracy) { -// iter_idx++; -// prev_val = val; -// grad_norm_prev = grad_norm; -// -// /* reset of the current gradient */ -// std::fill(gradient_current->begin(), gradient_current->end(), 0.0); -// val = calculate_gradient( data_points, n_inner_neurons, params_current, gradient_current ); -// -// grad_norm = 0.0; -// for(auto v: *gradient_current){ -// grad_norm += v * v; -// } -// grad_norm = std::sqrt(grad_norm); -// -// /* Update of the parameters */ -// /* step length calculation */ -// if(iter_idx < 10 || iter_idx % 100 == 0){ -// /* fixed step length */ -// gamma = 0.1 * accuracy; -// } -// else{ -// -// -// -// /* norm of the gradient calculation */ -// -// sk = 0.0; -// for(i = 0; i < gradient_current->size(); ++i){ -// sx = (*gradient_current)[i] - (*gradient_prev)[i]; -// sk += sx * sx; -// } -// sk = std::sqrt(sk); -// -// /* angle between two consecutive gradients */ -// double beta = 0.0, sx = 0.0; -// for(i = 0; i < gradient_current->size(); ++i){ -// sx += (gradient_current->at( i ) * gradient_prev->at( i )); -// } -// sx /= grad_norm * grad_norm_prev; -// beta = std::sqrt(std::acos( sx ) / PI); -// -// -//// eval_step_size_simple( gamma, val, prev_val, sk, grad_norm, grad_norm_prev ); -// eval_step_size_mk( gamma, beta, c, grad_norm_prev, grad_norm, val, prev_val ); -// } -// -// -// -// -// -// -// for(i = 0; i < gradient_current->size(); ++i){ -// (*params_prev)[i] = (*params_current)[i] - gamma * (*gradient_current)[i]; -// } -// -// /* switcheroo */ -// ptr_mem = gradient_prev; -// gradient_prev = gradient_current; -// gradient_current = ptr_mem; -// -// ptr_mem = params_prev; -// params_prev = params_current; -// params_current = ptr_mem; -// -// -// if(iter_idx % 1 == 0){ -// printf("Iteration %12d. Step size: %15.8f, C: %15.8f, Gradient norm: %15.8f. Total error: %10.8f\r", (int)iter_idx, gamma, c, grad_norm, val); -// std::cout.flush(); -// } -// } -// printf("Iteration %12d. Step size: %15.8f, C: %15.8f, Gradient norm: %15.8f. Total error: %10.8f\r\n", (int)iter_idx, gamma, c, grad_norm, val); -// std::cout.flush(); -// -// for (i = 0; i < n_inner_neurons; ++i) { -// wi = (*params_current)[3 * i]; -// ai = (*params_current)[3 * i + 1]; -// bi = (*params_current)[3 * i + 2]; -// -// printf("Path %3d. w%d = %15.8f, b%d = %15.8f, a%d = %15.8f\n", (int)(i + 1), (int)(i + 1), wi, (int)(i + 1), bi, (int)(i + 1), ai); -// } -// std::cout << "********************************************************************************************************************************************" <<std::endl; -// -// -//// data_export_gradient(params_current); -//// if(total_error < 1e-3 || true){ -//// /* ISOTROPIC TEST SET */ -//// frac = (te - ts) / (test_size - 1); -//// for(j = 0; j < test_size; ++j){ -//// xj = frac * j + ts; -//// -//// std::cout << j + 1 << " " << xj << " " << eval_f(xj) << " " << eval_approx_f(xj, n_inner_neurons, *params_current) << " " << eval_df(xj) << " " << eval_approx_df(xj, n_inner_neurons, *params_current) << " " << eval_ddf(xj) << " " << eval_approx_ddf(xj, n_inner_neurons, *params_current) << std::endl; -//// } -//// } -// -// delete gradient_current; -// delete gradient_prev; -// delete params_current; -// delete params_prev; -// delete conjugate_direction_current; -// delete conjugate_direction_prev; -//} - -void test_ode(double accuracy, size_t n_inner_neurons, size_t train_size, double ds, double de, size_t n_test_points, double ts, double te, size_t max_iters, size_t n_particles){ - - std::cout << "Finding a solution via the Particle Swarm Optimization" << std::endl; - std::cout << "********************************************************************************************************************************************" <<std::endl; +void test_ode(double accuracy, + size_t n_inner_neurons, + size_t train_size, + double ds, + double de, + size_t n_test_points, + double ts, + double te, + size_t max_iters, + size_t n_particles) { + + std::cout << "Finding a solution via the Particle Swarm Optimization and Gradient descent method!" << std::endl; + std::cout + << "********************************************************************************************************************************************" + << std::endl; /* SOLVER SETUP */ - size_t n_inputs = 1; - size_t n_equations = 3; - DESolver solver_01( n_equations, n_inputs, n_inner_neurons ); + size_t n_inputs = 1; + size_t n_equations = 3; + l4n::DESolver solver_01(n_equations, + n_inputs, + n_inner_neurons); /* SETUP OF THE EQUATIONS */ - MultiIndex alpha_0( n_inputs ); - MultiIndex alpha_1( n_inputs ); - MultiIndex alpha_2( n_inputs ); - alpha_2.set_partial_derivative(0, 2); - alpha_1.set_partial_derivative(0, 1); + l4n::MultiIndex alpha_0(n_inputs); + l4n::MultiIndex alpha_1(n_inputs); + l4n::MultiIndex alpha_2(n_inputs); + alpha_2.set_partial_derivative(0, + 2); + alpha_1.set_partial_derivative(0, + 1); /* the governing differential equation */ - solver_01.add_to_differential_equation( 0, alpha_2, "1.0" ); - solver_01.add_to_differential_equation( 0, alpha_1, "4.0" ); - solver_01.add_to_differential_equation( 0, alpha_0, "4.0" ); + solver_01.add_to_differential_equation(0, + alpha_0, + "4.0"); + solver_01.add_to_differential_equation(0, + alpha_1, + "4.0"); + solver_01.add_to_differential_equation(0, + alpha_2, + "1.0"); /* dirichlet boundary condition */ - solver_01.add_to_differential_equation( 1, alpha_0, "1.0" ); + solver_01.add_to_differential_equation(1, + alpha_0, + "1.0"); /* neumann boundary condition */ - solver_01.add_to_differential_equation( 2, alpha_1, "1.0" ); + solver_01.add_to_differential_equation(2, + alpha_1, + "1.0"); /* SETUP OF THE TRAINING DATA */ - std::vector<double> inp, out; + std::vector<double> inp(1), out(1); double d1_s = ds, d1_e = de, frac; /* TRAIN DATA FOR THE GOVERNING DE */ std::vector<std::pair<std::vector<double>, std::vector<double>>> data_vec_g; - std::vector<double> test_points(train_size); + std::vector<double> test_points(train_size); /* ISOTROPIC TRAIN SET */ frac = (d1_e - d1_s) / (train_size - 1); - for(unsigned int i = 0; i < train_size; ++i){ - inp = {frac * i}; - out = {0.0}; - data_vec_g.emplace_back(std::make_pair(inp, out)); + for (unsigned int i = 0; i < train_size; ++i) { + inp[0] = frac * i; + out[0] = 0.0; + data_vec_g.push_back(std::make_pair(inp, + out)); test_points[i] = inp[0]; } /* CHEBYSCHEV TRAIN SET */ -// alpha = PI / (train_size - 1); -// frac = 0.5 * (d1_e - d1_s); -// for(unsigned int i = 0; i < train_size; ++i){ -// inp = {(std::cos(alpha * i) + 1.0) * frac + d1_s}; -// out = {0.0}; -// data_vec_g.emplace_back(std::make_pair(inp, out)); -// -// test_points[i] = inp[0]; -// } - DataSet ds_00(&data_vec_g); + l4n::DataSet ds_00(&data_vec_g); /* TRAIN DATA FOR DIRICHLET BC */ std::vector<std::pair<std::vector<double>, std::vector<double>>> data_vec_y; inp = {0.0}; out = {1.0}; - data_vec_y.emplace_back(std::make_pair(inp, out)); - DataSet ds_01(&data_vec_y); + data_vec_y.emplace_back(std::make_pair(inp, + out)); + l4n::DataSet ds_01(&data_vec_y); /* TRAIN DATA FOR NEUMANN BC */ std::vector<std::pair<std::vector<double>, std::vector<double>>> data_vec_dy; inp = {0.0}; out = {1.0}; - data_vec_dy.emplace_back(std::make_pair(inp, out)); - DataSet ds_02(&data_vec_dy); + data_vec_dy.emplace_back(std::make_pair(inp, + out)); + l4n::DataSet ds_02(&data_vec_dy); /* Placing the conditions into the solver */ - solver_01.set_error_function( 0, ErrorFunctionType::ErrorFuncMSE, &ds_00 ); - solver_01.set_error_function( 1, ErrorFunctionType::ErrorFuncMSE, &ds_01 ); - solver_01.set_error_function( 2, ErrorFunctionType::ErrorFuncMSE, &ds_02 ); - - - size_t total_dim = (2 + n_inputs) * n_inner_neurons; - - std::vector<double> params(total_dim), params_analytical(total_dim); - std::random_device seeder; - std::mt19937 gen(seeder()); - std::uniform_real_distribution<double> dist(-10.0, 10.0); - - std::vector<double> input(1); -// for( size_t testi = 0; testi < 50; ++testi ){ -// double test_error_eq1 = 0.0, total_error = 0.0; -// for(size_t i = 0; i < params.size(); ++i){ -// params[i] = dist(gen); -// } -// for(size_t i = 0; i < n_inner_neurons; ++i){ -// params_analytical[3 * i] = params[i]; -// params_analytical[3 * i + 1] = params[n_inner_neurons + i]; -// params_analytical[3 * i + 2] = params[2 * n_inner_neurons + i]; -// } -// -// for(auto d: *ds_00.get_data()){ -// input = d.first; -// double x = input[0]; -// -// double analytical_value_f = eval_approx_f(x, n_inner_neurons, params_analytical); -// double analytical_value_df = eval_approx_df(x, n_inner_neurons, params_analytical); -// double analytical_value_ddf = eval_approx_ddf(x, n_inner_neurons, params_analytical); -// -// double de_solver_value_eq1 = solver_01.eval_equation(0, ¶ms, input); -// double analytical_value_eq1 = 4 * analytical_value_f + 4 * analytical_value_df + analytical_value_ddf; -// test_error_eq1 += (de_solver_value_eq1 - analytical_value_eq1) * (de_solver_value_eq1 - analytical_value_eq1); -// -// } -// input[0] = 0.0; -// double de_solver_value_eq2 = solver_01.eval_equation(1, ¶ms, input); -// double analytical_value_eq2 = eval_approx_f(0.0, n_inner_neurons, params_analytical); -// double test_error_eq2 = (de_solver_value_eq2 - analytical_value_eq2) * (de_solver_value_eq2 - analytical_value_eq2); -// -// double de_solver_value_eq3 = solver_01.eval_equation(2, ¶ms, input); -// double analytical_value_eq3 = eval_approx_df(0.0, n_inner_neurons, params_analytical); -// double test_error_eq3 = (de_solver_value_eq3 - analytical_value_eq3) * (de_solver_value_eq3 - analytical_value_eq3); -// -// double total_error_de_solver = solver_01.eval_total_error(params); -// -// double total_error_analytical = eval_error_function(params_analytical, n_inner_neurons, test_points); -// -// printf("\tRepresentation test %6d, error of eq1: %10.8f, error of eq2: %10.8f, error of eq3: %10.8f, total error: %10.8f\n", (int)testi, std::sqrt(test_error_eq1), std::sqrt(test_error_eq2), std::sqrt(test_error_eq3), (total_error_analytical - total_error_de_solver) * (total_error_analytical - total_error_de_solver)); -// } - - /* TRAINING METHOD SETUP */ - std::vector<double> domain_bounds(2 * (solver_01.get_solution( alpha_0 )->get_n_biases() + solver_01.get_solution( alpha_0 )->get_n_weights())); - - for(size_t i = 0; i < domain_bounds.size() / 2; ++i){ - domain_bounds[2 * i] = -10; - domain_bounds[2 * i + 1] = 10; - } - - double c1 = 1.7; - double c2 = 1.7; - double w = 0.700; - - /* if the maximal velocity from the previous step is less than 'gamma' times the current maximal velocity, then one - * terminating criterion is met */ - double gamma = 0.5; + solver_01.set_error_function(0, + l4n::ErrorFunctionType::ErrorFuncMSE, + ds_00); - /* if 'delta' times 'n' particles are in the centroid neighborhood given by the radius 'epsilon', then the second - * terminating criterion is met ('n' is the total number of particles) */ - double epsilon = 0.02; - double delta = 0.7; - - ParticleSwarm swarm_01( - &domain_bounds, - c1, - c2, - w, - gamma, - epsilon, - delta, - n_particles, - max_iters - ); - solver_01.solve( swarm_01 ); - - NeuralNetwork *solution = solver_01.get_solution( alpha_0 ); - NeuralNetwork *solution_d = solver_01.get_solution( alpha_1 ); - NeuralNetwork *solution_dd = solver_01.get_solution( alpha_2 ); + solver_01.set_error_function(1, + l4n::ErrorFunctionType::ErrorFuncMSE, + ds_01); + solver_01.set_error_function(2, + l4n::ErrorFunctionType::ErrorFuncMSE, + ds_02); - std::vector<double> parameters(total_dim);//w1, a1, b1, w2, a2, b2, ... , wm, am, bm - std::vector<double> *weight_params = solution->get_parameter_ptr_weights(); - std::vector<double> *biases_params = solution->get_parameter_ptr_biases(); - for(size_t i = 0; i < n_inner_neurons; ++i){ - parameters[3 * i] = weight_params->at(i); - parameters[3 * i + 1] = weight_params->at(i + n_inner_neurons); - parameters[3 * i + 2] = biases_params->at(i); - printf("Path %3d. w%d = %15.8f, b%d = %15.8f, a%d = %15.8f\n", (int)(i + 1), (int)(i + 1), parameters[3 * i], (int)(i + 1), parameters[3 * i + 2], (int)(i + 1), parameters[3 * i + 1]); - } - - /* ISOTROPIC TEST SET FOR BOUNDARY CONDITIONS */ - /* first boundary condition & its error */ - /* - std::ofstream ofs("data_1d_ode1.txt", std::ofstream::out); - printf("Exporting files 'data_1d_ode1.txt': %7.3f%%\r", 0.0); - frac = (te - ts) / (n_test_points - 1); - - for(size_t i = 0; i < n_test_points; ++i){ - double x = frac * i + ts; - inp[0] = x; - - solution->eval_single(inp, out); - double F = out[0]; - - solution_d->eval_single( inp, out); - double DF = out[0]; - - solution_dd->eval_single( inp, out); - double DDF = out[0]; - - ofs << i + 1 << " " << x << " " << std::pow(E, -2*x) * (3*x + 1)<< " " << F << " " << std::pow(E, -2*x) * (1 - 6*x)<< " " << DF << " " << 4 * std::pow(E, -2*x) * (3*x - 2)<< " " << DDF << std::endl; - - printf("Exporting files 'data_1d_ode1.txt': %7.3f%%\r", (100.0 * i) / (n_test_points - 1)); - std::cout.flush(); - } - printf("Exporting files 'data_1d_ode1.txt': %7.3f%%\n", 100.0); - std::cout.flush(); - ofs.close(); - - std::cout << "********************************************************************************************************************************************" <<std::endl; - */ + /* TRAINING METHOD SETUP */ + /* optimize_via_particle_swarm( solver_01, alpha_0, max_iters, n_particles ); + export_solution( n_test_points, te, ts, solver_01 , alpha_0, alpha_1, alpha_2, "particle_" );*/ + auto start = std::chrono::system_clock::now(); + + optimize_via_gradient_descent(solver_01, + accuracy); + export_solution(n_test_points, + te, + ts, + solver_01, + alpha_0, + alpha_1, + alpha_2, + "gradient_"); + + auto end = std::chrono::system_clock::now(); + std::chrono::duration<double> elapsed_seconds = end - start; + std::cout << "elapsed time: " << elapsed_seconds.count() << std::endl; } int main() { std::cout << "Running lib4neuro Ordinary Differential Equation example 1" << std::endl; - std::cout << "********************************************************************************************************************************************" <<std::endl; + std::cout + << "********************************************************************************************************************************************" + << std::endl; std::cout << " Governing equation: y''(x) + 4y'(x) + 4y(x) = 0.0, for x in [0, 4]" << std::endl; std::cout << "Dirichlet boundary condition: y(0.0) = 1.0" << std::endl; std::cout << " Neumann boundary condition: y'(0.0) = 1.0" << std::endl; - std::cout << "********************************************************************************************************************************************" <<std::endl; - std::cout << "Expressing solution as y(x) = sum over [a_i / (1 + exp(bi - wxi*x ))], i in [1, n], where n is the number of hidden neurons" <<std::endl; - std::cout << "********************************************************************************************************************************************" <<std::endl; + std::cout + << "********************************************************************************************************************************************" + << std::endl; + std::cout + << "Expressing solution as y(x) = sum over [a_i / (1 + exp(bi - wxi*x ))], i in [1, n], where n is the number of hidden neurons" + << std::endl; + std::cout + << "********************************************************************************************************************************************" + << std::endl; unsigned int n_inner_neurons = 2; - unsigned int train_size = 10; - double accuracy = 1e-3; - double ds = 0.0; - double de = 4.0; - - unsigned int test_size = 300; - double ts = ds; - double te = de + 2; - - size_t particle_swarm_max_iters = 1000; - size_t n_particles = 100; - test_ode(accuracy, n_inner_neurons, train_size, ds, de, test_size, ts, te, particle_swarm_max_iters, n_particles); - -// std::vector<double> init_guess = {0.35088209, -0.23738505, 0.14160885, 3.72785473, -6.45758308, 1.73769138}; -// std::vector<double> init_guess(3 * n_inner_neurons); -// -// std::random_device seeder; -// std::mt19937 gen(seeder()); -// std::uniform_real_distribution<double> dist(-1.0, 1.0); -// for(unsigned int i = 0; i < init_guess.size(); ++i){ -// init_guess[i] = dist(gen); -// } - - //TODO, sometimes produces nans in combinations with extremely high number of iterations, INVESTIGATE -// test_analytical_gradient_y(init_guess, accuracy, n_inner_neurons, train_size, ds, de, test_size, ts, te); + unsigned int train_size = 10; + double accuracy = 1e-1; + double ds = 0.0; + double de = 4.0; + + unsigned int test_size = 10; + double ts = ds; + double te = de + 2; + + size_t particle_swarm_max_iters = 10; + size_t n_particles = 2; + + test_ode(accuracy, + n_inner_neurons, + train_size, + ds, + de, + test_size, + ts, + te, + particle_swarm_max_iters, + n_particles); return 0; diff --git a/src/examples/net_test_pde_1.cpp b/src/examples/net_test_pde_1.cpp index 48913ef4ba6891d59bfa8b22ab09f573d11ca2e0..b69cd9193811c0523d3e2a998bad953603149aa9 100644 --- a/src/examples/net_test_pde_1.cpp +++ b/src/examples/net_test_pde_1.cpp @@ -24,696 +24,23 @@ #include "4neuro.h" -//y(x, t) = ... ai * f(wxi * x + wti * t - bi) -double eval_approx_y(double x, double t, size_t n_inner_neurons, std::vector<double> ¶meters){ - double value= 0.0, wxi, wti, ai, bi, ei, ei1; - for(size_t i = 0; i < n_inner_neurons; ++i){ +void optimize_via_particle_swarm(l4n::DESolver& solver, + l4n::MultiIndex& alpha, + size_t max_iters, + size_t n_particles) { - wxi = parameters[4 * i + 0]; - wti = parameters[4 * i + 1]; - ai = parameters[4 * i + 2]; - bi = parameters[4 * i + 3]; + printf("Solution via the particle swarm optimization!\n"); + std::vector<double> domain_bounds( + 2 * (solver.get_solution(alpha)->get_n_biases() + solver.get_solution(alpha)->get_n_weights())); - ei = std::pow(E, bi - wxi * x - wti * t); - ei1 = ei + 1.0; - - value += ai / (ei1); - } - return value; -} -//yt(x, t) = ... (ai * wti * e^(bi - wti * t - wxi * x))/(e^(bi - wti * t - wxi * x) + 1)^2 -double eval_approx_yt(double x, double t, size_t n_inner_neurons, std::vector<double> ¶meters){ - double value= 0.0, wxi, wti, ai, bi, ei, ei1; - - for(size_t i = 0; i < n_inner_neurons; ++i){ - - wxi = parameters[4 * i + 0]; - wti = parameters[4 * i + 1]; - ai = parameters[4 * i + 2]; - bi = parameters[4 * i + 3]; - - ei = std::pow(E, bi - wxi * x - wti * t); - ei1 = ei + 1.0; - - value += ai * wti * ei / (ei1 * ei1); - } - return value; -} -////yx(x, t) = ... (ai * wxi * e^(bi - t * wti - wxi * x))/(e^(bi - t * wti - wxi * x) + 1)^2 -//double eval_approx_yx(double x, double t, size_t n_inner_neurons, std::vector<double> ¶meters){ -// double value= 0.0, wxi, wti, ai, bi, ei, ei1; -// -// for(size_t i = 0; i < n_inner_neurons; ++i){ -// -// wxi = parameters[4 * i + 0]; -// wti = parameters[4 * i + 1]; -// ai = parameters[4 * i + 2]; -// bi = parameters[4 * i + 3]; -// -// ei = std::pow(E, bi - wxi * x - wti * t); -// ei1 = ei + 1.0; -// -// value += (ai * wxi * ei1)/(ei1 * ei1); -// } -// return value; -//} -//yxx(x, t) = ... (ai * wxi * e^(bi - t * wti - wxi * x))/(e^(bi - t * wti - wxi * x) + 1)^2 -double eval_approx_yxx(double x, double t, size_t n_inner_neurons, std::vector<double> ¶meters){ - double value= 0.0, wxi, wti, ai, bi, ei, ei1; - for(size_t i = 0; i < n_inner_neurons; ++i){ - - wxi = parameters[4 * i + 0]; - wti = parameters[4 * i + 1]; - ai = parameters[4 * i + 2]; - bi = parameters[4 * i + 3]; - - ei = std::pow(E, bi - wxi * x - wti * t); - ei1 = ei + 1.0; - - value += (2 * ai * wxi * wxi * ei * ei) / (ei1 * ei1 * ei1) - (ai * wxi * wxi * ei) / (ei1 * ei1); - } - return value; -} -// -//double eval_approx_da_y(double x, double t, size_t neuron_idx, std::vector<double> ¶meters){ -// double wxi, wti, bi, ei, ei1; -// -// wxi = parameters[4 * neuron_idx + 0]; -// wti = parameters[4 * neuron_idx + 1]; -// bi = parameters[4 * neuron_idx + 3]; -// -// ei = std::pow(E, bi - wxi * x - wti * t); -// ei1 = ei + 1.0; -// -// return 1.0 / ei1; -//} -// -//double eval_approx_dwx_y(double x, double t, size_t neuron_idx, std::vector<double> ¶meters){ -// double wxi, wti, ai, bi, ei, ei1; -// wxi = parameters[4 * neuron_idx + 0]; -// wti = parameters[4 * neuron_idx + 1]; -// ai = parameters[4 * neuron_idx + 2]; -// bi = parameters[4 * neuron_idx + 3]; -// -// ei = std::pow(E, bi - wxi * x - wti * t); -// ei1 = ei + 1.0; -// -// return (ai * x * ei)/(ei1 * ei1); -//} -// -//double eval_approx_dwt_y(double x, double t, size_t neuron_idx, std::vector<double> ¶meters){ -// double wxi, wti, ai, bi, ei, ei1; -// wxi = parameters[4 * neuron_idx + 0]; -// wti = parameters[4 * neuron_idx + 1]; -// ai = parameters[4 * neuron_idx + 2]; -// bi = parameters[4 * neuron_idx + 3]; -// -// ei = std::pow(E, bi - wxi * x - wti * t); -// ei1 = ei + 1.0; -// -// return (ai * t * ei)/(ei1 * ei1); -//} -// -//double eval_approx_db_y(double x, double t, size_t neuron_idx, std::vector<double> ¶meters){ -// double wxi, wti, bi, ei, ai, ei1; -// wxi = parameters[4 * neuron_idx + 0]; -// wti = parameters[4 * neuron_idx + 1]; -// ai = parameters[4 * neuron_idx + 2]; -// bi = parameters[4 * neuron_idx + 3]; -// -// ei = std::pow(E, bi - wxi * x - wti * t); -// ei1 = ei + 1.0; -// -// return -(ai * ei)/(ei1 * ei1); -//} -// -//double eval_approx_da_yt(double x, double t, size_t neuron_idx, std::vector<double> ¶meters){ -// double wxi, wti, bi, ei, ei1; -// -// wxi = parameters[4 * neuron_idx + 0]; -// wti = parameters[4 * neuron_idx + 1]; -// bi = parameters[4 * neuron_idx + 3]; -// -// ei = std::pow(E, bi - wxi * x - wti * t); -// ei1 = ei + 1.0; -// -// return (wti * ei)/(ei1 * ei1); -//} -// -//double eval_approx_dwx_yt(double x, double t, size_t neuron_idx, std::vector<double> ¶meters){ -// double wxi, wti, ai, bi, ei, ei1; -// wxi = parameters[4 * neuron_idx + 0]; -// wti = parameters[4 * neuron_idx + 1]; -// ai = parameters[4 * neuron_idx + 2]; -// bi = parameters[4 * neuron_idx + 3]; -// -// ei = std::pow(E, bi - wxi * x - wti * t); -// ei1 = ei + 1.0; -// -// return (2 * ai * wti * x * ei * ei)/(ei1 * ei1 * ei1) - (ai * wti * x * ei)/(ei1 * ei1); -//} -// -//double eval_approx_dwt_yt(double x, double t, size_t neuron_idx, std::vector<double> ¶meters){ -// double wxi, wti, ai, bi, ei, ei1; -// wxi = parameters[4 * neuron_idx + 0]; -// wti = parameters[4 * neuron_idx + 1]; -// ai = parameters[4 * neuron_idx + 2]; -// bi = parameters[4 * neuron_idx + 3]; -// -// ei = std::pow(E, bi - wxi * x - wti * t); -// ei1 = ei + 1.0; -// -// return -(ai * t * wti * ei) / (ei1 * ei1) + (2 * ai * t * wti * ei * ei)/(ei1 * ei1 * ei1) + (ai * ei)/(ei1 * ei1); -//} -// -//double eval_approx_db_yt(double x, double t, size_t neuron_idx, std::vector<double> ¶meters){ -// double wxi, wti, ai, bi, ei, ei1; -// wxi = parameters[4 * neuron_idx + 0]; -// wti = parameters[4 * neuron_idx + 1]; -// ai = parameters[4 * neuron_idx + 2]; -// bi = parameters[4 * neuron_idx + 3]; -// -// ei = std::pow(E, bi - wxi * x - wti * t); -// ei1 = ei + 1.0; -// -// return (ai * wti * ei) / (ei1 * ei1) - (2 * ai * wti * ei * ei) / (ei1 * ei1 * ei1); -//} -// -//double eval_approx_da_yxx(double x, double t, size_t neuron_idx, std::vector<double> ¶meters){ -// double wxi, wti, ai, bi, ei, ei1, ebp, eb, etx; -// wxi = parameters[4 * neuron_idx + 0]; -// wti = parameters[4 * neuron_idx + 1]; -// ai = parameters[4 * neuron_idx + 2]; -// bi = parameters[4 * neuron_idx + 3]; -// -// ei = std::pow(E, bi - wxi * x - wti * t); -// ebp= std::pow(E, bi + wxi * x + wti * t); -// eb = std::pow(E, bi); -// etx = std::pow(E, wxi * x + wti * t); -// ei1 = eb + etx; -// -// return -(wxi * wxi * ebp * (etx - eb))/(ei1 * ei1 * ei1); -//} -// -//double eval_approx_dwx_yxx(double x, double t, size_t neuron_idx, std::vector<double> ¶meters){ -// double wxi, wti, ai, bi, ei, ei1, ebp, eb, etx; -// wxi = parameters[4 * neuron_idx + 0]; -// wti = parameters[4 * neuron_idx + 1]; -// ai = parameters[4 * neuron_idx + 2]; -// bi = parameters[4 * neuron_idx + 3]; -// -// ei = std::pow(E, bi - wxi * x - wti * t); -// ebp= std::pow(E, bi + wxi * x + wti * t); -// eb = std::pow(E, bi); -// etx = std::pow(E, wxi * x + wti * t); -// ei1 = eb + etx; -// -// return (ai * wxi * wxi * x * ei) / ((ei + 1) * (ei + 1)) - (6 * ai * wxi * wxi * x * ei * ei) / ((ei + 1) * (ei + 1) * (ei + 1)) + (6 * ai * wxi *wxi * x * ei * ei * ei) / ((ei + 1) * (ei + 1) * (ei + 1) * (ei + 1)) - (2 * ai * wxi * ei) / ((ei + 1) * (ei + 1)) + (4 * ai * wxi * ei * ei)/((ei + 1) * (ei + 1) * (ei + 1)); -//} -// -//double eval_approx_dwt_yxx(double x, double t, size_t neuron_idx, std::vector<double> ¶meters){ -// double wxi, wti, ai, bi, ei, ei1, ebp, eb, etx; -// wxi = parameters[4 * neuron_idx + 0]; -// wti = parameters[4 * neuron_idx + 1]; -// ai = parameters[4 * neuron_idx + 2]; -// bi = parameters[4 * neuron_idx + 3]; -// -// ei = std::pow(E, bi - wxi * x - wti * t); -// ebp= std::pow(E, bi + wxi * x + wti * t); -// eb = std::pow(E, bi); -// etx = std::pow(E, wxi * x + wti * t); -// ei1 = eb + etx; -// -// return (ai * t * wxi * wxi * ei) / ((ei + 1) * (ei + 1)) - (6 * ai * t * wxi * wxi * ei * ei) / ((ei + 1) * (ei + 1) * (ei + 1)) + (6 * ai * t * wxi * wxi * ei * ei * ei) / ((ei + 1) * (ei + 1) * (ei + 1) * (ei + 1)); -//} -// -//double eval_approx_db_yxx(double x, double t, size_t neuron_idx, std::vector<double> ¶meters){ -// double wxi, wti, ai, bi, ei, ei1, ebp, eb, etx; -// wxi = parameters[4 * neuron_idx + 0]; -// wti = parameters[4 * neuron_idx + 1]; -// ai = parameters[4 * neuron_idx + 2]; -// bi = parameters[4 * neuron_idx + 3]; -// -// ei = std::pow(E, bi - wxi * x - wti * t); -// ebp= std::pow(E, bi + wxi * x + wti * t); -// eb = std::pow(E, bi); -// etx = std::pow(E, wxi * x + wti * t); -// ei1 = eb + etx; -// -// return (ai * wxi * wxi * eb * ebp) / (ei1 * ei1 * ei1) - (ai * wxi * wxi * ebp * (etx - eb)) / (ei1 * ei1 * ei1) + (3 * ai * wxi * wxi * eb * ebp * (etx - eb)) / (ei1 * ei1 * ei1 * ei1); -//} -// -//void eval_step_size_simple(double &gamma, double val, double prev_val, double sk, double grad_norm, double grad_norm_prev){ -// -// if(val > prev_val){ -// gamma *= 0.99999; -// } -// -// if(sk <= 1e-3 || grad_norm < grad_norm_prev){ -// /* movement on a line */ -// /* new slope is less steep, speed up */ -// gamma *= 1.0005; -// } -// else if(grad_norm > grad_norm_prev){ -// /* new slope is more steep, slow down*/ -// gamma /= 1.0005; -// } -// else{ -// gamma /= 1.005; -// } -//// gamma *= 0.999999; -//} -// -//void eval_step_size_mk( double &gamma, double beta, double &c, double grad_norm_prev, double grad_norm, double fi, double fim ){ -// -// if( fi > fim ) -// { -// c /= 1.0000005; -// } -// else if( fi < fim ) -// { -// c *= 1.0000005; -// } -// -// gamma *= std::pow( c, 1.0 - 2.0 * beta) * std::pow( grad_norm_prev / grad_norm, 1.0 / c ); -// -// -//} -// -//double calculate_gradient( std::vector<double> &data_points, size_t n_inner_neurons, std::vector<double> *parameters, std::vector<double> *gradient ){ -// size_t i, j, k; -// double x, t, mem, derror, total_error, approx; -// -// size_t train_size = data_points.size(); -// -// /* error of boundary condition: y(0, t) = sin(t) => e1 = 1/n * (sin(t) - y(0, t))^2 */ -// for(i = 0; i < train_size; ++i){ -// -// t = data_points[i]; -// mem = (std::sin(t) - eval_approx_y(0.0, t, n_inner_neurons, *parameters)); -// derror = 2.0 * mem / train_size; -// -// for(j = 0; j < n_inner_neurons; ++j){ -// (*gradient)[4 * j + 0] -= derror * eval_approx_dwx_y(0, t, j, *parameters); -// (*gradient)[4 * j + 1] -= derror * eval_approx_dwt_y(0, t, j, *parameters); -// (*gradient)[4 * j + 2] -= derror * eval_approx_da_y(0, t, j, *parameters); -// (*gradient)[4 * j + 3] -= derror * eval_approx_db_y(0, t, j, *parameters); -// } -// -// total_error += mem * mem / train_size; -// } -// -// -// -// /* error boundary condition: y(x, 0) = e^(-(0.5)^(0.5)x) * sin(-(0.5)^(0.5)x) => e2 = 1/n * (e^(-(0.5)^(0.5)x) * sin(-(0.5)^(0.5)x) - y(x, 0))^2 */ -// for(i = 0; i < train_size; ++i){ -// -// x = data_points[i]; -// mem = (std::pow(E, -0.707106781 * x) * std::sin( -0.707106781 * x ) - eval_approx_y(x, 0.0, n_inner_neurons, *parameters)); -// derror = 2.0 * mem / train_size; -// -// for(j = 0; j < n_inner_neurons; ++j){ -// (*gradient)[4 * j + 0] -= derror * eval_approx_dwx_y(x, 0, j, *parameters); -// (*gradient)[4 * j + 1] -= derror * eval_approx_dwt_y(x, 0, j, *parameters); -// (*gradient)[4 * j + 2] -= derror * eval_approx_da_y(x, 0, j, *parameters); -// (*gradient)[4 * j + 3] -= derror * eval_approx_db_y(x, 0, j, *parameters); -// } -// -// total_error += mem * mem / train_size; -// } -// -// /* error of the governing equation: y_xx - y_t = 0 => e3 = 1/n^2 * (0 - y_xx + y_t)^2 */ -// for(i = 0; i < data_points.size(); ++i){ -// x = data_points[i]; -// for(j = 0; j < data_points.size(); ++j){ -// t = data_points[j]; -// -// approx = eval_approx_yxx(x, t, n_inner_neurons, *parameters) - eval_approx_yt(x, t, n_inner_neurons, *parameters); -// mem = 0.0 - approx; -// derror = 2.0 * mem / (train_size * train_size); -// for(k = 0; k < n_inner_neurons; ++k){ -// (*gradient)[4 * k + 0] -= derror * (eval_approx_dwx_yxx(x, t, k, *parameters) - eval_approx_dwx_yt(x, t, k, *parameters)); -// (*gradient)[4 * k + 1] -= derror * (eval_approx_dwt_yxx(x, t, k, *parameters) - eval_approx_dwt_yt(x, t, k, *parameters)); -// (*gradient)[4 * k + 2] -= derror * ( eval_approx_da_yxx(x, t, k, *parameters) - eval_approx_da_yt(x, t, k, *parameters)); -// (*gradient)[4 * k + 3] -= derror * ( eval_approx_db_yxx(x, t, k, *parameters) - eval_approx_db_yt(x, t, k, *parameters)); -// } -// total_error += mem * mem / (train_size * train_size); -// } -// } -// return total_error; -//} -// -//void solve_example_gradient(std::vector<double> &guess, double accuracy, size_t n_inner_neurons, size_t train_size, double ds, double de, size_t n_test_points, double ts, double te){ -// std::cout << "Finding a solution via a Gradient Descent method with adaptive step-length" << std::endl; -// std::cout << "********************************************************************************************************************************************" <<std::endl; -// /* SETUP OF THE TRAINING DATA */ -// std::vector<double> inp, out; -// -// double frac, alpha, x; -// double grad_norm = accuracy * 10.0, mem, ai, bi, wxi, wti, error, derror, approx, t, gamma, total_error, sk, sy, sx, sg, beta; -// double grad_norm_prev = grad_norm; -// size_t i, j, k, iter_idx = 0; -// -// /* TRAIN DATA FOR THE GOVERNING DE */ -// std::vector<double> data_points(train_size); -// -// /* ISOTROPIC TRAIN SET */ -// frac = (de - ds) / (train_size - 1); -// for(i = 0; i < train_size; ++i){ -// data_points[i] = frac * i + ds; -//// std::cout << data_points[i] << std::endl; -// } -// -//// /* CHEBYSCHEV TRAIN SET */ -//// alpha = PI / (train_size ); -//// frac = 0.5 * (de - ds); -//// for(i = 0; i < train_size; ++i){ -//// x = (std::cos(PI - alpha * i) + 1.0) * frac + ds; -//// data_points[i] = x; -//// } -// -// std::vector<double> *gradient_current = new std::vector<double>(4 * n_inner_neurons); -// std::vector<double> *gradient_prev = new std::vector<double>(4 * n_inner_neurons); -// std::vector<double> *params_current = new std::vector<double>(guess); -// std::vector<double> *params_prev = new std::vector<double>(guess); -// -// std::vector<double> *ptr_mem; -// -// std::fill(gradient_current->begin(), gradient_current->end(), 0.0); -// std::fill(gradient_prev->begin(), gradient_prev->end(), 0.0); -// -//// for (i = 0; i < n_inner_neurons; ++i) { -//// wxi = (*params_current)[4 * i + 0]; -//// wti = (*params_current)[4 * i + 1]; -//// ai = (*params_current)[4 * i + 2]; -//// bi = (*params_current)[4 * i + 3]; -//// -//// printf("Path %3d. wx = %15.8f, wt = %15.8f, b = %15.8f, a = %15.8f\n", (int)(i + 1), wxi, wti, bi, ai); -//// } -// -// gamma = 1.0; -// double val = 0.0, prev_val, c = 2.0; -// prev_val = 0.0; -// while( grad_norm > accuracy) { -// iter_idx++; -// prev_val = val; -// grad_norm_prev = grad_norm; -// -// /* reset of the current gradient */ -// std::fill(gradient_current->begin(), gradient_current->end(), 0.0); -// val = calculate_gradient( data_points, n_inner_neurons, params_current, gradient_current ); -// -// grad_norm = 0.0; -// for(auto v: *gradient_current){ -// grad_norm += v * v; -// } -// grad_norm = std::sqrt(grad_norm); -// -// /* Update of the parameters */ -// /* step length calculation */ -// if(iter_idx < 10 || iter_idx % 100 == 0){ -// /* fixed step length */ -// gamma = 0.1 * accuracy; -// } -// else{ -// -// -// -// /* norm of the gradient calculation */ -// -// sk = 0.0; -// for(i = 0; i < gradient_current->size(); ++i){ -// sx = (*gradient_current)[i] - (*gradient_prev)[i]; -// sk += sx * sx; -// } -// sk = std::sqrt(sk); -// -// /* angle between two consecutive gradients */ -// double beta = 0.0, sx = 0.0; -// for(i = 0; i < gradient_current->size(); ++i){ -// sx += (gradient_current->at( i ) * gradient_prev->at( i )); -// } -// sx /= grad_norm * grad_norm_prev; -// beta = std::sqrt(std::acos( sx ) / PI); -// -// -//// eval_step_size_simple( gamma, val, prev_val, sk, grad_norm, grad_norm_prev ); -// eval_step_size_mk( gamma, beta, c, grad_norm_prev, grad_norm, val, prev_val ); -// } -// -// -// -// -// -// -// for(i = 0; i < gradient_current->size(); ++i){ -// (*params_prev)[i] = (*params_current)[i] - gamma * (*gradient_current)[i]; -// } -// -// /* switcheroo */ -// ptr_mem = gradient_prev; -// gradient_prev = gradient_current; -// gradient_current = ptr_mem; -// -// ptr_mem = params_prev; -// params_prev = params_current; -// params_current = ptr_mem; -// -// -// if(iter_idx % 1 == 0){ -// printf("Iteration %12d. Step size: %15.8f, C: %15.8f, Gradient norm: %15.8f. Total error: %10.8f\r", (int)iter_idx, gamma, c, grad_norm, val); -// std::cout.flush(); -// } -// } -// printf("Iteration %12d. Step size: %15.8f, C: %15.8f, Gradient norm: %15.8f. Total error: %10.8f\r\n", (int)iter_idx, gamma, c, grad_norm, val); -// std::cout.flush(); -// -// for (i = 0; i < n_inner_neurons; ++i) { -// wxi = (*params_current)[4 * i + 0]; -// wti = (*params_current)[4 * i + 1]; -// ai = (*params_current)[4 * i + 2]; -// bi = (*params_current)[4 * i + 3]; -// -// printf("Path %3d. wx%d = %15.8f, wt%d = %15.8f, b%d = %15.8f, a%d = %15.8f\n", (int)(i + 1), (int)(i + 1), wxi , (int)(i + 1), wti, (int)(i + 1), bi, (int)(i + 1), ai); -// } -// std::cout << "********************************************************************************************************************************************" <<std::endl; -// -//// for (i = 0; i < n_inner_neurons; ++i) { -//// wxi = (*params_current)[4 * i + 0]; -//// wti = (*params_current)[4 * i + 1]; -//// ai = (*params_current)[4 * i + 2]; -//// bi = (*params_current)[4 * i + 3]; -//// -//// printf("%f/(1+e^(%f-%fx-%ft)) + ", (int)(i + 1), ai, bi, wxi, wti); -//// } -//// printf("\n"); -// -// -// /* SOLUTION EXPORT */ -// printf("Exporting file 'data_2d_pde1_y.txt' : %7.3f%%\r", 0.0); -// std::cout.flush(); -// -// std::vector<double> input, output(1); -// std::ofstream ofs_y("data_2d_pde1_y.txt", std::ofstream::out); -// frac = (te - ts) / (n_test_points - 1); -// for(i = 0; i < n_test_points; ++i){ -// x = i * frac + ts; -// for(j = 0; j < n_test_points; ++j){ -// t = j * frac + ts; -// ofs_y << x << " " << t << " " << eval_approx_y(x, t, n_inner_neurons, *params_current) << std::endl; -// printf("Exporting file 'data_2d_pde1_y.txt' : %7.3f%%\r", (100.0 * (j + i * n_test_points)) / (n_test_points * n_test_points - 1)); -// std::cout.flush(); -// } -// } -// printf("Exporting file 'data_2d_pde1_y.txt' : %7.3f%%\n", 100.0); -// std::cout.flush(); -// ofs_y.close(); -// -// printf("Exporting file 'data_2d_pde1_yt.txt' : %7.3f%%\r", 0.0); -// std::cout.flush(); -// std::ofstream ofs_t("data_2d_pde1_yt.txt", std::ofstream::out); -// frac = (te - ts) / (n_test_points - 1); -// for(i = 0; i < n_test_points; ++i){ -// x = i * frac + ts; -// for(j = 0; j < n_test_points; ++j){ -// t = j * frac + ts; -// ofs_t << x << " " << t << " " << eval_approx_yt(x, t, n_inner_neurons, *params_current) << std::endl; -// printf("Exporting file 'data_2d_pde1_yt.txt' : %7.3f%%\r", (100.0 * (j + i * n_test_points)) / (n_test_points * n_test_points - 1)); -// std::cout.flush(); -// } -// } -// printf("Exporting file 'data_2d_pde1_yt.txt' : %7.3f%%\n", 100.0); -// std::cout.flush(); -// ofs_t.close(); -// -// printf("Exporting file 'data_2d_pde1_yx.txt' : %7.3f%%\r", 0.0); -// std::cout.flush(); -// std::ofstream ofs_x("data_2d_pde1_yx.txt", std::ofstream::out); -// frac = (te - ts) / (n_test_points - 1); -// for(i = 0; i < n_test_points; ++i){ -// x = i * frac + ts; -// for(j = 0; j < n_test_points; ++j){ -// t = j * frac + ts; -// ofs_x << x << " " << t << " " << eval_approx_yx(x, t, n_inner_neurons, *params_current) << std::endl; -// printf("Exporting file 'data_2d_pde1_yx.txt' : %7.3f%%\r", (100.0 * (j + i * n_test_points)) / (n_test_points * n_test_points - 1)); -// std::cout.flush(); -// } -// } -// printf("Exporting file 'data_2d_pde1_yx.txt' : %7.3f%%\n", 100.0); -// std::cout.flush(); -// ofs_x.close(); -// -// printf("Exporting file 'data_2d_pde1_yxx.txt' : %7.3f%%\r", 0.0); -// std::cout.flush(); -// std::ofstream ofs_xx("data_2d_pde1_yxx.txt", std::ofstream::out); -// frac = (te - ts) / (n_test_points - 1); -// for(i = 0; i < n_test_points; ++i){ -// x = i * frac + ts; -// for(j = 0; j < n_test_points; ++j){ -// t = j * frac + ts; -// ofs_xx << x << " " << t << " " << eval_approx_yxx(x, t, n_inner_neurons, *params_current) << std::endl; -// printf("Exporting file 'data_2d_pde1_yxx.txt' : %7.3f%%\r", (100.0 * (j + i * n_test_points)) / (n_test_points * n_test_points - 1)); -// std::cout.flush(); -// } -// } -// printf("Exporting file 'data_2d_pde1_yxx.txt' : %7.3f%%\n", 100.0); -// std::cout.flush(); -// ofs_xx.close(); -// -// /* governing equation error */ -// std::ofstream ofs_error("data_2d_pde1_first_equation_error.txt", std::ofstream::out); -// printf("Exporting file 'data_2d_pde1_first_equation_error.txt' : %7.3f%%\r", 0.0); -// for(i = 0; i < n_test_points; ++i){ -// x = i * frac + ts; -// for(j = 0; j < n_test_points; ++j){ -// t = j * frac + ts; -// ofs_error << x << " " << t << " " << std::fabs(eval_approx_yxx(x, t, n_inner_neurons, *params_current) - eval_approx_yt(x, t, n_inner_neurons, *params_current)) << std::endl; -// printf("Exporting file 'data_2d_pde1_first_equation_error.txt' : %7.3f%%\r", (100.0 * (j + i * n_test_points)) / (n_test_points * n_test_points - 1)); -// std::cout.flush(); -// } -// } -// printf("Exporting file 'data_2d_pde1_first_equation_error.txt' : %7.3f%%\n", 100.0); -// std::cout.flush(); -// ofs_error.close(); -// -// /* ISOTROPIC TEST SET FOR BOUNDARY CONDITIONS */ -// /* first boundary condition & its error */ -// std::ofstream ofs_bc_t("data_1d_pde1_yt.txt", std::ofstream::out); -// std::ofstream ofs_bc_x("data_1d_pde1_yx.txt", std::ofstream::out); -// printf("Exporting files 'data_1d_pde1_yt.txt' and 'data_1d_pde1_yx.txt' : %7.3f%%\r", 0.0); -// for(i = 0; i < n_test_points; ++i){ -// x = frac * i + ts; -// t = frac * i + ts; -// -// double yt = std::sin(t); -// double yx = std::pow(E, -0.707106781 * x) * std::sin( -0.707106781 * x ); -// -// double evalt = eval_approx_y(0, t, n_inner_neurons, *params_current); -// double evalx = eval_approx_y(x, 0, n_inner_neurons, *params_current); -// -// ofs_bc_t << i + 1 << " " << t << " " << yt << " " << evalt << " " << std::fabs(evalt - yt) << std::endl; -// ofs_bc_x << i + 1 << " " << x << " " << yx << " " << evalx << " " << std::fabs(evalx - yx) << std::endl; -// -// printf("Exporting files 'data_1d_pde1_yt.txt' and 'data_1d_pde1_yx.txt' : %7.3f%%\r", (100.0 * i) / (n_test_points - 1)); -// std::cout.flush(); -// } -// printf("Exporting files 'data_1d_pde1_yt.txt' and 'data_1d_pde1_yx.txt' : %7.3f%%\r", 100.0); -// std::cout.flush(); -// ofs_bc_t.close(); -// ofs_bc_x.close(); -// -// delete gradient_current; -// delete gradient_prev; -// delete params_current; -// delete params_prev; -//} - -void solve_example_particle_swarm(double accuracy, size_t n_inner_neurons, size_t train_size, double ds, double de, size_t n_test_points, double ts, double te, size_t max_iters, size_t n_particles){ - std::cout << "Finding a solution via the Particle Swarm Optimization" << std::endl; - std::cout << "********************************************************************************************************************************************" <<std::endl; - - /* solution properties */ - - /* do not change below */ - size_t n_inputs = 2; - size_t n_equations = 3; - DESolver solver_01( n_equations, n_inputs, n_inner_neurons ); - - /* SETUP OF THE EQUATIONS */ - MultiIndex alpha_00( n_inputs ); - MultiIndex alpha_01( n_inputs ); - MultiIndex alpha_20( n_inputs ); - - alpha_00.set_partial_derivative(0, 0); - - alpha_01.set_partial_derivative(1, 1); - - alpha_20.set_partial_derivative(0, 2); - - /* the governing differential equation */ - solver_01.add_to_differential_equation( 0, alpha_20, "1.0" ); - solver_01.add_to_differential_equation( 0, alpha_01, "-1.0" ); - - /* dirichlet boundary condition */ - solver_01.add_to_differential_equation( 1, alpha_00, "1.0" ); - solver_01.add_to_differential_equation( 2, alpha_00, "1.0" ); - - - /* SETUP OF THE TRAINING DATA */ - //TODO neater data setup - std::vector<double> inp, out; - - double frac, x, t; - - /* TRAIN DATA FOR THE GOVERNING DE */ - std::vector<double> test_bounds_2d = {ds, de, ds, de}; - - /* GOVERNING EQUATION RHS */ - auto f1 = [](std::vector<double>&input) -> std::vector<double> { - std::vector<double> output(1); - output[0] = 0.0; - return output; - }; - DataSet ds_00(test_bounds_2d, train_size, f1, 1); - - std::vector<std::pair<std::vector<double>, std::vector<double>>> data_vec_t; - std::vector<std::pair<std::vector<double>, std::vector<double>>> data_vec_x; - /* ISOTROPIC TRAIN SET */ - frac = (de - ds) / (train_size - 1); - for(unsigned int i = 0; i < train_size; ++i){ - inp = {0.0, frac * i}; - out = {std::sin(inp[1])}; - data_vec_t.emplace_back(std::make_pair(inp, out)); - - inp = {frac * i, 0.0}; - out = {std::pow(E, -0.707106781 * inp[0]) * std::sin( -0.707106781 * inp[0] )}; - data_vec_x.emplace_back(std::make_pair(inp, out)); - - } - DataSet ds_t(&data_vec_t); - DataSet ds_x(&data_vec_x); - - - - - /* Placing the conditions into the solver */ - solver_01.set_error_function( 0, ErrorFunctionType::ErrorFuncMSE, &ds_00 ); - solver_01.set_error_function( 1, ErrorFunctionType::ErrorFuncMSE, &ds_t ); - solver_01.set_error_function( 2, ErrorFunctionType::ErrorFuncMSE, &ds_x ); - - size_t total_dim = (solver_01.get_solution( alpha_00 )->get_n_biases() + solver_01.get_solution( alpha_00 )->get_n_weights()); - - /* TRAINING METHOD SETUP */ - std::vector<double> domain_bounds(2 * total_dim); - - for(size_t i = 0; i < domain_bounds.size() / 2; ++i){ - domain_bounds[2 * i] = -10; + for (size_t i = 0; i < domain_bounds.size() / 2; ++i) { + domain_bounds[2 * i] = -10; domain_bounds[2 * i + 1] = 10; } double c1 = 1.7; double c2 = 1.7; - double w = 0.7; + double w = 0.700; /* if the maximal velocity from the previous step is less than 'gamma' times the current maximal velocity, then one * terminating criterion is met */ @@ -722,145 +49,353 @@ void solve_example_particle_swarm(double accuracy, size_t n_inner_neurons, size_ /* if 'delta' times 'n' particles are in the centroid neighborhood given by the radius 'epsilon', then the second * terminating criterion is met ('n' is the total number of particles) */ double epsilon = 0.02; - double delta = 0.7; - - ParticleSwarm swarm_01( - &domain_bounds, - c1, - c2, - w, - gamma, - epsilon, - delta, - n_particles, - max_iters + double delta = 0.7; + + l4n::ParticleSwarm swarm( + &domain_bounds, + c1, + c2, + w, + gamma, + epsilon, + delta, + n_particles, + max_iters ); - solver_01.solve( swarm_01 ); + solver.solve(swarm); +} + +void optimize_via_gradient_descent(l4n::DESolver& solver, + double accuracy) { + printf("Solution via a gradient descent method!\n"); + l4n::GradientDescent gd(accuracy, + 1000); + + solver.randomize_parameters(); + solver.solve(gd); +} + +void export_solution(size_t n_test_points, + double te, + double ts, + l4n::DESolver& solver, + l4n::MultiIndex& alpha_00, + l4n::MultiIndex& alpha_01, + l4n::MultiIndex& alpha_20, + const std::string prefix) { + l4n::NeuralNetwork* solution = solver.get_solution(alpha_00); + l4n::NeuralNetwork* solution_t = solver.get_solution(alpha_01); + l4n::NeuralNetwork* solution_xx = solver.get_solution(alpha_20); size_t i, j; - std::vector<double> *w1_ptr = solver_01.get_solution( alpha_00 )->get_parameter_ptr_weights(); - std::vector<double> *w2_ptr = solver_01.get_solution( alpha_00 )->get_parameter_ptr_biases(); - std::vector<double> export_params(total_dim); - for(size_t i = 0; i < n_inner_neurons; ++i){ - export_params[4 * i + 0] = w1_ptr->at(i); - export_params[4 * i + 1] = w1_ptr->at(n_inner_neurons + i); - export_params[4 * i + 2] = w1_ptr->at(2 * n_inner_neurons + i); - export_params[4 * i + 3] = w2_ptr->at( i ); - - printf("Path %3d. wx%d = %15.8f, wt%d = %15.8f, b%d = %15.8f, a%d = %15.8f\n", (int)(i + 1), (int)(i + 1), export_params[4 * i + 0] , (int)(i + 1), export_params[4 * i + 1], (int)(i + 1), export_params[4 * i + 3], (int)(i + 1), export_params[4 * i + 2]); - } - std::cout << "********************************************************************************************************************************************" << std::endl; - /* PRACTICAL END OF THE EXAMPLE */ - - /* SOLUTION EXPORT */ - for(i = 0; i < n_inner_neurons; ++i){ - export_params[4 * i + 0] = w1_ptr->at(i); - export_params[4 * i + 1] = w1_ptr->at(n_inner_neurons + i); - export_params[4 * i + 2] = w1_ptr->at(2 * n_inner_neurons + i); - export_params[4 * i + 3] = w2_ptr->at( i ); - } -/* - printf("Exporting file 'data_2d_pde1_y.txt' : %7.3f%%\r", 0.0); + double x, t; + /* ISOTROPIC TEST SET FOR BOUNDARY CONDITIONS */ + /* first boundary condition & its error */ + + char buff[256]; + sprintf(buff, + "%sdata_2d_pde1_y.txt", + prefix.c_str()); + std::string final_fn(buff); + + printf("Exporting file '%s' : %7.3f%%\r", + final_fn.c_str(), + 0.0); std::cout.flush(); - std::vector<double> input, output(1); - std::ofstream ofs("data_2d_pde1_y.txt", std::ofstream::out); - frac = (te - ts) / (n_test_points - 1); - for(i = 0; i < n_test_points; ++i){ - x = i * frac + ts; - for(j = 0; j < n_test_points; ++j){ - t = j * frac + ts; - ofs << x << " " << t << " " << eval_approx_y(x, t, n_inner_neurons, export_params) << std::endl; - printf("Exporting file 'data_2d_pde1_y.txt' : %7.3f%%\r", (100.0 * (j + i * n_test_points)) / (n_test_points * n_test_points - 1)); + std::vector<double> input(2), output(1), output_t(1), output_xx(1); + std::ofstream ofs(final_fn, + std::ofstream::out); + double frac = (te - ts) / (n_test_points - 1); + for (i = 0; i < n_test_points; ++i) { + x = i * frac + ts; + for (j = 0; j < n_test_points; ++j) { + t = j * frac + ts; + input = {x, t}; + + solution->eval_single(input, + output); + + ofs << x << " " << t << " " << output[0] << std::endl; + printf("Exporting file '%s' : %7.3f%%\r", + final_fn.c_str(), + (100.0 * (j + i * n_test_points)) / (n_test_points * n_test_points - 1)); std::cout.flush(); } } - printf("Exporting file 'data_2d_pde1_y.txt' : %7.3f%%\n", 100.0); + printf("Exporting file '%s' : %7.3f%%\n", + final_fn.c_str(), + 100.0); std::cout.flush(); ofs.close(); /* governing equation error */ - /* - ofs = std::ofstream("data_2d_pde1_first_equation_error.txt", std::ofstream::out); - printf("Exporting file 'data_2d_pde1_first_equation_error.txt' : %7.3f%%\r", 0.0); - for(i = 0; i < n_test_points; ++i){ - x = i * frac + ts; - for(j = 0; j < n_test_points; ++j){ - t = j * frac + ts; - ofs << x << " " << t << " " << std::fabs(eval_approx_yxx(x, t, n_inner_neurons, export_params) - eval_approx_yt(x, t, n_inner_neurons, export_params)) << std::endl; - printf("Exporting file 'data_2d_pde1_first_equation_error.txt' : %7.3f%%\r", (100.0 * (j + i * n_test_points)) / (n_test_points * n_test_points - 1)); + sprintf(buff, + "%sdata_2d_pde1_first_equation_error.txt", + prefix.c_str()); + final_fn = std::string(buff); + + ofs = std::ofstream(final_fn, + std::ofstream::out); + printf("Exporting file '%s' : %7.3f%%\r", + final_fn.c_str(), + 0.0); + for (i = 0; i < n_test_points; ++i) { + x = i * frac + ts; + for (j = 0; j < n_test_points; ++j) { + t = j * frac + ts; + input = {x, t}; + + solution_t->eval_single(input, + output_t); + solution_xx->eval_single(input, + output_xx); + + ofs << x << " " << t << " " << std::fabs(output_xx[0] - output_t[0]) << std::endl; + printf("Exporting file 'data_2d_pde1_first_equation_error.txt' : %7.3f%%\r", + (100.0 * (j + i * n_test_points)) / (n_test_points * n_test_points - 1)); std::cout.flush(); } } - printf("Exporting file 'data_2d_pde1_first_equation_error.txt' : %7.3f%%\n", 100.0); + printf("Exporting file '%s' : %7.3f%%\n", + final_fn.c_str(), + 100.0); std::cout.flush(); ofs.close(); -*/ + /* ISOTROPIC TEST SET FOR BOUNDARY CONDITIONS */ /* first boundary condition & its error */ - /* - ofs = std::ofstream("data_1d_pde1_yt.txt", std::ofstream::out); - std::ofstream ofs2("data_1d_pde1_yx.txt", std::ofstream::out); - printf("Exporting files 'data_1d_pde1_yt.txt' and 'data_1d_pde1_yx.txt' : %7.3f%%\r", 0.0); - for(i = 0; i < n_test_points; ++i){ + sprintf(buff, + "%sdata_1d_pde1_yt.txt", + prefix.c_str()); + std::string final_fn_t(buff); + + sprintf(buff, + "%sdata_1d_pde1_yx.txt", + prefix.c_str()); + std::string final_fn_x(buff); + + ofs = std::ofstream(final_fn_t, + std::ofstream::out); + std::ofstream ofs2(final_fn_x, + std::ofstream::out); + printf("Exporting files '%s' and '%s' : %7.3f%%\r", + final_fn_t.c_str(), + final_fn_x.c_str(), + 0.0); + for (i = 0; i < n_test_points; ++i) { x = frac * i + ts; t = frac * i + ts; double yt = std::sin(t); - double yx = std::pow(E, -0.707106781 * x) * std::sin( -0.707106781 * x ); + double yx = std::pow(l4n::E, + -0.707106781 * x) * std::sin(-0.707106781 * x); + + input = {0, t}; + solution->eval_single(input, + output); + double evalt = output[0]; - double evalt = eval_approx_y(0, t, n_inner_neurons, export_params); - double evalx = eval_approx_y(x, 0, n_inner_neurons, export_params); + input = {x, 0}; + solution->eval_single(input, + output); + double evalx = output[0]; ofs << i + 1 << " " << t << " " << yt << " " << evalt << " " << std::fabs(evalt - yt) << std::endl; ofs2 << i + 1 << " " << x << " " << yx << " " << evalx << " " << std::fabs(evalx - yx) << std::endl; - printf("Exporting files 'data_1d_pde1_yt.txt' and 'data_1d_pde1_yx.txt' : %7.3f%%\r", (100.0 * i) / (n_test_points - 1)); + printf("Exporting files '%s' and '%s' : %7.3f%%\r", + final_fn_t.c_str(), + final_fn_x.c_str(), + (100.0 * i) / (n_test_points - 1)); std::cout.flush(); } - printf("Exporting files 'data_1d_pde1_yt.txt' and 'data_1d_pde1_yx.txt' : %7.3f%%\r", 100.0); + printf("Exporting files '%s' and '%s' : %7.3f%%\n", + final_fn_t.c_str(), + final_fn_x.c_str(), + 100.0); std::cout.flush(); ofs2.close(); ofs.close(); - */ + + std::cout + << "********************************************************************************************************************************************" + << std::endl; +} + +void test_pde(double accuracy, + size_t n_inner_neurons, + size_t train_size, + double ds, + double de, + size_t n_test_points, + double ts, + double te, + size_t max_iters, + size_t n_particles) { + + /* do not change below */ + size_t n_inputs = 2; + size_t n_equations = 3; + l4n::DESolver solver_01(n_equations, + n_inputs, + n_inner_neurons); + + /* SETUP OF THE EQUATIONS */ + l4n::MultiIndex alpha_00(n_inputs); + l4n::MultiIndex alpha_01(n_inputs); + l4n::MultiIndex alpha_20(n_inputs); + + alpha_00.set_partial_derivative(0, + 0); + alpha_01.set_partial_derivative(1, + 1); + alpha_20.set_partial_derivative(0, + 2); + + /* the governing differential equation */ + solver_01.add_to_differential_equation(0, + alpha_20, + "1.0"); + solver_01.add_to_differential_equation(0, + alpha_01, + "-1.0"); + + /* dirichlet boundary condition */ + solver_01.add_to_differential_equation(1, + alpha_00, + "1.0"); + solver_01.add_to_differential_equation(2, + alpha_00, + "1.0"); + + /* SETUP OF THE TRAINING DATA */ + //TODO neater data setup + std::vector<double> inp, out; + + double frac, x, t; + + /* TRAIN DATA FOR THE GOVERNING DE */ + std::vector<std::pair<std::vector<double>, std::vector<double>>> data_vec_zero; + + /* GOVERNING EQUATION RHS */ + frac = (de - ds) / (train_size - 1); + for (unsigned int i = 0; i < train_size; ++i) { + for (unsigned int j = 0; j < train_size; ++j) { + inp = {frac * j, frac * i}; + out = {0.0}; + data_vec_zero.emplace_back(std::make_pair(inp, + out)); + } + } + l4n::DataSet ds_00(&data_vec_zero); + + std::vector<std::pair<std::vector<double>, std::vector<double>>> data_vec_t; + std::vector<std::pair<std::vector<double>, std::vector<double>>> data_vec_x; + /* ISOTROPIC TRAIN SET */ + frac = (de - ds) / (train_size - 1); + for (unsigned int i = 0; i < train_size; ++i) { + inp = {0.0, frac * i}; + out = {std::sin(inp[1])}; + data_vec_t.emplace_back(std::make_pair(inp, + out)); + + inp = {frac * i, 0.0}; + out = {std::pow(l4n::E, + -0.707106781 * inp[0]) * std::sin(-0.707106781 * inp[0])}; + data_vec_x.emplace_back(std::make_pair(inp, + out)); + + } + l4n::DataSet ds_t(&data_vec_t); + l4n::DataSet ds_x(&data_vec_x); + std::cout << "Train data setup finished" << std::endl; + + + + /* Placing the conditions into the solver */ + solver_01.set_error_function(0, + l4n::ErrorFunctionType::ErrorFuncMSE, + ds_00); + solver_01.set_error_function(1, + l4n::ErrorFunctionType::ErrorFuncMSE, + ds_t); + solver_01.set_error_function(2, + l4n::ErrorFunctionType::ErrorFuncMSE, + ds_x); + std::cout << "Error function defined" << std::endl; + + /* Solving the equation */ + optimize_via_particle_swarm(solver_01, + alpha_00, + max_iters, + n_particles); + export_solution(n_test_points, + te, + ts, + solver_01, + alpha_00, + alpha_01, + alpha_20, + "particle_"); + + optimize_via_gradient_descent(solver_01, + accuracy); + export_solution(n_test_points, + te, + ts, + solver_01, + alpha_00, + alpha_01, + alpha_20, + "gradient_"); } int main() { std::cout << "Running lib4neuro Partial Differential Equation example 1" << std::endl; - std::cout << "********************************************************************************************************************************************" <<std::endl; - std::cout << " Governing equation: y_xx - y_t = 0, for (x, t) in [0, 1] x [0, 1]" << std::endl; - std::cout << "Dirichlet boundary condition: y(0, t) = sin(t), for t in [0, 1]" << std::endl; - std::cout << "Dirichlet boundary condition: y(x, 0) = exp(-sqrt(0.5)x) * sin(-sqrt(0.5)x), for x in [0, 1]" << std::endl; - std::cout << "********************************************************************************************************************************************" <<std::endl; - std::cout << "Expressing solution as y(x, t) = sum over [a_i / (1 + exp(bi - wxi*x - wti*t))], i in [1, n], where n is the number of hidden neurons" <<std::endl; - std::cout << "********************************************************************************************************************************************" <<std::endl; - - unsigned int n_inner_neurons = 6; - unsigned int train_size = 15; - double accuracy = 1e-4; - double ds = 0.0; - double de = 1.0; - - unsigned int test_size = 20; - double ts = ds; - double te = de + 0; - - size_t particle_swarm_max_iters = 1000; - size_t n_particles = 50; - solve_example_particle_swarm(accuracy, n_inner_neurons, train_size, ds, de, test_size, ts, te, particle_swarm_max_iters, n_particles); - - -// std::vector<double> init_guess(4 * n_inner_neurons); -// std::random_device seeder; -// std::mt19937 gen(seeder()); -// std::uniform_real_distribution<double> dist(-1.0, 1.0); -// for(unsigned int i = 0; i < init_guess.size(); ++i){ -// init_guess[i] = dist(gen); -// } -// -//// init_guess = {-0.21709230, -0.26189447, 0.77853923, 0.41091127, -0.44311897, -0.99036349, 0.84912023, -0.16920743}; -// solve_example_gradient(init_guess, accuracy, n_inner_neurons, train_size, ds, de, test_size, ts, te); + std::cout + << "********************************************************************************************************************************************" + << std::endl; + std::cout + << " Governing equation: y_xx - y_t = 0, for (x, t) in [0, 1] x [0, 1]" + << std::endl; + std::cout << "Dirichlet boundary condition: y(0, t) = sin(t), for t in [0, 1]" + << std::endl; + std::cout << "Dirichlet boundary condition: y(x, 0) = exp(-sqrt(0.5)x) * sin(-sqrt(0.5)x), for x in [0, 1]" + << std::endl; + std::cout + << "********************************************************************************************************************************************" + << std::endl; + std::cout + << "Expressing solution as y(x, t) = sum over [a_i / (1 + exp(bi - wxi*x - wti*t))], i in [1, n], where n is the number of hidden neurons" + << std::endl; + std::cout + << "********************************************************************************************************************************************" + << std::endl; + + unsigned int n_inner_neurons = 2; + unsigned int train_size = 5; + double accuracy = 1e-1; + double ds = 0.0; + double de = 1.0; + + unsigned int test_size = 10; + double ts = ds; + double te = de + 0; + + size_t particle_swarm_max_iters = 10; + size_t n_particles = 5; + test_pde(accuracy, + n_inner_neurons, + train_size, + ds, + de, + test_size, + ts, + te, + particle_swarm_max_iters, + n_particles); return 0; } diff --git a/src/examples/network_serialization.cpp b/src/examples/network_serialization.cpp index b5811670b4dd10ccaacf7dc066f9741f197025a7..bae85fcc39a499555282f8c472cffdce8ee90e83 100644 --- a/src/examples/network_serialization.cpp +++ b/src/examples/network_serialization.cpp @@ -11,55 +11,70 @@ int main() { std::cout << "Running lib4neuro Serialization example 1" << std::endl; - std::cout << "********************************************************************************************************************************************" <<std::endl; + std::cout + << "********************************************************************************************************************************************" + << std::endl; std::cout << "First, it finds an approximate solution to the system of equations below:" << std::endl; std::cout << "0 * w1 + 1 * w2 = 0.50 + b" << std::endl; std::cout << "1 * w1 + 0.5*w2 = 0.75 + b" << std::endl; - std::cout << "********************************************************************************************************************************************" <<std::endl; - std::cout << "Then it stores the network with its weights into a file via serialization" <<std::endl; - std::cout << "Then it loads the network from a file via serialization" <<std::endl; - std::cout << "Finally it tests the loaded network parameters by evaluating the error function" <<std::endl; - std::cout << "********************************************************************************************************************************************" <<std::endl; + std::cout + << "********************************************************************************************************************************************" + << std::endl; + std::cout << "Then it stores the network with its weights into a file via serialization" << std::endl; + std::cout << "Then it loads the network from a file via serialization" << std::endl; + std::cout << "Finally it tests the loaded network parameters by evaluating the error function" << std::endl; + std::cout + << "********************************************************************************************************************************************" + << std::endl; /* TRAIN DATA DEFINITION */ std::vector<std::pair<std::vector<double>, std::vector<double>>> data_vec; - std::vector<double> inp, out; + std::vector<double> inp, out; inp = {0, 1}; out = {0.5}; - data_vec.emplace_back(std::make_pair(inp, out)); + data_vec.emplace_back(std::make_pair(inp, + out)); inp = {1, 0.5}; out = {0.75}; - data_vec.emplace_back(std::make_pair(inp, out)); + data_vec.emplace_back(std::make_pair(inp, + out)); - DataSet ds(&data_vec); + l4n::DataSet ds(&data_vec); /* NETWORK DEFINITION */ - NeuralNetwork net; + l4n::NeuralNetwork net; /* Input neurons */ - NeuronLinear *i1 = new NeuronLinear( ); //f(x) = x - NeuronLinear *i2 = new NeuronLinear( ); //f(x) = x - /* Output neuron */ - NeuronLinear *o1 = new NeuronLinear( ); //f(x) = x + std::shared_ptr<l4n::NeuronLinear> i1 = std::make_shared<l4n::NeuronLinear>(); + std::shared_ptr<l4n::NeuronLinear> i2 = std::make_shared<l4n::NeuronLinear>(); + /* Output neuron */ + std::shared_ptr<l4n::NeuronLinear> o1 = std::make_shared<l4n::NeuronLinear>(); /* Adding neurons to the net */ - size_t idx1 = net.add_neuron(i1, BIAS_TYPE::NO_BIAS); - size_t idx2 = net.add_neuron(i2, BIAS_TYPE::NO_BIAS); - size_t idx3 = net.add_neuron(o1, BIAS_TYPE::NEXT_BIAS); - - std::vector<double> *bv = net.get_parameter_ptr_biases(); - for(size_t i = 0; i < 1; ++i){ + size_t idx1 = net.add_neuron(i1, + l4n::BIAS_TYPE::NO_BIAS); + size_t idx2 = net.add_neuron(i2, + l4n::BIAS_TYPE::NO_BIAS); + size_t idx3 = net.add_neuron(o1, + l4n::BIAS_TYPE::NEXT_BIAS); + + std::vector<double>* bv = net.get_parameter_ptr_biases(); + for (size_t i = 0; i < 1; ++i) { bv->at(i) = 1.0; } /* Adding connections */ - net.add_connection_simple(idx1, idx3, SIMPLE_CONNECTION_TYPE::NEXT_WEIGHT); - net.add_connection_simple(idx2, idx3, SIMPLE_CONNECTION_TYPE::NEXT_WEIGHT); + net.add_connection_simple(idx1, + idx3, + l4n::SIMPLE_CONNECTION_TYPE::NEXT_WEIGHT); + net.add_connection_simple(idx2, + idx3, + l4n::SIMPLE_CONNECTION_TYPE::NEXT_WEIGHT); //net.randomize_weights(); @@ -75,21 +90,22 @@ int main() { net.specify_output_neurons(net_output_neurons_indices); /* ERROR FUNCTION SPECIFICATION */ - MSE mse(&net, &ds); + l4n::MSE mse(&net, + &ds); /* TRAINING METHOD SETUP */ std::vector<double> domain_bounds(2 * (net.get_n_weights() + net.get_n_biases())); - for(size_t i = 0; i < domain_bounds.size() / 2; ++i){ - domain_bounds[2 * i] = -10; + for (size_t i = 0; i < domain_bounds.size() / 2; ++i) { + domain_bounds[2 * i] = -10; domain_bounds[2 * i + 1] = 10; } - double c1 = 1.7; - double c2 = 1.7; - double w = 0.7; - size_t n_particles = 50; - size_t iter_max = 1000; + double c1 = 1.7; + double c2 = 1.7; + double w = 0.7; + size_t n_particles = 5; + size_t iter_max = 10; /* if the maximal velocity from the previous step is less than 'gamma' times the current maximal velocity, then one * terminating criterion is met */ @@ -98,63 +114,80 @@ int main() { /* if 'delta' times 'n' particles are in the centroid neighborhood given by the radius 'epsilon', then the second * terminating criterion is met ('n' is the total number of particles) */ double epsilon = 0.02; - double delta = 0.7; - - ParticleSwarm swarm_01( - &domain_bounds, - c1, - c2, - w, - gamma, - epsilon, - delta, - n_particles, - iter_max + double delta = 0.7; + + l4n::ParticleSwarm swarm_01( + &domain_bounds, + c1, + c2, + w, + gamma, + epsilon, + delta, + n_particles, + iter_max ); - swarm_01.optimize( mse ); + swarm_01.optimize(mse); - std::vector<double> *parameters = swarm_01.get_parameters(); - net.copy_parameter_space(parameters); + std::vector<double>* parameters = swarm_01.get_parameters(); + net.copy_parameter_space(swarm_01.get_parameters()); - printf("w1 = %10.7f\n", parameters->at( 0 )); - printf("w2 = %10.7f\n", parameters->at( 1 )); - printf(" b = %10.7f\n", parameters->at( 2 )); + printf("w1 = %10.7f\n", + parameters->at(0)); + printf("w2 = %10.7f\n", + parameters->at(1)); + printf(" b = %10.7f\n", + parameters->at(2)); /* SAVE NETWORK TO THE FILE */ - std::cout << "********************************************************************************************************************************************" <<std::endl; + std::cout + << "********************************************************************************************************************************************" + << std::endl; std::cout << "Network generated by the example" << std::endl; - net.print_stats(); + net.write_stats(); net.save_text("saved_network.4nt"); - std::cout << "--------------------------------------------------------------------------------------------------------------------------------------------" <<std::endl; + std::cout + << "--------------------------------------------------------------------------------------------------------------------------------------------" + << std::endl; double error = 0.0; inp = {0, 1}; - net.eval_single( inp, out ); + net.eval_single(inp, + out); error += (0.5 - out[0]) * (0.5 - out[0]); std::cout << "x = (0, 1), expected output: 0.50, real output: " << out[0] << std::endl; inp = {1, 0.5}; - net.eval_single( inp, out ); + net.eval_single(inp, + out); error += (0.75 - out[0]) * (0.75 - out[0]); std::cout << "x = (1, 0.5), expected output: 0.75, real output: " << out[0] << std::endl; std::cout << "Error of the network: " << 0.5 * error << std::endl; - std::cout << "********************************************************************************************************************************************" <<std::endl; + std::cout + << "********************************************************************************************************************************************" + << std::endl; std::cout << "Network loaded from a file" << std::endl; - NeuralNetwork net2("saved_network.4nt"); - net2.print_stats(); - std::cout << "--------------------------------------------------------------------------------------------------------------------------------------------" <<std::endl; + l4n::NeuralNetwork net2("saved_network.4nt"); + net2.write_stats(); + std::cout + << "--------------------------------------------------------------------------------------------------------------------------------------------" + << std::endl; error = 0.0; - inp = {0, 1}; - net2.eval_single( inp, out ); + inp = {0, 1}; + net2.eval_single(inp, + out); error += (0.5 - out[0]) * (0.5 - out[0]); std::cout << "x = (0, 1), expected output: 0.50, real output: " << out[0] << std::endl; inp = {1, 0.5}; - net2.eval_single( inp, out ); + net2.eval_single(inp, + out); error += (0.75 - out[0]) * (0.75 - out[0]); std::cout << "x = (1, 0.5), expected output: 0.75, real output: " << out[0] << std::endl; std::cout << "Error of the network: " << 0.5 * error << std::endl; - std::cout << "********************************************************************************************************************************************" <<std::endl; + std::cout + << "********************************************************************************************************************************************" + << std::endl; return 0; -} \ No newline at end of file +} diff --git a/src/examples/seminar.cpp b/src/examples/seminar.cpp index 6369f099c4b0f688e2c56abf022f30495228a5be..11926803baaf724c2e7058023eb858048454a392 100644 --- a/src/examples/seminar.cpp +++ b/src/examples/seminar.cpp @@ -9,54 +9,74 @@ #include <iostream> #include <fstream> -#include "../../include/4neuro.h" +#include "4neuro.h" #include "../Solvers/DESolver.h" int main() { std::cout << std::endl << "Running lib4neuro Moldyn Seminar example" << std::endl; - std::cout << "********************************************************************************************************************************************" <<std::endl; - - - NeuralNetwork XOR; - unsigned int i1 = XOR.add_neuron( new NeuronLinear( ), BIAS_TYPE::NO_BIAS ); - unsigned int i2 = XOR.add_neuron( new NeuronLinear( ), BIAS_TYPE::NO_BIAS ); - - unsigned int h1 = XOR.add_neuron( new NeuronLogistic( ) ); - unsigned int h2 = XOR.add_neuron( new NeuronLogistic( ) ); - - unsigned int o1 = XOR.add_neuron( new NeuronLinear( ), BIAS_TYPE::NO_BIAS ); - - XOR.add_connection_simple( i1, h1 ); - XOR.add_connection_simple( i2, h1 ); - - XOR.add_connection_simple( i1, h2 ); - XOR.add_connection_simple( i2, h2 ); - - XOR.add_connection_simple( h1, o1 ); - XOR.add_connection_simple( h2, o1 ); + std::cout + << "********************************************************************************************************************************************" + << std::endl; + + + l4n::NeuralNetwork XOR; + std::shared_ptr<l4n::NeuronLinear> in1 = std::make_shared<l4n::NeuronLinear>(); + std::shared_ptr<l4n::NeuronLinear> in2 = std::make_shared<l4n::NeuronLinear>(); + size_t i1 = XOR.add_neuron(in1, + l4n::BIAS_TYPE::NO_BIAS); + size_t i2 = XOR.add_neuron(in2, + l4n::BIAS_TYPE::NO_BIAS); + + std::shared_ptr<l4n::NeuronLogistic> hn1 = std::make_shared<l4n::NeuronLogistic>(); + std::shared_ptr<l4n::NeuronLogistic> hn2 = std::make_shared<l4n::NeuronLogistic>(); + size_t h1 = XOR.add_neuron(hn1); + size_t h2 = XOR.add_neuron(hn2); + + std::shared_ptr<l4n::NeuronLinear> on1 = std::make_shared<l4n::NeuronLinear>(); + size_t o1 = XOR.add_neuron(on1, + l4n::BIAS_TYPE::NO_BIAS); + + XOR.add_connection_simple(i1, + h1); + XOR.add_connection_simple(i2, + h1); + + XOR.add_connection_simple(i1, + h2); + XOR.add_connection_simple(i2, + h2); + + XOR.add_connection_simple(h1, + o1); + XOR.add_connection_simple(h2, + o1); /* TRAIN DATA DEFINITION */ std::vector<std::pair<std::vector<double>, std::vector<double>>> data_vec; - std::vector<double> inp, out; + std::vector<double> inp, out; inp = {0, 0}; out = {0}; - data_vec.emplace_back(std::make_pair(inp, out)); + data_vec.emplace_back(std::make_pair(inp, + out)); inp = {0, 1}; out = {1}; - data_vec.emplace_back(std::make_pair(inp, out)); + data_vec.emplace_back(std::make_pair(inp, + out)); inp = {1, 0}; out = {1}; - data_vec.emplace_back(std::make_pair(inp, out)); + data_vec.emplace_back(std::make_pair(inp, + out)); inp = {1, 1}; out = {0}; - data_vec.emplace_back(std::make_pair(inp, out)); + data_vec.emplace_back(std::make_pair(inp, + out)); - DataSet ds(&data_vec); + l4n::DataSet ds(&data_vec); /* specification of the input/output neurons */ std::vector<size_t> net_input_neurons_indices(2); @@ -71,23 +91,24 @@ int main() { /* ERROR FUNCTION SPECIFICATION */ - MSE mse(&XOR, &ds); + l4n::MSE mse(&XOR, + &ds); /* TRAINING METHOD SETUP */ std::vector<double> domain_bounds(2 * (XOR.get_n_weights() + XOR.get_n_biases())); - for(size_t i = 0; i < domain_bounds.size() / 2; ++i){ - domain_bounds[2 * i] = -10; + for (size_t i = 0; i < domain_bounds.size() / 2; ++i) { + domain_bounds[2 * i] = -10; domain_bounds[2 * i + 1] = 10; } - double c1 = 1.7; - double c2 = 1.7; - double w = 0.7; - size_t n_particles = 50; - size_t iter_max = 1000; + double c1 = 1.7; + double c2 = 1.7; + double w = 0.7; + size_t n_particles = 5; + size_t iter_max = 10; /* if the maximal velocity from the previous step is less than 'gamma' times the current maximal velocity, then one * terminating criterion is met */ @@ -96,47 +117,49 @@ int main() { /* if 'delta' times 'n' particles are in the centroid neighborhood given by the radius 'epsilon', then the second * terminating criterion is met ('n' is the total number of particles) */ double epsilon = 0.02; - double delta = 0.7; - - ParticleSwarm swarm_01( - &domain_bounds, - c1, - c2, - w, - gamma, - epsilon, - delta, - n_particles, - iter_max + double delta = 0.7; + + l4n::ParticleSwarm swarm_01( + &domain_bounds, + c1, + c2, + w, + gamma, + epsilon, + delta, + n_particles, + iter_max ); - swarm_01.optimize( mse ); + swarm_01.optimize(mse); - std::vector<double> *parameters = swarm_01.get_parameters( ); - XOR.copy_parameter_space(parameters); + XOR.copy_parameter_space(swarm_01.get_parameters()); /* ERROR CALCULATION */ double error = 0.0; inp = {0, 0}; - XOR.eval_single( inp, out ); + XOR.eval_single(inp, + out); error += (0 - out[0]) * (0 - out[0]); std::cout << "x = (0, 0), expected output: 0, real output: " << out[0] << std::endl; inp = {0, 1}; - XOR.eval_single( inp, out ); + XOR.eval_single(inp, + out); error += (1 - out[0]) * (1 - out[0]); std::cout << "x = (0, 1), expected output: 1, real output: " << out[0] << std::endl; inp = {1, 0}; - XOR.eval_single( inp, out ); + XOR.eval_single(inp, + out); error += (1 - out[0]) * (1 - out[0]); std::cout << "x = (1, 0), expected output: 1, real output: " << out[0] << std::endl; inp = {1, 1}; - XOR.eval_single( inp, out ); + XOR.eval_single(inp, + out); error += (0 - out[0]) * (0 - out[0]); std::cout << "x = (1, 1), expected output: 0, real output: " << out[0] << std::endl; - return 0; } diff --git a/src/examples/x2_data.txt b/src/examples/x2_data.txt new file mode 100644 index 0000000000000000000000000000000000000000..111c81e0696d07edee12402db9f46ff6e58294ec --- /dev/null +++ b/src/examples/x2_data.txt @@ -0,0 +1,122 @@ +x y +-6 0 +-5.9 0 +-5.8 0 +-5.7 0 +-5.6 0 +-5.5 0 +-5.4 0 +-5.3 0 +-5.2 0 +-5.1 0 +-5 0 +-4.9 0 +-4.8 0 +-4.7 0 +-4.6 0 +-4.5 0 +-4.4 0 +-4.3 0 +-4.2 0 +-4.1 0 +-4 0 +-3.9 0 +-3.8 0 +-3.7 0 +-3.6 0 +-3.5 0 +-3.4 0 +-3.3 0 +-3.2 0 +-3.1 0 +-3 0 +-2.9 0 +-2.8 0 +-2.7 0 +-2.6 0 +-2.5 0 +-2.4 0 +-2.3 0 +-2.2 0 +-2.1 0 +-2 0 +-1.9 0 +-1.8 0 +-1.7 0 +-1.6 0 +-1.5 0 +-1.4 0 +-1.3 0 +-1.2 0 +-1.1 0 +-1 0 +-0.9 0.05 +-0.8 0.1 +-0.7 0.15 +-0.6 0.2 +-0.5 0.25 +-0.4 0.3 +-0.3 0.35 +-0.2 0.4 +-0.1 0.45 +0 0.5 +0.1 0.55 +0.2 0.6 +0.3 0.65 +0.4 0.7 +0.5 0.75 +0.6 0.8 +0.7 0.85 +0.8 0.9 +0.9 0.95 +1 1 +1.1 1 +1.2 1 +1.3 1 +1.4 1 +1.5 1 +1.6 1 +1.7 1 +1.8 1 +1.9 1 +2 1 +2.1 1 +2.2 1 +2.3 1 +2.4 1 +2.5 1 +2.6 1 +2.7 1 +2.8 1 +2.9 1 +3 1 +3.1 1 +3.2 1 +3.3 1 +3.4 1 +3.5 1 +3.6 1 +3.7 1 +3.8 1 +3.9 1 +4 1 +4.1 1 +4.2 1 +4.3 1 +4.4 1 +4.5 1 +4.6 1 +4.7 1 +4.8 1 +4.9 1 +5 1 +5.1 1 +5.2 1 +5.3 1 +5.4 1 +5.5 1 +5.6 1 +5.7 1 +5.8 1 +5.9 1 +6 1 diff --git a/src/examples/x2_fitting.cpp b/src/examples/x2_fitting.cpp new file mode 100644 index 0000000000000000000000000000000000000000..828215414bbb0c0476099b752e5625b357572bcb --- /dev/null +++ b/src/examples/x2_fitting.cpp @@ -0,0 +1,35 @@ +#include <iostream> + +#include "4neuro.h" + +int main() { + + l4n::CSVReader reader("x2_data.txt", + "\t", + true); + reader.read(); + + std::vector<unsigned int> input_ind = {0}; + std::vector<unsigned int> output_ind = {1}; + std::shared_ptr<l4n::DataSet> ds = reader.get_data_set(&input_ind, + &output_ind); + + std::vector<unsigned int> neuron_numbers_in_layers = {1, 15, 1}; + std::vector<l4n::NEURON_TYPE> hidden_type_v = {l4n::NEURON_TYPE::LOGISTIC}; + l4n::FullyConnectedFFN net(&neuron_numbers_in_layers, + &hidden_type_v); + + l4n::MSE mse(&net, + ds.get()); + + l4n::GradientDescent gs(1e-5, + 20, + 200000); + + net.randomize_parameters(); + gs.optimize(mse); + + mse.eval_on_data_set(ds.get()); + + return 0; +} diff --git a/src/exceptions.h b/src/exceptions.h new file mode 100644 index 0000000000000000000000000000000000000000..3bb3b0ba3145bda963db1608e451065753ac3d74 --- /dev/null +++ b/src/exceptions.h @@ -0,0 +1,20 @@ + +#ifndef LIB4NEURO_EXCEPTIONS_H +#define LIB4NEURO_EXCEPTIONS_H + +#include <stdexcept> +#include <boost/type_index.hpp> //TODO Can Boost be included here? + +#ifdef L4N_DEBUG +#define ERR_MSG(msg) std::string(boost::typeindex::type_id_with_cvr<decltype(*this)>().pretty_name()) + "::" + __func__ + "(): " + msg + " (" +__FILE__ + ":" + std::to_string(__LINE__) + ")" +#else +#define ERR_MSG(msg) std::string(boost::typeindex::type_id_with_cvr<decltype(*this)>().pretty_name()) + "::" + __func__ + "(): " + msg + "" +#endif // L4N_DEBUG + +#define THROW_RUNTIME_ERROR(msg) throw std::runtime_error(ERR_MSG(msg)) // Errors, that can't be detected by looking at the code +#define THROW_LOGIC_ERROR(msg) throw std::logic_error(ERR_MSG(msg)) // Errors, that can be detected by looking at the code +#define THROW_INVALID_ARGUMENT_ERROR(msg) throw std::invalid_argument(ERR_MSG(msg)) +#define THROW_NOT_IMPLEMENTED_ERROR(msg) throw std::logic_error(ERR_MSG("This function is not implemented. " + msg)) +#define THROW_OUT_OF_RANGE_ERROR(msg) throw std::out_of_range(ERR_MSG(msg)) + +#endif //LIB4NEURO_EXCEPTIONS_H diff --git a/src/exprtk.cpp b/src/exprtk.cpp index cb6a8ebb2adf7b00eeef1e85f498a71735d6004d..d70f36ffb661ecbfa4bd127461901ae7b62b4af9 100644 --- a/src/exprtk.cpp +++ b/src/exprtk.cpp @@ -1,5 +1,2 @@ -// -// Created by martin on 8/8/18. -// #include <exprtk.hpp> \ No newline at end of file diff --git a/src/message.h b/src/message.h index 75f8098a860b4c5ed053ae169369da6123a9f619..ea134969c8184d2b16e49a07747602947f82b150 100644 --- a/src/message.h +++ b/src/message.h @@ -1,20 +1,24 @@ -// -// Created by martin on 9/8/18. -// #ifndef PROJECT_MESSAGE_H #define PROJECT_MESSAGE_H -#define MSG_INFO(str) {\ - std::cout << "INFO: " << str << std::endl;\ -} +#include <cassert> +#include <iomanip> -#ifdef DEBUG -#define MSG_DEBUG(str) {\ - std::cout << "DEBUG: " << str << std::endl;\ -} +#define COL_WIDTH 20 +#define R_ALIGN std::setw(COL_WIDTH) << std::right + +#define COUT_INFO(inp) std::cout << std::flush << std::fixed << std::setprecision(12) << "INFO: " << inp << std::flush + +#ifdef L4N_DEBUG +#define COUT_DEBUG(inp) assert(std::cerr << std::flush << std::fixed << std::setprecision(12) << "DEBUG: " << inp << std::flush) + +#define WRITE_TO_OFS_DEBUG(ofs, msg) { if(ofs && ofs->is_open())\ + *ofs << std::fixed << std::setprecision(12) << "DEBUG: " << msg;\ + } #else -#define MSG_DEBUG(str) -#endif +#define COUT_DEBUG(inp) +#define WRITE_TO_OFS_DEBUG(ofs, msg) +#endif // L4N_DEBUG #endif //PROJECT_MESSAGE_H diff --git a/src/settings.h b/src/settings.h index 4ed3e1860035bef5188d28f7982a604b07dc16d0..c7c72710f841f030381f93281732e13bcd0140fa 100644 --- a/src/settings.h +++ b/src/settings.h @@ -11,7 +11,6 @@ /** * If defined, the NN feed-forward will print out whats happening */ -//#define VERBOSE_NN_EVAL #ifdef _WINDOWS #define LIB4NEURO_API __declspec(dllexport) diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index 4da5a2430fec97979935d9d9c95547dac2ef220b..cd40522465c122b6754ba412cad3cdd71382c7c0 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -2,46 +2,74 @@ # UNIT TESTS # ############## -add_executable(linear_neuron_test NeuronLinear_test.cpp) -target_link_libraries(linear_neuron_test lib4neuro boost_unit_test) +ADD_EXECUTABLE(linear_neuron_test NeuronLinear_test.cpp) +TARGET_LINK_LIBRARIES(linear_neuron_test lib4neuro boost_unit_test) +TARGET_INCLUDE_DIRECTORIES(linear_neuron_test PRIVATE ${Boost_INCLUDE_DIRS} ${TURTLE_INCLUDE_DIR}) -add_executable(constant_neuron_test NeuronConstant_test.cpp) -target_link_libraries(constant_neuron_test lib4neuro boost_unit_test) +ADD_EXECUTABLE(constant_neuron_test NeuronConstant_test.cpp) +TARGET_LINK_LIBRARIES(constant_neuron_test lib4neuro boost_unit_test) +TARGET_INCLUDE_DIRECTORIES(constant_neuron_test PRIVATE ${Boost_INCLUDE_DIRS} ${TURTLE_INCLUDE_DIR}) -add_executable(binary_neuron_test NeuronBinary_test.cpp) -target_link_libraries(binary_neuron_test lib4neuro boost_unit_test) +ADD_EXECUTABLE(binary_neuron_test NeuronBinary_test.cpp) +TARGET_LINK_LIBRARIES(binary_neuron_test lib4neuro boost_unit_test) +TARGET_INCLUDE_DIRECTORIES(binary_neuron_test PRIVATE ${Boost_INCLUDE_DIRS} ${TURTLE_INCLUDE_DIR}) -add_executable(logistic_neuron_test NeuronLogistic_test.cpp) -target_link_libraries(logistic_neuron_test lib4neuro boost_unit_test) +ADD_EXECUTABLE(logistic_neuron_test NeuronLogistic_test.cpp) +TARGET_LINK_LIBRARIES(logistic_neuron_test lib4neuro boost_unit_test) +TARGET_INCLUDE_DIRECTORIES(logistic_neuron_test PRIVATE ${Boost_INCLUDE_DIRS} ${TURTLE_INCLUDE_DIR}) -add_executable(connectionFunctionGeneral_test ConnectionFunctionGeneral_test.cpp) -target_link_libraries(connectionFunctionGeneral_test lib4neuro boost_unit_test) +ADD_EXECUTABLE(connectionFunctionGeneral_test ConnectionFunctionGeneral_test.cpp) +TARGET_LINK_LIBRARIES(connectionFunctionGeneral_test lib4neuro boost_unit_test) +TARGET_INCLUDE_DIRECTORIES(connectionFunctionGeneral_test PRIVATE ${Boost_INCLUDE_DIRS} ${TURTLE_INCLUDE_DIR}) -add_executable(neural_network_test NeuralNetwork_test.cpp) -target_link_libraries(neural_network_test lib4neuro boost_unit_test) +ADD_EXECUTABLE(neural_network_test NeuralNetwork_test.cpp) +TARGET_LINK_LIBRARIES(neural_network_test lib4neuro boost_unit_test) +TARGET_INCLUDE_DIRECTORIES(neural_network_test PRIVATE ${Boost_INCLUDE_DIRS} ${TURTLE_INCLUDE_DIR}) -add_executable(connection_Function_identity_test ConnectionFunctionIdentity_test.cpp) -target_link_libraries(connection_Function_identity_test lib4neuro boost_unit_test) +ADD_EXECUTABLE(connection_Function_identity_test ConnectionFunctionIdentity_test.cpp) +TARGET_LINK_LIBRARIES(connection_Function_identity_test lib4neuro boost_unit_test) +TARGET_INCLUDE_DIRECTORIES(connection_Function_identity_test PRIVATE ${Boost_INCLUDE_DIRS} ${TURTLE_INCLUDE_DIR}) -add_executable(dataset_test DataSet_test.cpp) -target_link_libraries(dataset_test lib4neuro boost_unit_test) +ADD_EXECUTABLE(dataset_test DataSet_test.cpp) +TARGET_LINK_LIBRARIES(dataset_test lib4neuro boost_unit_test) +TARGET_INCLUDE_DIRECTORIES(dataset_test PRIVATE ${Boost_INCLUDE_DIRS} ${TURTLE_INCLUDE_DIR}) -add_executable(errorfunction_test ErrorFunctions_test.cpp) -target_link_libraries(errorfunction_test lib4neuro boost_unit_test) +ADD_EXECUTABLE(errorfunction_test ErrorFunctions_test.cpp) +TARGET_LINK_LIBRARIES(errorfunction_test lib4neuro boost_unit_test) +TARGET_INCLUDE_DIRECTORIES(errorfunction_test PRIVATE ${Boost_INCLUDE_DIRS} ${TURTLE_INCLUDE_DIR}) -add_executable(particle_swarm_test ParticleSwarm_test.cpp) -target_link_libraries(particle_swarm_test lib4neuro boost_unit_test) +ADD_EXECUTABLE(particle_swarm_test ParticleSwarm_test.cpp) +TARGET_LINK_LIBRARIES(particle_swarm_test lib4neuro boost_unit_test) +TARGET_INCLUDE_DIRECTORIES(particle_swarm_test PRIVATE ${Boost_INCLUDE_DIRS} ${TURTLE_INCLUDE_DIR}) -add_executable(particle_test Particle_test.cpp) -target_link_libraries(particle_test lib4neuro boost_unit_test) +ADD_EXECUTABLE(particle_test Particle_test.cpp) +TARGET_LINK_LIBRARIES(particle_test lib4neuro boost_unit_test) +TARGET_INCLUDE_DIRECTORIES(particle_test PRIVATE ${Boost_INCLUDE_DIRS} ${TURTLE_INCLUDE_DIR}) -add_executable(NeuralNetworkSum_test NeuralNetworkSum_test.cpp) -target_link_libraries(NeuralNetworkSum_test lib4neuro boost_unit_test) +ADD_EXECUTABLE(NeuralNetworkSum_test NeuralNetworkSum_test.cpp) +TARGET_LINK_LIBRARIES(NeuralNetworkSum_test lib4neuro boost_unit_test) +TARGET_INCLUDE_DIRECTORIES(NeuralNetworkSum_test PRIVATE ${Boost_INCLUDE_DIRS} ${TURTLE_INCLUDE_DIR}) -add_executable(DESolver_test DESolver_test.cpp) -target_link_libraries(DESolver_test lib4neuro boost_unit_test) +ADD_EXECUTABLE(DESolver_test DESolver_test.cpp) +TARGET_LINK_LIBRARIES(DESolver_test lib4neuro boost_unit_test) +TARGET_INCLUDE_DIRECTORIES(DESolver_test PRIVATE ${Boost_INCLUDE_DIRS} ${TURTLE_INCLUDE_DIR}) -set_target_properties( +#TODO fix GradientDescent test +#add_executable(GradientDescent_test GradientDescent_test.cpp) +#target_link_libraries(GradientDescent_test lib4neuro boost_unit_test) +#target_include_directories(GradientDescent_test PRIVATE ${Boost_INCLUDE_DIRS} ${TURTLE_INCLUDE_DIR}) + + +SET(TEST_OUTPUT_DIR ${PROJECT_BINARY_DIR}/tests) + +SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${TEST_OUTPUT_DIR}) +SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${TEST_OUTPUT_DIR}) +SET(CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG ${TEST_OUTPUT_DIR}) +SET(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE ${TEST_OUTPUT_DIR}) +SET(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG ${TEST_OUTPUT_DIR}) +SET(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE ${TEST_OUTPUT_DIR}) + +SET_TARGET_PROPERTIES( linear_neuron_test constant_neuron_test binary_neuron_test @@ -55,10 +83,18 @@ set_target_properties( NeuralNetworkSum_test errorfunction_test DESolver_test + # GradientDescent_test + PROPERTIES - ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib" - LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/lib" - RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/unit-tests" + ARCHIVE_OUTPUT_DIRECTORY $<1:${TEST_OUTPUT_DIR}> + LIBRARY_OUTPUT_DIRECTORY $<1:${TEST_OUTPUT_DIR}> + RUNTIME_OUTPUT_DIRECTORY $<1:${TEST_OUTPUT_DIR}> + #CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${TEST_OUTPUT_DIR} + #CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${TEST_OUTPUT_DIR} + #CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG ${TEST_OUTPUT_DIR} + #CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE ${TEST_OUTPUT_DIR} + #CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG ${TEST_OUTPUT_DIR} + #CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE ${TEST_OUTPUT_DIR} ) diff --git a/src/tests/ConnectionFunctionGeneral_test.cpp b/src/tests/ConnectionFunctionGeneral_test.cpp index 7675d51f9af5899011edbd2424f1cd68a7a36e94..36d6f86b21d7d58c562b1049c7840e4b33fd6f27 100644 --- a/src/tests/ConnectionFunctionGeneral_test.cpp +++ b/src/tests/ConnectionFunctionGeneral_test.cpp @@ -7,6 +7,11 @@ #define BOOST_TEST_MODULE Connection_test +// TODO fix boost_test_lib and remove the following include! +#ifdef _WINDOWS +#include <boost/test/included/unit_test.hpp> +#endif + #include "boost_unit_tests_preamble.h" #include "../NetConnection/ConnectionFunctionGeneral.h" @@ -24,21 +29,22 @@ BOOST_AUTO_TEST_SUITE(Connection_test) */ BOOST_AUTO_TEST_CASE(Connection_construction__test) { - BOOST_CHECK_NO_THROW(ConnectionFunctionGeneral *functionGeneral = new ConnectionFunctionGeneral()); + BOOST_CHECK_NO_THROW(ConnectionFunctionGeneral* functionGeneral = new ConnectionFunctionGeneral()); std::vector<size_t> param_indices; param_indices.push_back(0); std::string paramToFunction = "this do nothing! Why is it here?"; - BOOST_CHECK_NO_THROW(ConnectionFunctionGeneral *functionGeneral = new ConnectionFunctionGeneral(param_indices, - paramToFunction)); + BOOST_CHECK_NO_THROW(ConnectionFunctionGeneral* functionGeneral = new ConnectionFunctionGeneral(param_indices, + paramToFunction)); } BOOST_AUTO_TEST_CASE(Connection_eval_test) { - ConnectionFunctionGeneral *functionGeneral = new ConnectionFunctionGeneral(); + ConnectionFunctionGeneral* functionGeneral = new ConnectionFunctionGeneral(); //TODO implementation not finnish yet; std::vector<double> parameter_space; - BOOST_CHECK_EQUAL(0, functionGeneral->eval(parameter_space)); + BOOST_CHECK_EQUAL(0, + functionGeneral->eval(parameter_space)); } BOOST_AUTO_TEST_CASE(Connection_eval_partial_derivative_test) { @@ -46,4 +52,4 @@ BOOST_AUTO_TEST_SUITE(Connection_test) } -BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/tests/ConnectionFunctionIdentity_test.cpp b/src/tests/ConnectionFunctionIdentity_test.cpp index bb859971abbbf08e01edebe72d88694d25ad3b4f..e9f5480b3e7ab0093ff047fddf70521c409caf32 100644 --- a/src/tests/ConnectionFunctionIdentity_test.cpp +++ b/src/tests/ConnectionFunctionIdentity_test.cpp @@ -8,6 +8,11 @@ #define BOOST_TEST_MODULE ConnectionWeightIdentity_test +// TODO fix boost_test_lib and remove the following include! +#ifdef _WINDOWS +#include <boost/test/included/unit_test.hpp> +#endif + #include "boost_unit_tests_preamble.h" #include "../NetConnection/ConnectionFunctionIdentity.h" @@ -17,32 +22,35 @@ */ BOOST_AUTO_TEST_SUITE(ConnectionWeightIdentity_test) - /** - * Test of correct construction of ConnectionFunctionIdentity - */ +/** + * Test of correct construction of ConnectionFunctionIdentity + */ BOOST_AUTO_TEST_CASE(ConnectionWeightIdentity_construction_test) { std::vector<double> weight_array = {1, 2, 3, 4, 5}; //Test of none exception when creation new instance of ConnectionFunctionIdentity - BOOST_CHECK_NO_THROW(ConnectionFunctionIdentity *CFI = new ConnectionFunctionIdentity() ); - BOOST_CHECK_NO_THROW(ConnectionFunctionIdentity *CFI = new ConnectionFunctionIdentity(2) ); + BOOST_CHECK_NO_THROW(ConnectionFunctionIdentity* CFI = new ConnectionFunctionIdentity()); + BOOST_CHECK_NO_THROW(ConnectionFunctionIdentity* CFI = new ConnectionFunctionIdentity(2)); } - /** - * Test of eval method - */ +/** + * Test of eval method + */ BOOST_AUTO_TEST_CASE(ConnectionWeightIdentity_eval_test) { - ConnectionFunctionIdentity *CFI1 = new ConnectionFunctionIdentity(); - ConnectionFunctionIdentity *CFI2 = new ConnectionFunctionIdentity(0); - ConnectionFunctionIdentity *CFI3 = new ConnectionFunctionIdentity(2); + ConnectionFunctionIdentity* CFI1 = new ConnectionFunctionIdentity(); + ConnectionFunctionIdentity* CFI2 = new ConnectionFunctionIdentity(0); + ConnectionFunctionIdentity* CFI3 = new ConnectionFunctionIdentity(2); std::vector<double> parameter_space; parameter_space.push_back(5); //Test of correct output of eval method - BOOST_CHECK_EQUAL(1, CFI1->eval(parameter_space)); - BOOST_CHECK_EQUAL(5, CFI2->eval(parameter_space)); - BOOST_CHECK_THROW(CFI3->eval(parameter_space), std::out_of_range); + BOOST_CHECK_EQUAL(1, + CFI1->eval(parameter_space)); + BOOST_CHECK_EQUAL(5, + CFI2->eval(parameter_space)); + BOOST_CHECK_THROW(CFI3->eval(parameter_space), + std::out_of_range); } diff --git a/src/tests/DESolver_test.cpp b/src/tests/DESolver_test.cpp index 08e94178f601304120600138aa8c02f2bae01407..aaf1bd52508fb82a5ec9bbc5a514be8e844a44f3 100644 --- a/src/tests/DESolver_test.cpp +++ b/src/tests/DESolver_test.cpp @@ -7,12 +7,17 @@ #define BOOST_TEST_MODULE DESolver_test +// TODO fix boost_test_lib and remove the following include! +#ifdef _WINDOWS +#include <boost/test/included/unit_test.hpp> +#endif + #include "boost_unit_tests_preamble.h" #include <iostream> #include "../Solvers/DESolver.h" - +using namespace lib4neuro; /** * Boost testing suite for testing DESolver.h @@ -20,46 +25,56 @@ */ BOOST_AUTO_TEST_SUITE(DESolver_test) - /** - * Test of MultiIndex construction test - */ +/** + * Test of MultiIndex construction test + */ BOOST_AUTO_TEST_CASE(MultiIndex_construction_test) { BOOST_CHECK_NO_THROW(MultiIndex multiIndex(2)); } - /** - * Test of MultiIndex set_partial_deravitive method - */ +/** + * Test of MultiIndex set_partial_deravitive method + */ BOOST_AUTO_TEST_CASE(MultiIndex_set_partial_derivative_test) { MultiIndex multiIndex(2); - BOOST_CHECK_NO_THROW(multiIndex.set_partial_derivative(0, 1)); - BOOST_CHECK_NO_THROW(multiIndex.set_partial_derivative(1, 2)); + BOOST_CHECK_NO_THROW(multiIndex.set_partial_derivative(0, + 1)); + BOOST_CHECK_NO_THROW(multiIndex.set_partial_derivative(1, + 2)); //BOOST_CHECK_THROW(multiIndex.set_partial_derivative(2, 3), std::out_of_range); } - /** - * Testo of MultiIndex get_partial_derivative_degrees method - */ +/** + * Testo of MultiIndex get_partial_derivative_degrees method + */ BOOST_AUTO_TEST_CASE(MultiIndex_get_partial_derivative_degrees_test) { MultiIndex multiIndex(2); - multiIndex.set_partial_derivative(0, 1); - multiIndex.set_partial_derivative(1, 2); - - BOOST_CHECK_EQUAL(1, multiIndex.get_partial_derivatives_degrees()->at(0)); - BOOST_CHECK_EQUAL(2, multiIndex.get_partial_derivatives_degrees()->at(1)); + multiIndex.set_partial_derivative(0, + 1); + multiIndex.set_partial_derivative(1, + 2); + + BOOST_CHECK_EQUAL(1, + multiIndex.get_partial_derivatives_degrees()->at(0)); + BOOST_CHECK_EQUAL(2, + multiIndex.get_partial_derivatives_degrees()->at(1)); } - /** - * Test of MultiIndex operator< method - */ +/** + * Test of MultiIndex operator< method + */ BOOST_AUTO_TEST_CASE(MultiIndex_operator_test) { MultiIndex multiIndex1(1); - multiIndex1.set_partial_derivative(0, 1); + multiIndex1.set_partial_derivative(0, + 1); MultiIndex multiIndex2(2); - multiIndex2.set_partial_derivative(0, 1); - multiIndex2.set_partial_derivative(1, 2); + multiIndex2.set_partial_derivative(0, + 1); + multiIndex2.set_partial_derivative(1, + 2); MultiIndex multiIndex3(1); - multiIndex3.set_partial_derivative(0, 2); + multiIndex3.set_partial_derivative(0, + 2); BOOST_CHECK(multiIndex1.operator<(multiIndex2)); //BOOST_CHECK_THROW(multiIndex2.operator<(multiIndex1), std::out_of_range); @@ -67,64 +82,84 @@ BOOST_AUTO_TEST_SUITE(DESolver_test) BOOST_CHECK(multiIndex1.operator<((multiIndex3))); } - /** - * Test of MultiIndex toString method - */ +/** + * Test of MultiIndex toString method + */ BOOST_AUTO_TEST_CASE(MultiIndex_toString_test) { MultiIndex multiIndex(2); - BOOST_CHECK_EQUAL("0, 0", multiIndex.to_string()); + BOOST_CHECK_EQUAL("0, 0", + multiIndex.to_string()); } - /** - * Test of MultiIndex get_degree method - */ +/** + * Test of MultiIndex get_degree method + */ BOOST_AUTO_TEST_CASE(MultiIndex_get_degree_test) { MultiIndex multiIndex(2); - BOOST_CHECK_EQUAL(0, multiIndex.get_degree()); + BOOST_CHECK_EQUAL(0, + multiIndex.get_degree()); - multiIndex.set_partial_derivative(0, 1); - multiIndex.set_partial_derivative(1, 3); + multiIndex.set_partial_derivative(0, + 1); + multiIndex.set_partial_derivative(1, + 3); - BOOST_CHECK_EQUAL(4, multiIndex.get_degree()); + BOOST_CHECK_EQUAL(4, + multiIndex.get_degree()); } - /** - * Test of DESolver construction - */ +/** + * Test of DESolver construction + */ BOOST_AUTO_TEST_CASE(DESolver_construction_test) { - BOOST_CHECK_THROW(DESolver(0, 1, 1), std::invalid_argument); - BOOST_CHECK_THROW(DESolver(1, 0, 1), std::invalid_argument); - BOOST_CHECK_THROW(DESolver(1, 1, 0), std::invalid_argument); - BOOST_CHECK_NO_THROW(DESolver deSolver(1, 1, 1)); - - /*boost::test_tools::output_test_stream output; - { - cout_redirect guard(output.rdbuf()); - DESolver deSolver(1,1,1,1); - } - BOOST_CHECK(output.is_equal("Differential Equation Solver with 1 equations\n--------------------------------------------------------------------------\nConstructing NN structure representing the solution [1 input neurons][1 inner neurons][1 output neurons]...\n adding a connection between input neuron 0 and inner neuron 0, weight index 0\n adding a connection between inner neuron 0 and output neuron 0, weight index 1\ndone\n\n")); - */ + BOOST_CHECK_THROW(DESolver(0, + 1, + 1), + std::invalid_argument); + BOOST_CHECK_THROW(DESolver(1, + 0, + 1), + std::invalid_argument); + BOOST_CHECK_THROW(DESolver(1, + 1, + 0), + std::invalid_argument); + BOOST_CHECK_NO_THROW(DESolver deSolver(1, + 1, + 1)); + + //TODO fix it + //std::stringstream buffer1; + //std::streambuf * old1 = std::cout.rdbuf(buffer1.rdbuf()); + //DESolver deSolver(1, 1, 1); + //std::string text = buffer1.str(); + // + // // BOOST_CHECK(text._Equal("Differential Equation Solver with 1 equations\n--------------------------------------------------------------------------\nConstructing NN structure representing the solution [1 input neurons][1 inner neurons][1 output neurons]...\n adding a connection between input neuron 0 and inner neuron 0, weight index 0\n adding a connection between inner neuron 0 and output neuron 0, weight index 1\ndone\n\n")); + //std::cout.rdbuf(old1); } - /** - * Test of DESolver get_solution method - */ +/** + * Test of DESolver get_solution method + */ BOOST_AUTO_TEST_CASE(DESolver_get_solution_test) { - DESolver deSolver(1, 1, 1); - MultiIndex *alpha = new MultiIndex(1); - NeuralNetwork *network = deSolver.get_solution(*alpha); - BOOST_CHECK_EQUAL(1, network->get_n_inputs()); - BOOST_CHECK_EQUAL(1, network->get_n_outputs()); + DESolver deSolver(1, + 1, + 1); + MultiIndex* alpha = new MultiIndex(1); + BOOST_CHECK_EQUAL(1, + deSolver.get_solution(*alpha)->get_n_inputs()); + BOOST_CHECK_EQUAL(1, + deSolver.get_solution(*alpha)->get_n_outputs()); } - BOOST_AUTO_TEST_CASE(DESolver_add_eq_test){ - DESolver *deSolver = new DESolver(1,1,1); + BOOST_AUTO_TEST_CASE(DESolver_add_eq_test) { + /*DESolver *deSolver = new DESolver(1,1,1); MultiIndex *multiIndex = new MultiIndex(2); multiIndex->set_partial_derivative(0,1); multiIndex->set_partial_derivative(1,0.5); - deSolver->add_to_differential_equation(0, *multiIndex, "0.5" ); - + deSolver->add_to_differential_equation(0, *multiIndex, "0.5" ); + std::vector<double> inp, out; std::vector<std::pair<std::vector<double>, std::vector<double>>> data_vec_dy; inp = {0.0}; @@ -136,7 +171,7 @@ BOOST_AUTO_TEST_SUITE(DESolver_test) std::vector<double> weights; weights.push_back(1.0); - BOOST_CHECK_EQUAL(64,deSolver->eval_total_error(weights)); + BOOST_CHECK_EQUAL(64,deSolver->eval_total_error(weights));*/ } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/tests/DataSet_test.cpp b/src/tests/DataSet_test.cpp index 23dc52ee76dce2a66ad5a07000ebeabbc4ae1212..15762e1292d85918b338384ec1e74cb6e43d2b3f 100644 --- a/src/tests/DataSet_test.cpp +++ b/src/tests/DataSet_test.cpp @@ -6,41 +6,42 @@ */ #define BOOST_TEST_MODULE DataSet_test - -#include "boost_unit_tests_preamble.h" -#include "../DataSet/DataSet.h" -//#include <cstdio> -//#include <iostream> +// TODO fix boost_test_lib and remove the following include! +#ifdef _WINDOWS +#include <boost/test/included/unit_test.hpp> +#endif + +#ifndef BOOST_TEST_DYN_LINK +#define BOOST_TEST_DYN_LINK +#endif + +#ifndef BOOST_TEST_NO_MAIN +#define BOOST_TEST_NO_MAIN +#endif -//#include <boost/filesystem.hpp> +#include <boost/test/unit_test.hpp> +#include <boost/test/output_test_stream.hpp> +#include "../DataSet/DataSet.h" +#include "stdio.h" +#include <iostream> /** * Boost testing suite for testing DataSet.h */ BOOST_AUTO_TEST_SUITE(DataSet_test) - struct cout_redirect { - cout_redirect(std::streambuf *new_buffer) - : old(std::cout.rdbuf(new_buffer)) {} - - ~cout_redirect() { - std::cout.rdbuf(old); - } - - private: - std::streambuf *old; - }; /** - * Test of DataSet constructor with filepath parameter + * Test of lib4neuro::DataSet constructor with filepath parameter */ BOOST_AUTO_TEST_CASE(DataSet_construction_from_file_test) { //test of exception with non-existing file path //TODO resolve exception throw - // DataSet dataSet("file/unknown"); - //BOOST_CHECK_THROW(DataSet dataSet("file unknown"), boost::archive::archive_exception::input_stream_error); + //lib4neuro::DataSet DataSet("file/unknown"); + + //BOOST_CHECK_THROW(lib4neuro::DataSet DataSet("file unknown"), std::out_of_range);// boost::archive::archive_exception::input_stream_error); } /** @@ -48,181 +49,21 @@ BOOST_AUTO_TEST_SUITE(DataSet_test) */ BOOST_AUTO_TEST_CASE(DataSet_construction_from_vector_test) { std::vector<std::pair<std::vector<double>, std::vector<double>>> data_vec; - std::vector<double> inp, out; + std::vector<double> inp, out; for (int i = 0; i < 3; i++) { inp.push_back(i); out.push_back(i + 4); } - data_vec.emplace_back(std::make_pair(inp, out)); + data_vec.emplace_back(std::make_pair(inp, + out)); - DataSet dataSet(&data_vec); + lib4neuro::DataSet ds(&data_vec); // TODO why is the ds object created unitialized? //test of no exception when create object DataSet - BOOST_CHECK_NO_THROW(DataSet dataSet(&data_vec)); - } - -/** - * Test of get_data method - */ - BOOST_AUTO_TEST_CASE(DataSet_get_data_test) { - std::vector<std::pair<std::vector<double>, std::vector<double>>> data_vec; - std::vector<double> inp, out; - - for (int i = 0; i < 1; i++) { - inp.push_back(i); - out.push_back(i + 4); - } - - data_vec.emplace_back(std::make_pair(inp, out)); - DataSet dataSet(&data_vec); - - //test of equal data - //TODO out of range, == - BOOST_CHECK_EQUAL(0, dataSet.get_data()->at(0).first.at(0)); - BOOST_CHECK_EQUAL(4, dataSet.get_data()->at(0).second.at(0)); - - } - -/** - * Test of add_data_pair method - */ - BOOST_AUTO_TEST_CASE(DataSet_add_daata_pair_test) { - std::vector<std::pair<std::vector<double>, std::vector<double>>> data_vec; - std::vector<double> inp, out; - - for (int i = 0; i < 3; i++) { - inp.push_back(i); - out.push_back(i + 4); - } - - data_vec.emplace_back(std::make_pair(inp, out)); - - DataSet dataSet(&data_vec); - - inp.clear(); - out.clear(); - for (int i = 8; i < 11; i++) { - inp.push_back(i); - out.push_back(i + 4); - } - - dataSet.add_data_pair(inp, out); - - // Test of correct add of input - BOOST_CHECK_EQUAL(8, dataSet.get_data()->at(1).first.at(0)); - // Test of correct add of output - BOOST_CHECK_EQUAL(12, dataSet.get_data()->at(1).second.at(0)); - - } - - /** - * Test of get_input_dim and get_output_dim methods - */ - BOOST_AUTO_TEST_CASE(DataSet_dimension_test) { - std::vector<std::pair<std::vector<double>, std::vector<double>>> data_vec; - std::vector<double> inp, out; - - for (int i = 0; i < 3; i++) { - inp.push_back(i); - out.push_back(i + 4); - } - - data_vec.emplace_back(std::make_pair(inp, out)); - - DataSet dataSet(&data_vec); - - //Test of correct input dimension - BOOST_CHECK_EQUAL(3, dataSet.get_input_dim()); - //Test of correct output dimension - BOOST_CHECK_EQUAL(3, dataSet.get_output_dim()); - } - -/** - * Test of get_n_elements method - */ - BOOST_AUTO_TEST_CASE(DataSet_get_number_of_elements_test) { - std::vector<std::pair<std::vector<double>, std::vector<double>>> data_vec; - std::vector<double> inp, out; - - for (int i = 0; i < 3; i++) { - inp.push_back(i); - out.push_back(i + 4); - } - data_vec.emplace_back(std::make_pair(inp, out)); - inp.clear(); - out.clear(); - for (int i = 8; i < 11; i++) { - inp.push_back(i); - out.push_back(i + 4); - } - data_vec.emplace_back(std::make_pair(inp, out)); - - DataSet dataSet(&data_vec); - - //Test of correct number of elements - BOOST_CHECK_EQUAL(2, dataSet.get_n_elements()); + BOOST_CHECK_NO_THROW(new lib4neuro::DataSet(&data_vec)); } -/** - * Test of print_data method - */ - BOOST_AUTO_TEST_CASE(DataSet_print_data_test) { - //TODO this test causes problems on windows machines - /* - std::vector<std::pair<std::vector<double>, std::vector<double>>> data_vec; - std::vector<double> inp, out; - - for (int i = 0; i < 1; i++) { - inp.push_back(i); - out.push_back(i + 4); - } - - data_vec.emplace_back(std::make_pair(inp, out)); - - DataSet dataSet(&data_vec); - - boost::test_tools::output_test_stream output; - { - cout_redirect guard(output.rdbuf()); - dataSet.print_data(); - } - - //Test of correct print of DataSet - BOOST_CHECK(output.is_equal("0 -> 4 \n")); - */ - } - -/** - * Test of store_text method - */ - BOOST_AUTO_TEST_CASE(DataSet_store_text_test) { - std::vector<std::pair<std::vector<double>, std::vector<double>>> data_vec; - std::vector<double> inp, out; - - for (int i = 0; i < 3; i++) { - inp.push_back(i); - out.push_back(i + 4); - } - - data_vec.emplace_back(std::make_pair(inp, out)); - - DataSet dataSet(&data_vec); - int elements = dataSet.get_n_elements(); - std::string filename = "testDataSet"; - dataSet.store_text(filename); - - //Test of correct file creations - //BOOST_CHECK(boost::filesystem::exists( "testDataSet" )); - - DataSet newDataSet("testDataSet"); - - //Test of correct number of element from dataSet from file - BOOST_CHECK_EQUAL(elements, newDataSet.get_n_elements()); - - // removing of created file - remove("testDataSet"); - } -BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/tests/ErrorFunctions_test.cpp b/src/tests/ErrorFunctions_test.cpp index a45879c2c07a1077a155dd90a990ee02a02a0328..2364dfda1b366496bc17a5e267c9a6d115ef3ebd 100644 --- a/src/tests/ErrorFunctions_test.cpp +++ b/src/tests/ErrorFunctions_test.cpp @@ -6,88 +6,252 @@ */ #define BOOST_TEST_MODULE ErrorFunctions_test +// TODO fix boost_test_lib and remove the following include! +#ifdef _WINDOWS +#include <boost/test/included/unit_test.hpp> +#endif + #include "boost_unit_tests_preamble.h" -#include "../ErrorFunction/ErrorFunctions.h" +#include "../ErrorFunction/ErrorFunctionsMock.h" + +using namespace lib4neuro; + +MOCK_BASE_CLASS(mock_network, + lib4neuro::NeuralNetwork +) { + MOCK_METHOD(get_subnet, + 2) + + MOCK_METHOD(add_neuron, + 3) + + MOCK_METHOD(add_connection_simple, + 4) + + MOCK_METHOD(add_existing_connection, + 4) + + MOCK_METHOD(copy_parameter_space, + 1) + + MOCK_METHOD(set_parameter_space_pointers, + 1) + + MOCK_METHOD(eval_single, + 3) + + MOCK_METHOD(add_to_gradient_single, + 4) + + MOCK_METHOD(randomize_weights, + 0) + + MOCK_METHOD(randomize_biases, + 0) + + MOCK_METHOD(randomize_parameters, + 0) + + MOCK_METHOD(get_n_inputs, + 0) + + MOCK_METHOD(get_n_outputs, + 0) + + MOCK_METHOD(get_n_weights, + 0) + + MOCK_METHOD(get_n_biases, + 0) + + MOCK_METHOD(get_neuron_bias_index, + 1) + + MOCK_METHOD(get_n_neurons, + 0) + + MOCK_METHOD(specify_input_neurons, + 1) + + MOCK_METHOD(specify_output_neurons, + 1) + + MOCK_METHOD(get_parameter_ptr_biases, + 0) + + MOCK_METHOD(get_parameter_ptr_weights, + 0) + + MOCK_METHOD(save_text, + 1) + + MOCK_METHOD(write_weights, + 0, + void(), + id1) + + MOCK_METHOD(write_weights, + 1, + void(std::string), + id2 + ) + + MOCK_METHOD(write_weights, + 1, + void(std::ofstream + *), + id3) + + MOCK_METHOD(write_biases, + 0, + void(), + id4) + + MOCK_METHOD(write_biases, + 1, + void(std::string), + id5 + ) + + MOCK_METHOD(write_biases, + 1, + void(std::ofstream + *), + id6) + + MOCK_METHOD(write_stats, + 0, + void(), + id7) + + MOCK_METHOD(write_stats, + 1, + void(std::string), + id8 + ) + + MOCK_METHOD(write_stats, + 1, + void(std::ofstream + *), + id9) +}; + +MOCK_BASE_CLASS(mock_dataSet, + lib4neuro::DataSet +) { + mock_dataSet(std::vector<std::pair<std::vector<double>, std::vector<double>>> + * i) + : + lib4neuro::DataSet(i) { + + } + + MOCK_METHOD(add_data_pair, + 2) + + MOCK_METHOD(get_n_elements, + 0) + + MOCK_METHOD(get_input_dim, + 0) + + MOCK_METHOD(get_output_dim, + 0) + + MOCK_METHOD(print_data, + 0) + + MOCK_METHOD(store_text, + 1) + + MOCK_METHOD(store_data_text, + 1, + void(std::string), + id1 + ) + + MOCK_METHOD(store_data_text, + 1, + void(std::ofstream + *), + id2) +}; + /** * Boost testing suite for testing ErrorFunction.h * doesn't test inherited methods */ -BOOST_AUTO_TEST_SUITE(ErrorFunctions_test) +BOOST_AUTO_TEST_SUITE(ErrorFunctions_test); BOOST_AUTO_TEST_CASE(ErrorFunction_MSE_Construction_Test) { - NeuralNetwork network; - std::vector<std::pair<std::vector<double>, std::vector<double>>> data_vec; - std::vector<double> inp, out; - for (int i = 0; i < 3; i++) { - inp.push_back(i); - out.push_back(i + 4); - } - data_vec.emplace_back(std::make_pair(inp, out)); - DataSet dataSet(&data_vec); - BOOST_CHECK_NO_THROW(MSE mse(&network, &dataSet)); + mock_network network; + MOCK_EXPECT(network.get_n_biases).returns(1); + MOCK_EXPECT(network.get_n_weights).returns(1); + std::vector<double> inp, out; + std::vector<std::pair<std::vector<double>, std::vector<double>>> data_vec_dy; + inp = {0.0}; + out = {8.0}; + data_vec_dy.emplace_back(std::make_pair(inp, + out)); + //DataSet ds_02(&data_vec_dy); + + + mock_dataSet dataSet(&data_vec_dy); + + BOOST_CHECK_NO_THROW(MSE mse(&network, + &dataSet)); } BOOST_AUTO_TEST_CASE(ErrorFunction_MSE_Eval_Test) { - Neuron *n1 = new NeuronLinear(); - Neuron *n2 = new NeuronLinear(); - NeuralNetwork network; + mock_network network; + MOCK_EXPECT(network.get_n_biases).returns(1); + MOCK_EXPECT(network.get_n_weights).returns(1); + MOCK_EXPECT(network.eval_single); std::vector<std::pair<std::vector<double>, std::vector<double>>> data_vec; - std::vector<double> inp, out; - for (int i = 0; i < 1; i++) { + std::vector<double> inp, out; + for (int i = 0; i < 1; i++) { inp.push_back(i); out.push_back(i + 4); } - data_vec.emplace_back(std::make_pair(inp, out)); - network.add_neuron(n1); - network.add_neuron(n2); - network.add_connection_simple(0, 1, SIMPLE_CONNECTION_TYPE::UNITARY_WEIGHT, 2.5); - network.randomize_weights(); - std::vector<size_t> net_input_neurons_indices(1); - std::vector<size_t> net_output_neurons_indices(1); - net_input_neurons_indices[0] = 0; - net_output_neurons_indices[0] = 1; - network.specify_input_neurons(net_input_neurons_indices); - network.specify_output_neurons(net_output_neurons_indices); - - DataSet dataSet(&data_vec); + data_vec.emplace_back(std::make_pair(inp, + out)); + + mock_dataSet dataSet(&data_vec); std::vector<double> weights; weights.push_back(1); - MSE mse(&network, &dataSet); + MSE mse(&network, + &dataSet); - BOOST_CHECK_EQUAL(9, mse.eval(&weights)); + BOOST_CHECK_EQUAL(16, + mse.eval(&weights)); } BOOST_AUTO_TEST_CASE(ErrorFunction_MSE_Get_dimension_test) { - Neuron *n1 = new NeuronLinear(); - Neuron *n2 = new NeuronLinear(); - NeuralNetwork network; + mock_network network; + MOCK_EXPECT(network.get_n_biases).returns(1); + MOCK_EXPECT(network.get_n_weights).returns(1); std::vector<std::pair<std::vector<double>, std::vector<double>>> data_vec; - std::vector<double> inp, out; - for (int i = 0; i < 1; i++) { + std::vector<double> inp, out; + for (int i = 0; i < 1; i++) { inp.push_back(i); out.push_back(i + 4); } - data_vec.emplace_back(std::make_pair(inp, out)); - network.add_neuron(n1); - network.add_neuron(n2); - network.add_connection_simple(0, 1, SIMPLE_CONNECTION_TYPE::UNITARY_WEIGHT, 2.5); - network.randomize_weights(); - std::vector<size_t> net_input_neurons_indices(1); - std::vector<size_t> net_output_neurons_indices(1); - net_input_neurons_indices[0] = 0; - net_output_neurons_indices[0] = 1; - network.specify_input_neurons(net_input_neurons_indices); - network.specify_output_neurons(net_output_neurons_indices); - - DataSet dataSet(&data_vec); - - MSE mse(&network, &dataSet); - - BOOST_CHECK_EQUAL(2, mse.get_dimension()); + data_vec.emplace_back(std::make_pair(inp, + out)); + + mock_dataSet dataSet(&data_vec); + + + MSE mse(&network, + &dataSet); + + BOOST_CHECK_EQUAL(2, + mse.get_dimension()); } BOOST_AUTO_TEST_CASE(ErrorFunction_MSE_SUM_Construction_Test) { @@ -95,92 +259,46 @@ BOOST_AUTO_TEST_SUITE(ErrorFunctions_test) } BOOST_AUTO_TEST_CASE(ErrorFunction_MSE_SUM_Add_Error_Function_Test) { - NeuralNetwork network; - std::vector<std::pair<std::vector<double>, std::vector<double>>> data_vec; - std::vector<double> inp, out; - for (int i = 0; i < 3; i++) { - inp.push_back(i); - out.push_back(i + 4); - } - data_vec.emplace_back(std::make_pair(inp, out)); - DataSet dataSet(&data_vec); - - ErrorFunction *f = new MSE(&network, &dataSet); + mock_ErrorFunction f; + MOCK_EXPECT(f.get_dimension).returns(1); ErrorSum mse_sum; - BOOST_CHECK_NO_THROW(mse_sum.add_error_function(f)); + BOOST_CHECK_NO_THROW(mse_sum.add_error_function(&f, + 1)); } BOOST_AUTO_TEST_CASE(ErrorFunction_MSE_SUM_Eval_Test) { + mock_ErrorFunction f; + MOCK_EXPECT(f.get_dimension).returns(1); + MOCK_EXPECT(f.eval).returns(1.75); ErrorSum mse_sum; - Neuron *n1 = new NeuronLinear(); - Neuron *n2 = new NeuronLinear(); - NeuralNetwork network; - std::vector<std::pair<std::vector<double>, std::vector<double>>> data_vec; - std::vector<double> inp, out; - for (int i = 0; i < 1; i++) { - inp.push_back(i); - out.push_back(i + 4); - } - data_vec.emplace_back(std::make_pair(inp, out)); - network.add_neuron(n1); - network.add_neuron(n2); - network.add_connection_simple(0, 1, SIMPLE_CONNECTION_TYPE::UNITARY_WEIGHT, 2.5); - network.randomize_weights(); - std::vector<size_t> net_input_neurons_indices(1); - std::vector<size_t> net_output_neurons_indices(1); - net_input_neurons_indices[0] = 0; - net_output_neurons_indices[0] = 1; - network.specify_input_neurons(net_input_neurons_indices); - network.specify_output_neurons(net_output_neurons_indices); - - DataSet dataSet(&data_vec); - - ErrorFunction *f = new MSE(&network, &dataSet); - - mse_sum.add_error_function(f); - std::vector<double> weights; weights.push_back(1); - BOOST_CHECK_EQUAL(9, mse_sum.eval(&weights)); + mse_sum.add_error_function(&f); + BOOST_CHECK_EQUAL(1.75, + mse_sum.eval(&weights)); } BOOST_AUTO_TEST_CASE(ErrorFunction_MSE_SUM_Get_Dimension_test) { ErrorSum mse_sum; - BOOST_CHECK_EQUAL(0, mse_sum.get_dimension()); + BOOST_CHECK_EQUAL(0, + mse_sum.get_dimension()); + mock_ErrorFunction f; + MOCK_EXPECT(f.get_dimension).returns(2); + MOCK_EXPECT(f.eval).returns(1.75); - Neuron *n1 = new NeuronLinear(); - Neuron *n2 = new NeuronLinear(); - NeuralNetwork network; - std::vector<std::pair<std::vector<double>, std::vector<double>>> data_vec; - std::vector<double> inp, out; - for (int i = 0; i < 1; i++) { - inp.push_back(i); - out.push_back(i + 4); - } - data_vec.emplace_back(std::make_pair(inp, out)); - network.add_neuron(n1); - network.add_neuron(n2); - network.add_connection_simple(0, 1, SIMPLE_CONNECTION_TYPE::UNITARY_WEIGHT, 2.5); - network.randomize_weights(); - std::vector<size_t> net_input_neurons_indices(1); - std::vector<size_t> net_output_neurons_indices(1); - net_input_neurons_indices[0] = 0; - net_output_neurons_indices[0] = 1; - network.specify_input_neurons(net_input_neurons_indices); - network.specify_output_neurons(net_output_neurons_indices); - - DataSet dataSet(&data_vec); - ErrorFunction *f = new MSE(&network, &dataSet); + std::vector<double> weights; + weights.push_back(1); - mse_sum.add_error_function(f); + mse_sum.add_error_function(&f); - BOOST_CHECK_EQUAL(2, mse_sum.get_dimension()); + BOOST_CHECK_EQUAL(2, + mse_sum.get_dimension()); } -BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/tests/NeuralNetworkSum_test.cpp b/src/tests/NeuralNetworkSum_test.cpp index e28328bcec744ae8f70d30de3c03d3eb503efbaa..5436159c5af2091cc77d63fdd3182dc167c0a483 100644 --- a/src/tests/NeuralNetworkSum_test.cpp +++ b/src/tests/NeuralNetworkSum_test.cpp @@ -7,76 +7,194 @@ #define BOOST_TEST_MODULE NeuralNetworkSum_test +// TODO fix boost_test_lib and remove the following include! +#ifdef _WINDOWS +#include <boost/test/included/unit_test.hpp> +#endif + #include "boost_unit_tests_preamble.h" #include "../Network/NeuralNetworkSum.h" +#include <turtle/mock.hpp> + +using namespace lib4neuro; + +MOCK_BASE_CLASS(mock_network, + lib4neuro::NeuralNetwork +) { + MOCK_METHOD(get_subnet, + 2) + + MOCK_METHOD(add_neuron, + 3) + + MOCK_METHOD(add_connection_simple, + 4) + + MOCK_METHOD(add_existing_connection, + 4) + + MOCK_METHOD(copy_parameter_space, + 1) + + MOCK_METHOD(set_parameter_space_pointers, + 1) + + MOCK_METHOD(eval_single, + 3) + + MOCK_METHOD(add_to_gradient_single, + 4) + + MOCK_METHOD(randomize_weights, + 0) + + MOCK_METHOD(randomize_biases, + 0) + + MOCK_METHOD(randomize_parameters, + 0) + + MOCK_METHOD(get_n_inputs, + 0) + + MOCK_METHOD(get_n_outputs, + 0) + + MOCK_METHOD(get_n_weights, + 0) + + MOCK_METHOD(get_n_biases, + 0) + + MOCK_METHOD(get_neuron_bias_index, + 1) + + MOCK_METHOD(get_n_neurons, + 0) + + MOCK_METHOD(specify_input_neurons, + 1) + + MOCK_METHOD(specify_output_neurons, + 1) + + MOCK_METHOD(get_parameter_ptr_biases, + 0) + + MOCK_METHOD(get_parameter_ptr_weights, + 0) + + MOCK_METHOD(save_text, + 1) + + MOCK_METHOD(write_weights, + 0, + void(), + id1) + + MOCK_METHOD(write_weights, + 1, + void(std::string), + id2 + ) + + MOCK_METHOD(write_weights, + 1, + void(std::ofstream + *), + id3) + + MOCK_METHOD(write_biases, + 0, + void(), + id4) + + MOCK_METHOD(write_biases, + 1, + void(std::string), + id5 + ) + + MOCK_METHOD(write_biases, + 1, + void(std::ofstream + *), + id6) + + MOCK_METHOD(write_stats, + 0, + void(), + id7) + + MOCK_METHOD(write_stats, + 1, + void(std::string), + id8 + ) + + MOCK_METHOD(write_stats, + 1, + void(std::ofstream + *), + id9) +}; /** * Boost testing suite for testing NeuralNetworkSum.h */ BOOST_AUTO_TEST_SUITE(NeuralNetworkSum_test) - - /** - * Test of creating new instance of NeuralNetworkSum - */ +/** + * Test of creating new instance of NeuralNetworkSum + */ BOOST_AUTO_TEST_CASE(NeuralNetworkSum_constuction_test) { //Test of none exception raise when creating new instance of NeuralNewtwork BOOST_CHECK_NO_THROW(NeuralNetworkSum networkSum); } BOOST_AUTO_TEST_CASE(NeuralNetworkSum_add_network_test) { - NeuralNetwork network; + mock_network network; + //NeuralNetwork network; NeuralNetworkSum networkSum; - - BOOST_CHECK_NO_THROW(networkSum.add_network(&network, "5")); + std::string po = "f(x,y,z,t) =x+y+z+t"; + BOOST_CHECK_NO_THROW(networkSum.add_network(&network, + po)); } BOOST_AUTO_TEST_CASE(NeuralNetworkSum_eval_single_weights_test) { - Neuron *n1 = new NeuronLinear(); - Neuron *n2 = new NeuronLinear(); - NeuralNetwork network; - network.add_neuron(n1); - network.add_neuron(n2); - - network.add_connection_simple(0, 1, SIMPLE_CONNECTION_TYPE::UNITARY_WEIGHT, 2.5); - - std::vector<size_t> output_neuron_indices(1); - output_neuron_indices[0] = (size_t) 1; - network.specify_output_neurons(output_neuron_indices); - std::vector<size_t> input_neuron_indices(1); - input_neuron_indices[0] = (size_t) 0; - network.specify_input_neurons(input_neuron_indices); + mock_network network; + MOCK_EXPECT(network.eval_single); std::vector<double> input; input.push_back(1); std::vector<double> output; output.push_back(1); - double weights = 5; + double weights = 5; NeuralNetworkSum networkSum; - networkSum.add_network(&network, "2"); + networkSum.add_network(&network, + "f(x) =x"); - networkSum.eval_single(input, output); - BOOST_CHECK_EQUAL(2, output.at(0)); + networkSum.eval_single(input, + output); + BOOST_CHECK_EQUAL(0, + output.at(0)); } BOOST_AUTO_TEST_CASE(NeuralNetworkSum_get_weights_test) { NeuralNetworkSum networkSum; - BOOST_CHECK_EQUAL(0, networkSum.get_n_weights()); - - Neuron *n1 = new NeuronLinear(); - Neuron *n2 = new NeuronLinear(); - NeuralNetwork network; - network.add_neuron(n1); - network.add_neuron(n2); - - network.add_connection_simple(0, 1, SIMPLE_CONNECTION_TYPE::NEXT_WEIGHT, 2.5); + BOOST_CHECK_EQUAL(0, + networkSum.get_n_weights()); - networkSum.add_network(&network, "2"); + mock_network network; + MOCK_EXPECT(network.get_n_weights).returns(1); + networkSum.add_network(&network, + "f(x) =x"); - BOOST_CHECK_EQUAL(1, networkSum.get_n_weights()); + BOOST_CHECK_EQUAL(1, + networkSum.get_n_weights()); } -BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/tests/NeuralNetwork_test.cpp b/src/tests/NeuralNetwork_test.cpp index 20b0edc4a122391ece35ab3ccce93d81b327cca2..a2eaa16c49d611ba71ea5d2ef22901e633c0f9b0 100644 --- a/src/tests/NeuralNetwork_test.cpp +++ b/src/tests/NeuralNetwork_test.cpp @@ -7,21 +7,32 @@ #define BOOST_TEST_MODULE NeuralNetwork_test -#include "boost_unit_tests_preamble.h" +// TODO fix boost_test_lib and remove the following include! +#ifdef _WINDOWS +#include <boost/test/included/unit_test.hpp> +#endif +#include "boost_unit_tests_preamble.h" +#include <turtle/mock.hpp> #include "../Network/NeuralNetwork.h" -struct cerr_redirect { - cerr_redirect(std::streambuf *new_buffer) - : old(std::cerr.rdbuf(new_buffer) - ) {} +using namespace lib4neuro; - ~cerr_redirect() { - std::cout.rdbuf(old); - } -private: - std::streambuf *old; +MOCK_BASE_CLASS(mock_NeuronLinear, + lib4neuro::NeuronLinear +) { + MOCK_METHOD(activate, + 2) + + MOCK_METHOD(activation_function_eval_derivative_bias, + 2) + + MOCK_METHOD(activation_function_eval_derivative, + 2) + + MOCK_METHOD(get_derivative, + 0) }; /** @@ -29,109 +40,141 @@ private: */ BOOST_AUTO_TEST_SUITE(NeuralNetwork_test) - /** - * Test of creating new instance of NeuralNetwork - */ - BOOST_AUTO_TEST_CASE(NeuralNetwork_constuction_test) { - //Test of none exception raise when creating new instance of NeuralNewtwork +/** + * Test of creating new instance of NeuralNetwork + */ + BOOST_AUTO_TEST_CASE(NeuralNetwork_construction_test) { + //Test of none exception raise when creating new instance of NeuralNetwork BOOST_CHECK_NO_THROW(NeuralNetwork network); } - /** - * Test of add_neuron method - * Existing bias out of range cancelation - */ +/** + * Test of add_neuron method + * Existing bias out of range cancelation + */ BOOST_AUTO_TEST_CASE(NeuralNetwork_add_neuron_test) { - Neuron *n1 = new NeuronLinear(); - Neuron *n2 = new NeuronLinear(); - Neuron *n3 = new NeuronLinear(); + std::shared_ptr<mock_NeuronLinear> n1(new mock_NeuronLinear); + std::shared_ptr<mock_NeuronLinear> n2(new mock_NeuronLinear); + std::shared_ptr<mock_NeuronLinear> n3(new mock_NeuronLinear); + std::shared_ptr<mock_NeuronLinear> n4(new mock_NeuronLinear); NeuralNetwork network; - //Tests of correct neuron indexs when add_neuron - BOOST_CHECK_EQUAL(0, network.add_neuron(n1)); + //Tests of correct neuron indices when add_neuron + BOOST_CHECK_EQUAL(0, + network.add_neuron(n1)); - BOOST_CHECK_EQUAL(1, network.add_neuron(n2, BIAS_TYPE::NEXT_BIAS)); + BOOST_CHECK_EQUAL(1, + network.add_neuron(n2, + BIAS_TYPE::NEXT_BIAS)); - BOOST_CHECK_EQUAL(2, network.add_neuron(n3, BIAS_TYPE::NO_BIAS)); + BOOST_CHECK_EQUAL(2, + network.add_neuron(n3, + BIAS_TYPE::NO_BIAS)); - BOOST_CHECK_EQUAL(3, network.get_n_neurons()); + BOOST_CHECK_EQUAL(3, + network.get_n_neurons()); - BOOST_CHECK_EQUAL(2, network.get_n_biases()); + BOOST_CHECK_EQUAL(2, + network.get_n_biases()); -//////TODO fix dumping stack error -// boost::test_tools::output_test_stream output; -// { -// cerr_redirect guard(output.rdbuf()); -// network.add_neuron(n3, BIAS_TYPE::EXISTING_BIAS, 3); -// } -// BOOST_CHECK(output.is_equal("The supplied bias index is too large!\n\n")); } - /** - * Test of add_connection_simple method - */ +/** + * Test of add_connection_simple method + */ BOOST_AUTO_TEST_CASE(NeuralNetwork_add_connection_simple_test) { - Neuron *n1 = new NeuronLinear(); - Neuron *n2 = new NeuronLinear(); - NeuralNetwork network; - network.add_neuron(n1, BIAS_TYPE::NO_BIAS); - network.add_neuron(n2, BIAS_TYPE::NO_BIAS); - - BOOST_CHECK_EQUAL(0, network.add_connection_simple(0, 1, SIMPLE_CONNECTION_TYPE::NEXT_WEIGHT)); - BOOST_CHECK_EQUAL(1, network.add_connection_simple(0, 1, SIMPLE_CONNECTION_TYPE::NEXT_WEIGHT, 5)); - BOOST_CHECK_EQUAL(2, network.add_connection_simple(0, 1, SIMPLE_CONNECTION_TYPE::UNITARY_WEIGHT)); - BOOST_CHECK_EQUAL(3, network.add_connection_simple(0, 1, SIMPLE_CONNECTION_TYPE::UNITARY_WEIGHT, 5)); - BOOST_CHECK_EQUAL(4, network.add_connection_simple(0, 1, SIMPLE_CONNECTION_TYPE::EXISTING_WEIGHT, 1)); - - BOOST_CHECK_EQUAL(2, network.get_n_weights()); -//TODO fix dumping stack error -// boost::test_tools::output_test_stream output; -// { -// cerr_redirect guard(output.rdbuf()); -// network.add_connection_simple(0, 1, SIMPLE_CONNECTION_TYPE::EXISTING_WEIGHT,10); -// } -// BOOST_CHECK(output.is_equal("The supplied connection weight index is too large!\n\n")); + std::shared_ptr<mock_NeuronLinear> n1(new mock_NeuronLinear); + std::shared_ptr<mock_NeuronLinear> n2(new mock_NeuronLinear); + NeuralNetwork network; + network.add_neuron(n1, + BIAS_TYPE::NO_BIAS); + network.add_neuron(n2, + BIAS_TYPE::NO_BIAS); + + BOOST_CHECK_EQUAL(0, + network.add_connection_simple(0, + 1, + SIMPLE_CONNECTION_TYPE::NEXT_WEIGHT)); + BOOST_CHECK_EQUAL(1, + network.add_connection_simple(0, + 1, + SIMPLE_CONNECTION_TYPE::NEXT_WEIGHT, + 5)); + BOOST_CHECK_EQUAL(2, + network.add_connection_simple(0, + 1, + SIMPLE_CONNECTION_TYPE::UNITARY_WEIGHT)); + BOOST_CHECK_EQUAL(3, + network.add_connection_simple(0, + 1, + SIMPLE_CONNECTION_TYPE::UNITARY_WEIGHT, + 5)); + BOOST_CHECK_EQUAL(4, + network.add_connection_simple(0, + 1, + SIMPLE_CONNECTION_TYPE::EXISTING_WEIGHT, + 1)); + + BOOST_CHECK_EQUAL(2, + network.get_n_weights()); } - /** - * Test of add_connection_general method - */ +/** + * Test of add_connection_general method + */ BOOST_AUTO_TEST_CASE(NeuralNetwork_specify_inputs_neurons_test) { - NeuralNetwork network; - Neuron *n1 = new NeuronLinear(); - network.add_neuron(n1, BIAS_TYPE::NO_BIAS); + NeuralNetwork network; + mock_NeuronLinear po; + std::shared_ptr<mock_NeuronLinear> n1(new mock_NeuronLinear()); + + network.add_neuron(n1, + BIAS_TYPE::NO_BIAS); std::vector<size_t> inputs; inputs.push_back(0); - BOOST_CHECK_EQUAL(0, network.get_n_inputs()); + BOOST_CHECK_EQUAL(0, + network.get_n_inputs()); network.specify_input_neurons(inputs); - BOOST_CHECK_EQUAL(1, network.get_n_inputs()); + BOOST_CHECK_EQUAL(1, + network.get_n_inputs()); } BOOST_AUTO_TEST_CASE(NeuralNetwork_specify_outputs_neurons_test) { - NeuralNetwork network; - Neuron *n1 = new NeuronLinear(); - network.add_neuron(n1, BIAS_TYPE::NO_BIAS); + NeuralNetwork network; + std::shared_ptr<mock_NeuronLinear> n1(new mock_NeuronLinear); + network.add_neuron(n1, + BIAS_TYPE::NO_BIAS); std::vector<size_t> outputs; outputs.push_back(0); - BOOST_CHECK_EQUAL(0, network.get_n_outputs()); + BOOST_CHECK_EQUAL(0, + network.get_n_outputs()); network.specify_output_neurons(outputs); - BOOST_CHECK_EQUAL(1, network.get_n_outputs()); + BOOST_CHECK_EQUAL(1, + network.get_n_outputs()); } BOOST_AUTO_TEST_CASE(NeuralNetwork_eval_single_test) { - Neuron *n1 = new NeuronLinear(); - Neuron *n2 = new NeuronLinear(); + std::shared_ptr<mock_NeuronLinear> n1(new mock_NeuronLinear()); + std::shared_ptr<mock_NeuronLinear> n2(new mock_NeuronLinear()); + + mock_NeuronLinear n3 = *n1; + mock_NeuronLinear n4 = *n2; + MOCK_EXPECT(n3.activate).returns(5); + MOCK_EXPECT(n4.activate).returns(5); + NeuralNetwork network; network.add_neuron(n1); network.add_neuron(n2); - network.add_connection_simple(0, 1, SIMPLE_CONNECTION_TYPE::UNITARY_WEIGHT, 2.5); + network.add_connection_simple(0, + 1, + SIMPLE_CONNECTION_TYPE::UNITARY_WEIGHT, + 2.5); std::vector<size_t> output_neuron_indices; output_neuron_indices.push_back(1); @@ -146,30 +189,37 @@ BOOST_AUTO_TEST_SUITE(NeuralNetwork_test) std::vector<double> output; output.push_back(8); - network.eval_single(input, output); - BOOST_CHECK_EQUAL(5, output.at(0)); + network.eval_single(input, + output); + BOOST_CHECK_EQUAL(5, + output.at(0)); } BOOST_AUTO_TEST_CASE(NeuralNetwork_randomize_weights_test) { - Neuron *n1 = new NeuronLinear(); - Neuron *n2 = new NeuronLinear(); + std::shared_ptr<mock_NeuronLinear> n1(new mock_NeuronLinear()); + std::shared_ptr<mock_NeuronLinear> n2(new mock_NeuronLinear()); + NeuralNetwork network; - network.add_neuron(n1, BIAS_TYPE::NO_BIAS); - network.add_neuron(n2, BIAS_TYPE::NO_BIAS); + network.add_neuron(n1, + BIAS_TYPE::NO_BIAS); + network.add_neuron(n2, + BIAS_TYPE::NO_BIAS); for (int i = 0; i < 100; i++) { - network.add_connection_simple(0, 1, SIMPLE_CONNECTION_TYPE::NEXT_WEIGHT); + network.add_connection_simple(0, + 1, + SIMPLE_CONNECTION_TYPE::NEXT_WEIGHT); } network.randomize_weights(); - std::vector<double> *weights = network.get_parameter_ptr_weights(); + std::vector<double>* weights = network.get_parameter_ptr_weights(); - double sum=0; + double sum = 0; for (int i = 0; i < 100; i++) { sum += weights->at(i); } - sum=sum/100; - BOOST_CHECK(sum<0.15 && sum>-0.15); + sum = sum / 100; + BOOST_CHECK(sum < 0.15 && sum > -0.15); } -BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/tests/NeuronBinary_test.cpp b/src/tests/NeuronBinary_test.cpp index eb6eb5ea9d4e764a871d7e2f494caf64109867ca..57d6fc8b7f8c27c3b8fe17f929248bd05f35f5f6 100644 --- a/src/tests/NeuronBinary_test.cpp +++ b/src/tests/NeuronBinary_test.cpp @@ -7,34 +7,47 @@ #define BOOST_TEST_MODULE neuronBinary_test +// TODO fix boost_test_lib and remove the following include! +#ifdef _WINDOWS +#include <boost/test/included/unit_test.hpp> +#endif + #include "boost_unit_tests_preamble.h" #include "../Neuron/NeuronBinary.h" +using namespace lib4neuro; + /** * Boost testing suite for testing NeuronBinary.h * doesn't test inherited methods */ BOOST_AUTO_TEST_SUITE(neuronBinary_test) - /** - * Test of creating new instance of NeuronBinary - */ +/** + * Test of creating new instance of NeuronBinary + */ BOOST_AUTO_TEST_CASE(neuronBinary_construction_test) { - BOOST_CHECK_NO_THROW(NeuronBinary *neuron = new NeuronBinary()); + BOOST_CHECK_NO_THROW(NeuronBinary* neuron = new NeuronBinary()); } - /** - * Test of activate method - */ +/** + * Test of activate method + */ BOOST_AUTO_TEST_CASE(neuronBinary_activate_test) { - NeuronBinary *neuron = new NeuronBinary(); + NeuronBinary* neuron = new NeuronBinary(); //Test of correct state neuron - BOOST_CHECK_EQUAL(0.0, neuron->activate(2.0, 3.0)); - BOOST_CHECK_EQUAL(1.0, neuron->activate(3.0, 3.0)); - BOOST_CHECK_EQUAL(1.0, neuron->activate(3.0, 2.0)); + BOOST_CHECK_EQUAL(0.0, + neuron->activate(2.0, + 3.0)); + BOOST_CHECK_EQUAL(1.0, + neuron->activate(3.0, + 3.0)); + BOOST_CHECK_EQUAL(1.0, + neuron->activate(3.0, + 2.0)); } diff --git a/src/tests/NeuronConstant_test.cpp b/src/tests/NeuronConstant_test.cpp index 73fee92a4815ac1eadbe5c17e46b8314f64652d3..39d4d562058b447e5ca545dfd2e03964f1aecb6e 100644 --- a/src/tests/NeuronConstant_test.cpp +++ b/src/tests/NeuronConstant_test.cpp @@ -7,47 +7,67 @@ #define BOOST_TEST_MODULE neuronConstant_test +// TODO fix boost_test_lib and remove the following include! +#ifdef _WINDOWS +#include <boost/test/included/unit_test.hpp> +#endif + +// TODO fix boost_test_lib and remove the following include! +#ifdef _WINDOWS +#include <boost/test/included/unit_test.hpp> +#endif + #include "boost_unit_tests_preamble.h" #include "../Neuron/NeuronConstant.h" +using namespace lib4neuro; + /** * Boost testing suite for testing NeuronConstant.h * doesn't test inherited methods */ BOOST_AUTO_TEST_SUITE(neuronConstant_test) - /** - * Test of creating new instance of NeuronConstant - */ +/** + * Test of creating new instance of NeuronConstant + */ BOOST_AUTO_TEST_CASE(neuronConstant_construction_test) { - BOOST_CHECK_NO_THROW(NeuronConstant *neuron = new NeuronConstant(2.0)); - BOOST_CHECK_NO_THROW(NeuronConstant *neuron = new NeuronConstant()); + BOOST_CHECK_NO_THROW(NeuronConstant* neuron = new NeuronConstant(2.0)); + BOOST_CHECK_NO_THROW(NeuronConstant* neuron = new NeuronConstant()); } - /** - * Test of activate method - */ +/** + * Test of activate method + */ BOOST_AUTO_TEST_CASE(neuronConstant_activate__test) { - NeuronConstant *neuron = new NeuronConstant(2.0); + NeuronConstant* neuron = new NeuronConstant(2.0); //Test of correct state after activate neuron - BOOST_CHECK_EQUAL(2.0, neuron->activate(8.0, 7.0)); - - NeuronConstant *neuron2 = new NeuronConstant(); + BOOST_CHECK_EQUAL(2.0, + neuron->activate(8.0, + 7.0)); + + NeuronConstant* neuron2 = new NeuronConstant(); //Test of correct state after activate neuron - BOOST_CHECK_EQUAL(0.0, neuron2->activate(8.0, 7.0)); + BOOST_CHECK_EQUAL(0.0, + neuron2->activate(8.0, + 7.0)); } - /** - * Test of derivative methods - */ +/** + * Test of derivative methods + */ BOOST_AUTO_TEST_CASE(neuronConstant_derivative_test) { - NeuronConstant *neuron = new NeuronConstant(2.0); + NeuronConstant* neuron = new NeuronConstant(2.0); //Test of correct output of activation_function_get_derivative method - BOOST_CHECK_EQUAL(0.0, neuron->activation_function_eval_derivative(3.0, 2.0)); - BOOST_CHECK_EQUAL(0.0, neuron->activation_function_eval_derivative_bias(3.0, 2.0)); + BOOST_CHECK_EQUAL(0.0, + neuron->activation_function_eval_derivative(3.0, + 2.0)); + BOOST_CHECK_EQUAL(0.0, + neuron->activation_function_eval_derivative_bias(3.0, + 2.0)); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/tests/NeuronLinear_test.cpp b/src/tests/NeuronLinear_test.cpp index 5a86546704a0871cef0a4c673a007bb88776f8ef..c4db104592fd5190810b899f6151a48ef97e8515 100644 --- a/src/tests/NeuronLinear_test.cpp +++ b/src/tests/NeuronLinear_test.cpp @@ -7,41 +7,54 @@ #define BOOST_TEST_MODULE neuronLinear_test +// TODO fix boost_test_lib and remove the following include! +#ifdef _WINDOWS +#include <boost/test/included/unit_test.hpp> +#endif + #include "boost_unit_tests_preamble.h" #include "../Neuron/NeuronLinear.h" +using namespace lib4neuro; + /** * Boost testing suite for testing NeuronLinear.h * doesn't test inherited methods */ BOOST_AUTO_TEST_SUITE(neuronLinear_test) - /** - * Test of creating new instance of NeuronLinear - */ +/** + * Test of creating new instance of NeuronLinear + */ BOOST_AUTO_TEST_CASE(neuronLinear_construction_test) { - BOOST_CHECK_NO_THROW(NeuronLinear *neuron = new NeuronLinear()); + BOOST_CHECK_NO_THROW(NeuronLinear* neuron = new NeuronLinear()); } - /** - * Test of activate method - */ +/** + * Test of activate method + */ BOOST_AUTO_TEST_CASE(neuronLinear_activate_test) { - NeuronLinear *neuron = new NeuronLinear(); + NeuronLinear* neuron = new NeuronLinear(); //Test of correct state after activate neuron - BOOST_CHECK_EQUAL(5.0, neuron->activate(3.0, 2.0)); + BOOST_CHECK_EQUAL(5.0, + neuron->activate(3.0, + 2.0)); } - /** - * Test of derivative methods - */ +/** + * Test of derivative methods + */ BOOST_AUTO_TEST_CASE(neuronLinear_derivative_test) { - NeuronLinear *neuron = new NeuronLinear(); + NeuronLinear* neuron = new NeuronLinear(); //Test of correct output of activation_function_get_derivative method - BOOST_CHECK_EQUAL(1.0, neuron->activation_function_eval_derivative(3.0, 2.0)); - BOOST_CHECK_EQUAL(1.0, neuron->activation_function_eval_derivative_bias(3.0, 2.0)); + BOOST_CHECK_EQUAL(1.0, + neuron->activation_function_eval_derivative(3.0, + 2.0)); + BOOST_CHECK_EQUAL(1.0, + neuron->activation_function_eval_derivative_bias(3.0, + 2.0)); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/tests/NeuronLogistic_test.cpp b/src/tests/NeuronLogistic_test.cpp index 25440d97e61407784b80bcfe7b823d937b66135b..85c007448fa1245666a54f69ce5450cfb391d6ee 100644 --- a/src/tests/NeuronLogistic_test.cpp +++ b/src/tests/NeuronLogistic_test.cpp @@ -7,94 +7,128 @@ #define BOOST_TEST_MODULE neuronLogistic_test +// TODO fix boost_test_lib and remove the following include! +#ifdef _WINDOWS +#include <boost/test/included/unit_test.hpp> +#endif + #include "boost_unit_tests_preamble.h" #include "../Neuron/NeuronLogistic.h" +using namespace lib4neuro; + /** * Boost testing suite for testing NeuronLogistic.h * doesn't test inherited methods */ BOOST_AUTO_TEST_SUITE(neuronLogistic_test) - /** - * Test of creating new instance of NeuronLogistic - */ +/** + * Test of creating new instance of NeuronLogistic + */ BOOST_AUTO_TEST_CASE(neuronLogistic_construction__test) { - BOOST_CHECK_NO_THROW(NeuronLogistic *neuron = new NeuronLogistic()); + BOOST_CHECK_NO_THROW(NeuronLogistic* neuron = new NeuronLogistic()); } - /** - * Test of activate method - */ +/** + * Test of activate method + */ BOOST_AUTO_TEST_CASE(neuronLogistic_activate__test) { - NeuronLogistic *neuron = new NeuronLogistic(); + NeuronLogistic* neuron = new NeuronLogistic(); //Test of correct state after activate neuron - BOOST_CHECK_CLOSE(0.73105857863, neuron->activate(3.0,2.0), 0.00001); + BOOST_CHECK_CLOSE(0.73105857863, + neuron->activate(3.0, + 2.0), + 0.00001); } - /** - * Test of derivative methods - */ +/** + * Test of derivative methods + */ BOOST_AUTO_TEST_CASE(neuronLogistic_derivative_test) { - NeuronLogistic *neuron = new NeuronLogistic(); + NeuronLogistic* neuron = new NeuronLogistic(); //3.0 2.0 //Test of correct output of activation_function_get_derivative method - BOOST_CHECK_CLOSE(0.196611933241, neuron->activation_function_eval_derivative(3.0,2.0), 0.00001); - BOOST_CHECK_CLOSE(-0.196611933241, neuron->activation_function_eval_derivative_bias(3.0,2.0), 0.00001); + BOOST_CHECK_CLOSE(0.196611933241, + neuron->activation_function_eval_derivative(3.0, + 2.0), + 0.00001); + BOOST_CHECK_CLOSE(-0.196611933241, + neuron->activation_function_eval_derivative_bias(3.0, + 2.0), + 0.00001); } BOOST_AUTO_TEST_CASE(neuronLogistic_d1_construction__test) { - BOOST_CHECK_NO_THROW(NeuronLogistic_d1 *neuron = new NeuronLogistic_d1()); + BOOST_CHECK_NO_THROW(NeuronLogistic_d1* neuron = new NeuronLogistic_d1()); } - /** - * Test of activate method - */ +/** + * Test of activate method + */ BOOST_AUTO_TEST_CASE(neuronLogistic_d1_activate__test) { - NeuronLogistic_d1 *neuron = new NeuronLogistic_d1(); + NeuronLogistic_d1* neuron = new NeuronLogistic_d1(); //Test of correct state after activate neuron - BOOST_CHECK_CLOSE(0.196611933241, neuron->activate(3.0,2.0), 0.00001); + BOOST_CHECK_CLOSE(0.196611933241, + neuron->activate(3.0, + 2.0), + 0.00001); } - /** - * Test of derivative methods - */ +/** + * Test of derivative methods + */ BOOST_AUTO_TEST_CASE(neuronLogistic_d1_derivative_test) { - NeuronLogistic_d1 *neuron = new NeuronLogistic_d1(); + NeuronLogistic_d1* neuron = new NeuronLogistic_d1(); //3.0 2.0 //Test of correct output of activation_function_get_derivative method - BOOST_CHECK_CLOSE(-0.0908577476729, neuron->activation_function_eval_derivative(3.0,2.0), 0.00001); - BOOST_CHECK_CLOSE(0.0908577476729, neuron->activation_function_eval_derivative_bias(3.0,2.0), 0.00001); + BOOST_CHECK_CLOSE(-0.0908577476729, + neuron->activation_function_eval_derivative(3.0, + 2.0), + 0.00001); + BOOST_CHECK_CLOSE(0.0908577476729, + neuron->activation_function_eval_derivative_bias(3.0, + 2.0), + 0.00001); } BOOST_AUTO_TEST_CASE(neuronLogistic_d2_construction__test) { - BOOST_CHECK_NO_THROW(NeuronLogistic_d2 *neuron = new NeuronLogistic_d2()); + BOOST_CHECK_NO_THROW(NeuronLogistic_d2* neuron = new NeuronLogistic_d2()); } - /** - * Test of activate method - */ +/** + * Test of activate method + */ BOOST_AUTO_TEST_CASE(neuronLogistic_d2_activate__test) { - NeuronLogistic_d2 *neuron = new NeuronLogistic_d2(); + NeuronLogistic_d2* neuron = new NeuronLogistic_d2(); //Test of correct state after activate neuron - BOOST_CHECK_CLOSE(-0.0908577476729, neuron->activate(3.0,2.0), 0.00001); + BOOST_CHECK_CLOSE(-0.0908577476729, + neuron->activate(3.0, + 2.0), + 0.00001); } - /** - * Test of derivative methods - */ +/** + * Test of derivative methods + */ BOOST_AUTO_TEST_CASE(neuronLogistic_d2_derivative_test) { - NeuronLogistic_d2 *neuron = new NeuronLogistic_d2(); + NeuronLogistic_d2* neuron = new NeuronLogistic_d2(); //3.0 2.0 //Test of correct output of activation_function_get_derivative method - BOOST_CHECK_CLOSE(-0.03532558051623, neuron->activation_function_eval_derivative(3.0,2.0), 0.00001); - BOOST_CHECK_CLOSE(0.03532558051623, neuron->activation_function_eval_derivative_bias(3.0,2.0), 0.00001); + BOOST_CHECK_CLOSE(-0.03532558051623, + neuron->activation_function_eval_derivative(3.0, + 2.0), + 0.00001); + BOOST_CHECK_CLOSE(0.03532558051623, + neuron->activation_function_eval_derivative_bias(3.0, + 2.0), + 0.00001); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/tests/ParticleSwarm_test.cpp b/src/tests/ParticleSwarm_test.cpp index bfa44ecb2a093ef69ed6408bf31f42a266445fc6..db758f8e2e7c94e9f10d39e5b9094837230e8842 100644 --- a/src/tests/ParticleSwarm_test.cpp +++ b/src/tests/ParticleSwarm_test.cpp @@ -7,67 +7,79 @@ #define BOOST_TEST_MODULE ParticleSwarm_test -#include "boost_unit_tests_preamble.h" +// TODO fix boost_test_lib and remove the following include! +#ifdef _WINDOWS +#include <boost/test/included/unit_test.hpp> +#endif +#include "boost_unit_tests_preamble.h" +#include "../ErrorFunction/ErrorFunctionsMock.h" #include "../LearningMethods/ParticleSwarm.h" + +using namespace lib4neuro; + /** * Boost testing suite for testing ParticleSwarm.h */ -double test_particle_swarm_neural_net_error_function(double *weights){ - -return 0; - } +double test_particle_swarm_neural_net_error_function(double* weights) { + return 0; +} BOOST_AUTO_TEST_SUITE(ParticleSwarm_test) - BOOST_AUTO_TEST_CASE(ParticleSwarm_construction_test){ + BOOST_AUTO_TEST_CASE(ParticleSwarm_construction_test) { std::vector<double> domain_bound; domain_bound.push_back(5); - NeuralNetwork network; -// std::vector<std::pair<std::vector<double>, std::vector<double>>> data_vec; -// std::vector<double> inp, out; -// -// for (int i = 0; i < 3; i++) { -// inp.push_back(i); -// out.push_back(i + 4); -// } -// -// data_vec.emplace_back(std::make_pair(inp, out)); -// -// DataSet dataSet(&data_vec); -// ErrorFunction *error = new MSE(&network, &dataSet); - - BOOST_CHECK_NO_THROW(ParticleSwarm swarm(&domain_bound, 0, 1, 1, 0.5, 0.05, 0.5, 0, 20)); + + BOOST_CHECK_NO_THROW(ParticleSwarm swarm(&domain_bound, + 0, + 1, + 1, + 0.5, + 0.05, + 0.5, + 0, + 20)); } - BOOST_AUTO_TEST_CASE(ParticleSwarm_optimalize_test){ + BOOST_AUTO_TEST_CASE(ParticleSwarm_optimalize_and_get_parameters_test) { std::vector<double> domain_bound; + domain_bound.push_back(-5); + domain_bound.push_back(5); + domain_bound.push_back(-5); + domain_bound.push_back(5); + domain_bound.push_back(-5); + domain_bound.push_back(5); + domain_bound.push_back(-5); + domain_bound.push_back(5); + domain_bound.push_back(-5); domain_bound.push_back(5); - NeuralNetwork network; - std::vector<std::pair<std::vector<double>, std::vector<double>>> data_vec; - std::vector<double> inp, out; - for (int i = 0; i < 3; i++) { - inp.push_back(i); - out.push_back(i + 4); - } - data_vec.emplace_back(std::make_pair(inp, out)); + mock_ErrorFunction error; + + MOCK_EXPECT(error.get_dimension).returns(5); + MOCK_EXPECT(error.eval).returns(0.8); - DataSet dataSet(&data_vec); - ErrorFunction *error = new MSE(&network, &dataSet); + ParticleSwarm swarm(&domain_bound, + 0, + 1, + 1, + 1, + 1, + 1, + 5, + 20); - ParticleSwarm swarm(&domain_bound, 0, 1, 1, -1,1,1, 0, 20); - BOOST_CHECK_THROW(swarm.optimize( *error ), std::invalid_argument) ; + BOOST_CHECK_NO_THROW(swarm.optimize(error)); - ParticleSwarm swarm2(&domain_bound, 0, 1, 1, 1,-1,1, 0, 20); - BOOST_CHECK_THROW(swarm2.optimize( *error ), std::invalid_argument) ; - ParticleSwarm swarm3(&domain_bound, 0, 1, 1, 1,1,-1, 0, 20); - BOOST_CHECK_THROW(swarm3.optimize( *error ), std::invalid_argument) ; + for (int i = 0; i < swarm.get_parameters()->size(); i++) { + BOOST_CHECK_NO_THROW(swarm.get_parameters()->at(i)); + } } -BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/tests/Particle_test.cpp b/src/tests/Particle_test.cpp index 5329acd1f809842194d15a71a3d405cf23781e92..084706a6a4e09e7033b7efb7c8247c32f84b0073 100644 --- a/src/tests/Particle_test.cpp +++ b/src/tests/Particle_test.cpp @@ -7,162 +7,60 @@ #define BOOST_TEST_MODULE Particle_test -#include "boost_unit_tests_preamble.h" +// TODO fix boost_test_lib and remove the following include! +#ifdef _WINDOWS +#include <boost/test/included/unit_test.hpp> +#endif +#include "boost_unit_tests_preamble.h" +#include "../ErrorFunction/ErrorFunctionsMock.h" #include "../LearningMethods/ParticleSwarm.h" + +using namespace lib4neuro; + /** * Boost testing suite for testing ParticleSwarm.h * TODO */ - BOOST_AUTO_TEST_SUITE(Particle_test) +BOOST_AUTO_TEST_SUITE(Particle_test) BOOST_AUTO_TEST_CASE(Particle_construction_test) { - std::vector<double> domain_bound = {1, 2, 3, 4, 5}; - Neuron *n1 = new NeuronLinear(); - Neuron *n2 = new NeuronLinear(); - NeuralNetwork network; - std::vector<std::pair<std::vector<double>, std::vector<double>>> data_vec; - std::vector<double> inp, out; - - for (int i = 0; i < 1; i++) { - inp.push_back(i); - out.push_back(i + 4); - } - - data_vec.emplace_back(std::make_pair(inp, out)); - network.add_neuron(n1); - network.add_neuron(n2); - network.add_connection_simple(0, 1, SIMPLE_CONNECTION_TYPE::UNITARY_WEIGHT, 2.5); - network.randomize_weights(); - - std::vector<size_t> net_input_neurons_indices(1); - std::vector<size_t> net_output_neurons_indices(1); - net_input_neurons_indices[0] = 0; - - net_output_neurons_indices[0] = 1; - - network.specify_input_neurons(net_input_neurons_indices); - network.specify_output_neurons(net_output_neurons_indices); - - DataSet dataSet(&data_vec); - ErrorFunction *error = new MSE(&network, &dataSet); - Particle particle(error, &domain_bound); - BOOST_CHECK_NO_THROW(Particle particle(error, &domain_bound)); - // particle.get_coordinate(); + std::vector<double> domain_bound{1, 2, 3, 4, 5}; + mock_ErrorFunction error; + MOCK_EXPECT(error.get_dimension).once().returns(5); + MOCK_EXPECT(error.eval).once().returns(0.8); + BOOST_CHECK_NO_THROW(Particle(&error, + &domain_bound)); } BOOST_AUTO_TEST_CASE(Particle_get_coordinate_test) { - std::vector<double> domain_bound = {1, 2, 3, 4, 5}; - Neuron *n1 = new NeuronLinear(); - Neuron *n2 = new NeuronLinear(); - NeuralNetwork network; - std::vector<std::pair<std::vector<double>, std::vector<double>>> data_vec; - std::vector<double> inp, out; - - for (int i = 0; i < 1; i++) { - inp.push_back(i); - out.push_back(i + 4); - } - - data_vec.emplace_back(std::make_pair(inp, out)); - network.add_neuron(n1); - network.add_neuron(n2); - network.add_connection_simple(0, 1, SIMPLE_CONNECTION_TYPE::UNITARY_WEIGHT, 2.5); - network.randomize_weights(); - - std::vector<size_t> net_input_neurons_indices(1); - std::vector<size_t> net_output_neurons_indices(1); - net_input_neurons_indices[0] = 0; - - net_output_neurons_indices[0] = 1; - - network.specify_input_neurons(net_input_neurons_indices); - network.specify_output_neurons(net_output_neurons_indices); - - DataSet dataSet(&data_vec); - ErrorFunction *error = new MSE(&network, &dataSet); - Particle particle1( error, &domain_bound ); - Particle particle2( error, &domain_bound ); - - BOOST_CHECK(*particle1.get_coordinate() != *particle2.get_coordinate()); - } - - //Random - //TODO - /* - BOOST_AUTO_TEST_CASE(particle_change_coordiante_test) { - double domain_bound[5] = {1,2,3,4,5}; - Neuron *n1 = new NeuronLinear(1, 1); - Neuron *n2 = new NeuronLinear(2, 2); - NeuralNetwork network; - std::vector<std::pair<std::vector<double>, std::vector<double>>> data_vec; - std::vector<double> inp, out; - - for (int i = 0; i < 1; i++) { - inp.push_back(i); - out.push_back(i + 4); - } - - data_vec.emplace_back(std::make_pair(inp, out)); - network.add_neuron(n1); - network.add_neuron(n2); - network.add_connection_simple(0, 1, 0, 2.5); - network.randomize_weights(); - - std::vector<size_t> net_input_neurons_indices(1); - std::vector<size_t> net_output_neurons_indices(1); - net_input_neurons_indices[0] = 0; + std::vector<double> domain_bound{1, 2, 3, 4, 5}; + mock_ErrorFunction error; - net_output_neurons_indices[0] = 1; + MOCK_EXPECT(error.get_dimension).returns(5); + MOCK_EXPECT(error.eval).returns(0.8); - network.specify_input_neurons(net_input_neurons_indices); - network.specify_output_neurons(net_output_neurons_indices); + Particle particle1(&error, + &domain_bound); + Particle particle2(&error, + &domain_bound); + BOOST_CHECK(*particle1.get_coordinate() != *particle2.get_coordinate()); + } + BOOST_AUTO_TEST_CASE(Particle_get_optimal_value_test) { + std::vector<double> domain_bound{1, 2, 3, 4, 5}; + mock_ErrorFunction error; - DataSet dataSet(&data_vec); - ErrorFunction *error = new MSE(&network, &dataSet); - Particle particle(error, &domain_bound[0]); - particle.change_coordinate(1.0, 2.0, 2.0, &domain_bound[1], 1); - - - BOOST_CHECK_EQUAL(1.32664, *particle.get_coordinate()); - } - - BOOST_AUTO_TEST_CASE(particle_optimal_value_test){ - double domain_bound[5] = {1,2,3,4,5}; - Neuron *n1 = new NeuronLinear(1, 1); - Neuron *n2 = new NeuronLinear(2, 2); - NeuralNetwork network; - std::vector<std::pair<std::vector<double>, std::vector<double>>> data_vec; - std::vector<double> inp, out; - - for (int i = 0; i < 1; i++) { - inp.push_back(i); - out.push_back(i + 4); - } - - data_vec.emplace_back(std::make_pair(inp, out)); - network.add_neuron(n1); - network.add_neuron(n2); - network.add_connection_simple(0, 1, 0, 2.5); - network.randomize_weights(); - - std::vector<size_t> net_input_neurons_indices(1); - std::vector<size_t> net_output_neurons_indices(1); - net_input_neurons_indices[0] = 0; - - net_output_neurons_indices[0] = 1; + MOCK_EXPECT(error.get_dimension).returns(5); + MOCK_EXPECT(error.eval).returns(0.8); - network.specify_input_neurons(net_input_neurons_indices); - network.specify_output_neurons(net_output_neurons_indices); - DataSet dataSet(&data_vec); - ErrorFunction *error = new MSE(&network, &dataSet); - Particle particle(error, &domain_bound[0]); - BOOST_CHECK_CLOSE(1.789708839, particle.get_optimal_value(), 0.00001 ); - } - */ + Particle particle1(&error, + &domain_bound); + BOOST_CHECK_EQUAL(0.8, + particle1.get_optimal_value()); + } -BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/tests/boost_unit_tests_preamble.h b/src/tests/boost_unit_tests_preamble.h index a7656f83ac5d1e584c12b65dbd4af6e612ad4948..417ac6e5751380b2a51d0758d4b757a33502bacc 100644 --- a/src/tests/boost_unit_tests_preamble.h +++ b/src/tests/boost_unit_tests_preamble.h @@ -1,6 +1,3 @@ - - - #ifndef BOOST_TEST_DYN_LINK #define BOOST_TEST_DYN_LINK #endif