diff --git a/README.md b/README.md index d8053a97a..2248a3a3f 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,10 @@ The available optimizers for training neural networks are 1. Stochastic gradient descent 2. Adam (recommended) +Prerequisite +------------ +Building Inference-Engine requires a Fortran 2018 compiler. With `gfortran`, the required minimum compiler version is 13. + Downloading, Building and Testing --------------------------------- To download, build, and test Inference-Engine, enter the following commands in a Linux, macOS, or Windows Subsystem for Linux shell: diff --git a/app/train-cloud-microphysics.f90 b/cloud-microphysics/app/train-cloud-microphysics.f90 similarity index 99% rename from app/train-cloud-microphysics.f90 rename to cloud-microphysics/app/train-cloud-microphysics.f90 index 813ab6af6..89744f317 100644 --- a/app/train-cloud-microphysics.f90 +++ b/cloud-microphysics/app/train-cloud-microphysics.f90 @@ -17,8 +17,9 @@ program train_cloud_microphysics !! Internal dependencies; use inference_engine_m, only : & - inference_engine_t, mini_batch_t, input_output_pair_t, tensor_t, trainable_engine_t, rkind, NetCDF_file_t, & + inference_engine_t, mini_batch_t, input_output_pair_t, tensor_t, trainable_engine_t, rkind, & training_configuration_t, shuffle + use NetCDF_file_m, only: NetCDF_file_t use ubounds_m, only : ubounds_t implicit none diff --git a/cloud-microphysics/fpm.toml b/cloud-microphysics/fpm.toml new file mode 100644 index 000000000..8c7636251 --- /dev/null +++ b/cloud-microphysics/fpm.toml @@ -0,0 +1,11 @@ +name = "icar-trainer" +version = "0.0.0" +license = "license" +author = "Damian Rouson, Tan Nguyen, Jordan Welsman, David Torres, Brad Richardson, Katherine Rasmussen" +maintainer = "rouson@lbl.gov" + +[dependencies] +assert = {git = "https://github.com/sourceryinstitute/assert", tag = "1.5.0"} +sourcery = {git = "https://github.com/sourceryinstitute/sourcery", tag = "4.5.0"} +inference-engine = {path = "../"} +netcdf-interfaces = {git = "https://github.com/LKedward/netcdf-interfaces.git", rev = "d2bbb71ac52b4e346b62572b1ca1620134481096"} diff --git a/cloud-microphysics/setup.sh b/cloud-microphysics/setup.sh new file mode 100755 index 000000000..21a19a3b2 --- /dev/null +++ b/cloud-microphysics/setup.sh @@ -0,0 +1,153 @@ +#!/bin/sh + +set -e # exit on error + +usage() +{ + echo "Inference Engine Setup Script" + echo "" + echo "USAGE:" + echo "./setup.sh [--help|-h] | [-p|--prefix=PREFIX]" + echo "" + echo " --help Display this help text" + echo " --prefix=PREFIX Install binary in 'PREFIX/bin'" + echo " Default prefix='\$HOME/.local/bin'" + echo "" +} + +PREFIX="$HOME/.local" + +while [ "$1" != "" ]; do + PARAM=$(echo "$1" | awk -F= '{print $1}') + VALUE=$(echo "$1" | awk -F= '{print $2}') + case $PARAM in + -h | --help) + usage + exit + ;; + -p | --prefix) + PREFIX=$VALUE + ;; + *) + echo "ERROR: unknown parameter \"$PARAM\"" + usage + exit 1 + ;; + esac + shift +done + +set -u # error on use of undefined variable + +if ! command -v brew > /dev/null ; then + if ! command -v curl > /dev/null ; then + echo "Please install curl and then rerun ./setup.sh" + exit 1 + fi + /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" + if [ $(uname) = "Linux" ]; then + if [ -z "$PATH" ]; then + PATH=/home/linuxbrew/.linuxbrew/bin/ + else + PATH=/home/linuxbrew/.linuxbrew/bin/:"$PATH" + fi + fi +fi + +brew tap fortran-lang/fortran # required for building fpm +brew install fortran-lang/fortran/fpm netcdf netcdf-fortran pkg-config coreutils # coreutils supports `realpath` below + +PREFIX=`realpath $PREFIX` + +NETCDF_LIB_PATH="`brew --prefix netcdf`/lib" +HDF5_LIB_PATH="`brew --prefix hdf5`/lib" +NETCDFF_LIB_PATH="`brew --prefix netcdf-fortran`/lib" + +FPM_LD_FLAG=" -L$NETCDF_LIB_PATH -L$HDF5_LIB_PATH -L$NETCDFF_LIB_PATH" +FPM_FLAG="-fcoarray=single -O3 -fallow-argument-mismatch -ffree-line-length-none -L$NETCDF_LIB_PATH -L$HDF5_LIB_PATH" +FPM_FC=${FC:-"gfortran-13"} +FPM_CC=${CC:-"gcc-13"} + +mkdir -p build + +CI=${CI:-"false"} # GitHub Actions workflows set CI=true + +if [ $CI = true ]; then + PKG_CONFIG_PATH=`realpath ./build/pkgconfig` + echo "---------------" + echo "PKG_CONFIG_PATH=$PKG_CONFIG_PATH" + echo "---------------" +else + PKG_CONFIG_PATH=`realpath "$PREFIX"/lib/pkgconfig` +fi + +if [ ! -d $PKG_CONFIG_PATH ]; then + mkdir -p $PKG_CONFIG_PATH +fi + +INFERENCE_ENGINE_PC="$PKG_CONFIG_PATH/inference-engine.pc" +echo "INFERENCE_ENGINE_FPM_CC=\"$FPM_CC\"" > $INFERENCE_ENGINE_PC +echo "INFERENCE_ENGINE_FPM_FC=\"$FPM_FC\"" >> $INFERENCE_ENGINE_PC +echo "INFERENCE_ENGINE_FPM_LD_FLAG=\"$FPM_LD_FLAG\"" >> $INFERENCE_ENGINE_PC +echo "INFERENCE_ENGINE_FPM_FLAG=\"$FPM_FLAG\"" >> $INFERENCE_ENGINE_PC +echo "Name: inference-engine" >> $INFERENCE_ENGINE_PC +echo "Description: Inference Engine" >> $INFERENCE_ENGINE_PC +echo "URL: https://github.com/berkeleylab/inference-engine" >> $INFERENCE_ENGINE_PC +echo "Version: 0.1.2" >> $INFERENCE_ENGINE_PC +if [ $CI = true ]; then + echo "---------------" + echo "cat $INFERENCE_ENGINE_PC" + cat $INFERENCE_ENGINE_PC + echo "---------------" +fi + +export PKG_CONFIG_PATH +touch build/run-fpm.sh +RUN_FPM_SH="`realpath ./build/run-fpm.sh`" +echo "#!/bin/sh" >> $RUN_FPM_SH +echo "#-- DO NOT EDIT -- created by inference-engine/setup.sh" >> $RUN_FPM_SH +echo "export PKG_CONFIG_PATH" >> $RUN_FPM_SH +echo "" >> $RUN_FPM_SH +echo "fpm_arguments=\"\"" >> $RUN_FPM_SH +echo "program_arguments=\"\"" >> $RUN_FPM_SH +echo "while test \$# -gt 0" >> $RUN_FPM_SH +echo "do" >> $RUN_FPM_SH +echo " case \"\$1\" in" >> $RUN_FPM_SH +echo " --) program_arguments=\"\$@\"" >> $RUN_FPM_SH +echo " ;;" >> $RUN_FPM_SH +echo " *) if [ -z \"\$program_arguments\" ]; then" >> $RUN_FPM_SH +echo " fpm_arguments=\"\$fpm_arguments \$1\"" >> $RUN_FPM_SH +echo " fi" >> $RUN_FPM_SH +echo " ;;" >> $RUN_FPM_SH +echo " esac" >> $RUN_FPM_SH +echo " shift" >> $RUN_FPM_SH +echo "done" >> $RUN_FPM_SH +echo "" >> $RUN_FPM_SH +echo "`which fpm` \$fpm_arguments \\" >> $RUN_FPM_SH +echo "--profile release \\" >> $RUN_FPM_SH +echo "--c-compiler \"`pkg-config inference-engine --variable=INFERENCE_ENGINE_FPM_CC`\" \\" >> $RUN_FPM_SH +echo "--compiler \"`pkg-config inference-engine --variable=INFERENCE_ENGINE_FPM_FC`\" \\" >> $RUN_FPM_SH +echo "--flag \"-cpp `pkg-config inference-engine --variable=INFERENCE_ENGINE_FPM_FLAG`\" \\" >> $RUN_FPM_SH +echo "--link-flag \"`pkg-config inference-engine --variable=INFERENCE_ENGINE_FPM_LD_FLAG`\" \\" >> $RUN_FPM_SH +echo "\$program_arguments" >> $RUN_FPM_SH +chmod u+x $RUN_FPM_SH +if [ $CI = true ]; then + echo "---------------" + echo "cat $RUN_FPM_SH" + cat $RUN_FPM_SH + echo "---------------" +fi + +echo "$RUN_FPM_SH test" +$RUN_FPM_SH test + +echo "" +echo "____________________ cloud-microphysics has been set up! _______________________" +echo "" +echo "Usage:" +echo "" +echo "./build/run-fpm.sh run train-cloud-microphysics -- \ " +echo " --base --epochs \ " +echo " [--start ] [--end ] [--stride ]" +echo "" +echo "where angular brackets denote user-provided values and square brackets denote optional arguments" diff --git a/src/inference_engine/NetCDF_file_m.f90 b/cloud-microphysics/src/NetCDF_file_m.f90 similarity index 100% rename from src/inference_engine/NetCDF_file_m.f90 rename to cloud-microphysics/src/NetCDF_file_m.f90 diff --git a/src/inference_engine/NetCDF_file_s.f90 b/cloud-microphysics/src/NetCDF_file_s.f90 similarity index 100% rename from src/inference_engine/NetCDF_file_s.f90 rename to cloud-microphysics/src/NetCDF_file_s.f90 diff --git a/cloud-microphysics/test/main.f90 b/cloud-microphysics/test/main.f90 new file mode 100644 index 000000000..c0c5685e6 --- /dev/null +++ b/cloud-microphysics/test/main.f90 @@ -0,0 +1,25 @@ +! Copyright (c), The Regents of the University of California +! Terms of use are as specified in LICENSE.txt +program main + use netCDF_file_test_m, only : netCDF_file_test_t + implicit none + + real t_start, t_finish + + integer :: passes=0, tests=0 + + call cpu_time(t_start) + block + type(netCDF_file_test_t) netCDF_file_test + call netCDF_file_test%report(passes, tests) + end block + call cpu_time(t_finish) + + print * + print *,"Test suite execution time: ",t_finish - t_start + print * + print '(*(a,:,g0))',"_________ In total, ",passes," of ",tests, " tests pass. _________" + sync all + print * + if (passes/=tests) error stop "-------- One or more tests failed. See the above report. ---------" +end program diff --git a/test/netCDF_file_test_m.f90 b/cloud-microphysics/test/netCDF_file_test_m.f90 similarity index 100% rename from test/netCDF_file_test_m.f90 rename to cloud-microphysics/test/netCDF_file_test_m.f90 diff --git a/training_configuration.json b/cloud-microphysics/training_configuration.json similarity index 100% rename from training_configuration.json rename to cloud-microphysics/training_configuration.json diff --git a/example/learn-exponentiation.f90 b/example/learn-exponentiation.f90 index 2313d7455..404095038 100644 --- a/example/learn-exponentiation.f90 +++ b/example/learn-exponentiation.f90 @@ -120,7 +120,7 @@ pure function e(j,n) result(unit_vector) function perturbed_identity_network(perturbation_magnitude) result(trainable_engine) type(trainable_engine_t) trainable_engine real, intent(in) :: perturbation_magnitude - integer, parameter :: n(*) = [8, 64, 64, 64, 6] + integer, parameter :: n(*) = [8, 64, 64, 64, 6] ! nodes per layer (first layer = input, last layer = output) integer, parameter :: n_max = maxval(n), layers = size(n) integer j, k, l real, allocatable :: identity(:,:,:), w_harvest(:,:,:), b_harvest(:,:) @@ -144,4 +144,4 @@ function perturbed_identity_network(perturbation_magnitude) result(trainable_eng end associate end function -end program \ No newline at end of file +end program diff --git a/fpm.toml b/fpm.toml index 9cfa64e6e..9b38846f0 100644 --- a/fpm.toml +++ b/fpm.toml @@ -1,10 +1,9 @@ name = "inference-engine" version = "0.5.0" license = "license" -author = "Damian Rouson, Tan Nguyen, Jordan Welsman, David Torres, Brad Richardson" +author = "Damian Rouson, Tan Nguyen, Jordan Welsman, David Torres, Brad Richardson, Katherine Rasmussen" maintainer = "rouson@lbl.gov" [dependencies] assert = {git = "https://github.com/sourceryinstitute/assert", tag = "1.5.0"} sourcery = {git = "https://github.com/sourceryinstitute/sourcery", tag = "4.5.0"} -netcdf-interfaces = {git = "https://github.com/LKedward/netcdf-interfaces.git", rev = "d2bbb71ac52b4e346b62572b1ca1620134481096"} diff --git a/scripts/run-fpm.sh-header b/scripts/run-fpm.sh-header deleted file mode 100755 index bf8e478c5..000000000 --- a/scripts/run-fpm.sh-header +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/sh -#-- DO NOT EDIT -- created by inference-engine/setup.sh -export PKG_CONFIG_PATH - -fpm_arguments="" -program_arguments="" -while test $# -gt 0 -do - case "$1" in - --) program_arguments="$@" - ;; - *) if [ -z "$program_arguments" ]; then - fpm_arguments="$fpm_arguments $1" - fi - ;; - esac - shift -done - diff --git a/setup.sh b/setup.sh index 57d7a2338..e30b9bf30 100755 --- a/setup.sh +++ b/setup.sh @@ -7,10 +7,10 @@ usage() echo "Inference Engine Setup Script" echo "" echo "USAGE:" - echo "./setup.sh [--help|-h] | [-p|--prefix=PREFIX]" + echo "./setup.sh [--help|-h]" echo "" echo " --help Display this help text" - echo " --prefix=PREFIX Install binary in 'PREFIX/bin'" + echo " --prefix=PREFIX Install any binaries needed to build inference-engine in 'PREFIX/bin'" echo " Default prefix='\$HOME/.local/bin'" echo "" } @@ -39,107 +39,60 @@ done set -u # error on use of undefined variable -if ! command -v brew > /dev/null ; then +install_fpm_from_source() +{ + echo "Installing fpm in following location: $PREFIX/bin" + echo "Ensure $PREFIX/bin is in your path or rerun script with the --prefix=PREFIX flag" if ! command -v curl > /dev/null ; then + echo "This script uses curl to download source file for fpm to install it" echo "Please install curl and then rerun ./setup.sh" exit 1 fi - /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" - if [ $(uname) = "Linux" ]; then - if [ -z "$PATH" ]; then - PATH=/home/linuxbrew/.linuxbrew/bin/ - else - PATH=/home/linuxbrew/.linuxbrew/bin/:"$PATH" + mkdir temp-dir-to-build-fpm-for-inference-engine-installation + curl -L -o temp-dir-to-build-fpm-for-inference-engine-installation/fpm.F90 https://github.com/fortran-lang/fpm/releases/download/current/fpm.F90 + gfortran -o $PREFIX/bin/fpm -Jtemp-dir-to-build-fpm-for-inference-engine-installation temp-dir-to-build-fpm-for-inference-engine-installation/fpm.F90 + rm -rf temp-dir-to-build-fpm-for-inference-engine-installation + if command -v fpm > /dev/null ; then + echo "fpm installed" + else + echo "Some error has occured while trying to install fpm. Please install fpm, ensure it is in your path, and rerun script" + fi +} + +# if no fpm, install either through homebrew, or gfortran compiling fpm.F90 +if ! command -v fpm > /dev/null ; then + if ! command -v brew > /dev/null ; then + if ! command -v gfortran > /dev/null ; then + echo "Please install fpm, ensure it is in your path, and rerun script" + exit 1 + else # has gfortran, but not homebrew + install_fpm_from_source fi + else # has homebrew + brew tap fortran-lang/fortran + brew install fortran-lang/fortran/fpm fi fi - -brew tap fortran-lang/fortran # required for building fpm -brew install fpm netcdf netcdf-fortran pkg-config coreutils # coreutils supports `realpath` below - -PREFIX=`realpath $PREFIX` - -NETCDF_LIB_PATH="`brew --prefix netcdf`/lib" -HDF5_LIB_PATH="`brew --prefix hdf5`/lib" -NETCDFF_LIB_PATH="`brew --prefix netcdf-fortran`/lib" - -FPM_LD_FLAG=" -L$NETCDF_LIB_PATH -L$HDF5_LIB_PATH -L$NETCDFF_LIB_PATH" -FPM_FLAG="-fopenmp -g -fcoarray=single -O3 -fallow-argument-mismatch -ffree-line-length-none -L$NETCDF_LIB_PATH -L$HDF5_LIB_PATH" FPM_FC=${FC:-"gfortran-13"} FPM_CC=${CC:-"gcc-13"} mkdir -p build -CI=${CI:-"false"} # GitHub Actions workflows set CI=true - -if [ $CI = true ]; then - PKG_CONFIG_PATH=`realpath ./build/pkgconfig` - echo "---------------" - echo "PKG_CONFIG_PATH=$PKG_CONFIG_PATH" - echo "---------------" -else - PKG_CONFIG_PATH=`realpath "$PREFIX"/lib/pkgconfig` -fi - -if [ ! -d $PKG_CONFIG_PATH ]; then - mkdir -p $PKG_CONFIG_PATH -fi - -INFERENCE_ENGINE_PC="$PKG_CONFIG_PATH/inference-engine.pc" -echo "INFERENCE_ENGINE_FPM_CC=\"$FPM_CC\"" > $INFERENCE_ENGINE_PC -echo "INFERENCE_ENGINE_FPM_FC=\"$FPM_FC\"" >> $INFERENCE_ENGINE_PC -echo "INFERENCE_ENGINE_FPM_LD_FLAG=\"$FPM_LD_FLAG\"" >> $INFERENCE_ENGINE_PC -echo "INFERENCE_ENGINE_FPM_FLAG=\"$FPM_FLAG\"" >> $INFERENCE_ENGINE_PC -echo "Name: inference-engine" >> $INFERENCE_ENGINE_PC -echo "Description: Inference Engine" >> $INFERENCE_ENGINE_PC -echo "URL: https://github.com/berkeleylab/inference-engine" >> $INFERENCE_ENGINE_PC -echo "Version: 0.1.2" >> $INFERENCE_ENGINE_PC -if [ $CI = true ]; then - echo "---------------" - echo "cat $INFERENCE_ENGINE_PC" - cat $INFERENCE_ENGINE_PC - echo "---------------" -fi - -export PKG_CONFIG_PATH -cp scripts/run-fpm.sh-header build/run-fpm.sh -RUN_FPM_SH="`realpath ./build/run-fpm.sh`" -echo "`which fpm` \$fpm_arguments \\" >> $RUN_FPM_SH -echo "--profile release \\" >> $RUN_FPM_SH -echo "--c-compiler \"`pkg-config inference-engine --variable=INFERENCE_ENGINE_FPM_CC`\" \\" >> $RUN_FPM_SH -echo "--compiler \"`pkg-config inference-engine --variable=INFERENCE_ENGINE_FPM_FC`\" \\" >> $RUN_FPM_SH -echo "--flag \"-cpp `pkg-config inference-engine --variable=INFERENCE_ENGINE_FPM_FLAG`\" \\" >> $RUN_FPM_SH -echo "--link-flag \"`pkg-config inference-engine --variable=INFERENCE_ENGINE_FPM_LD_FLAG`\" \\" >> $RUN_FPM_SH -echo "\$program_arguments" >> $RUN_FPM_SH -chmod u+x $RUN_FPM_SH -if [ $CI = true ]; then - echo "---------------" - echo "cat $RUN_FPM_SH" - cat $RUN_FPM_SH - echo "---------------" -fi - -if command -v fpm > /dev/null 2>&1; then - brew tap fortran-lang/fortran - brew install fpm -fi - -echo "$RUN_FPM_SH test --profile release" -$RUN_FPM_SH test --profile release +fpm test echo "" -echo "____________________ Inference-Engine has been set up! _______________________" +echo "____________________ Inference-Engine has been set up! _______________________" echo "" echo "The example/ subdirectory contains sample use cases. Execute the following" echo "command to see a list of examples that you can run:" echo "" -echo "./build/run-fpm.sh run --example" +echo "fpm run --example" echo "" -echo "Execute an example with a command of the form" +echo "Then execute an example with a command of the form" echo "" -echo "./build/run-fpm.sh run --profile release --example " +echo "fpm run --profile release --example " echo "" -echo "where '--profile release' ensures an optimized build, which can greatly reduce" -echo "runtime. The latter command will print usage information if the example" -echo "requires additional arguments." +echo "where '--profile release' ensures an optimized build. Replace with any" +echo "name from the previously generated list. The example program will print usage" +echo "information if additional arguments are required." diff --git a/src/inference_engine/trainable_engine_s.f90 b/src/inference_engine/trainable_engine_s.f90 index ee696ef43..91eece432 100644 --- a/src/inference_engine/trainable_engine_s.f90 +++ b/src/inference_engine/trainable_engine_s.f90 @@ -35,7 +35,8 @@ class is(differentiable_activation_strategy_t) trainable_engine%differentiable_activation_strategy_ = activation class default - error stop "trainable_engine_s(from_inference_engine): activation strategy must be a differentiable_activation_stragegy_t" + error stop & + "trainable_engine_s(from_inference_engine): activation strategy must be a differentiable_activation_stragegy_t" end select end associate diff --git a/src/inference_engine_m.f90 b/src/inference_engine_m.f90 index e21c4fbca..c56d5ec48 100644 --- a/src/inference_engine_m.f90 +++ b/src/inference_engine_m.f90 @@ -9,7 +9,6 @@ module inference_engine_m use inference_engine_m_, only : inference_engine_t, difference_t, infer use kind_parameters_m, only : rkind use mini_batch_m, only : mini_batch_t - use NetCDF_file_m, only : NetCDF_file_t use network_configuration_m, only : network_configuration_t use relu_m, only : relu_t use sigmoid_m, only : sigmoid_t diff --git a/test/main.f90 b/test/main.f90 index cc3f840fd..cfb350749 100644 --- a/test/main.f90 +++ b/test/main.f90 @@ -27,13 +27,6 @@ program main call hyperparameters_test%report(passes, tests) call network_configuration_test%report(passes, tests) call training_configuration_test%report(passes, tests) -#ifndef __INTEL_FORTRAN - block - use netCDF_file_test_m, only : netCDF_file_test_t - type(netCDF_file_test_t) netCDF_file_test - call netCDF_file_test%report(passes, tests) - end block -#endif // __INTEL_FORTRAN call cpu_time(t_finish) print * diff --git a/test/network_configuration_test_m.f90 b/test/network_configuration_test_m.f90 index b512d640f..12afc99e3 100644 --- a/test/network_configuration_test_m.f90 +++ b/test/network_configuration_test_m.f90 @@ -44,7 +44,8 @@ function results() result(test_results) [ write_then_read_network_configuration() & ] & ) - call assert(size(descriptions) == size(outcomes),"network_configuration_test_m(results): size(descriptions) == size(outcomes)") + call assert(size(descriptions) == size(outcomes), & + "network_configuration_test_m(results): size(descriptions) == size(outcomes)") test_results = test_result_t(descriptions, outcomes) end associate @@ -62,4 +63,4 @@ function write_then_read_network_configuration() result(test_passes) end function -end module network_configuration_test_m \ No newline at end of file +end module network_configuration_test_m