Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make HDF5 optional under CMake #187

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ env:

jobs:

build_and_test_debug_profile:
gnu-fpm-debug:
name: Build and test in debug mode
runs-on: ubuntu-latest

Expand All @@ -42,7 +42,7 @@ jobs:
- name: Test
run: fpm test --profile debug

build_and_test_release_profile:
gnu-fpm-release:
name: Build and test in release mode
runs-on: ubuntu-latest

Expand Down
23 changes: 14 additions & 9 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,12 @@ include(FetchContent)
include(cmake/options.cmake)
include(cmake/compilers.cmake)

include(cmake/functional.cmake)
include(cmake/h5fortran.cmake)
include(cmake/json.cmake)
if(USE_KERAS_HDF5)
add_definitions(-DUSE_KERAS_HDF5)
include(cmake/functional.cmake)
include(cmake/h5fortran.cmake)
include(cmake/json.cmake)
endif()

# library to archive (libneural-fortran.a)
add_library(neural-fortran
Expand Down Expand Up @@ -65,12 +68,14 @@ add_library(neural-fortran
src/nf/io/nf_io_hdf5_submodule.f90
)

target_link_libraries(neural-fortran PRIVATE
functional::functional
h5fortran::h5fortran
HDF5::HDF5
jsonfortran::jsonfortran
)
if(USE_KERAS_HDF5)
target_link_libraries(neural-fortran PRIVATE
functional::functional
h5fortran::h5fortran
HDF5::HDF5
jsonfortran::jsonfortran
)
endif()

install(TARGETS neural-fortran)

Expand Down
29 changes: 19 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ Read the paper [here](https://arxiv.org/abs/1902.06714).
RMSProp, Adagrad, Adam, AdamW
* More than a dozen activation functions and their derivatives
* Loss functions and metrics: Quadratic, Mean Squared Error, Pearson Correlation etc.
* Loading dense and convolutional models from Keras HDF5 (.h5) files
* Data-based parallelism
* Loading dense and convolutional models from Keras HDF5 (.h5) files (optional)

### Available layers

Expand Down Expand Up @@ -51,24 +51,23 @@ cd neural-fortran
Required dependencies are:

* A Fortran compiler
* [HDF5](https://www.hdfgroup.org/downloads/hdf5/)
(must be provided by the OS package manager or your own build from source)
* [functional-fortran](https://github.com/wavebitscientific/functional-fortran),
[h5fortran](https://github.com/geospace-code/h5fortran),
[json-fortran](https://github.com/jacobwilliams/json-fortran)
(all handled by neural-fortran's build systems, no need for a manual install)
* [fpm](https://github.com/fortran-lang/fpm) or
[CMake](https://cmake.org) for building the code

Optional dependencies are:

* [HDF5](https://www.hdfgroup.org/downloads/hdf5/) for input of Keras models
saved as HDF5 files. You can use the HDF5 that ships from your system
package manager or build your own from source.
Note that HDF5 is still a required dependency if building with fpm instead
of CMake.
* OpenCoarrays (for parallel execution with GFortran)
* BLAS, MKL, or similar (for offloading `matmul` and `dot_product` calls)
* curl (for downloading testing and example datasets)
* curl (for downloading test and example datasets)

Compilers tested include:

* gfortran-9.4.0
* gfortran-13.2.0
* ifort-2021.4
* ifx-2021.4

Expand All @@ -84,7 +83,8 @@ fpm build \
--flag "-I$HDF5INC -L$HDF5LIB"
```

HDF5 is now a required dependency, so you have to provide it to fpm.
HDF5 is for now a required dependency when building with fpm, so you have to
provide it to fpm.
The above command assumes that the `HDF5INC` and `HDF5LIB` environment
variables are set to the include and library paths, respectively, of your
HDF5 install.
Expand Down Expand Up @@ -139,6 +139,15 @@ make

Tests and examples will be built in the `bin/` directory.

#### Building with Keras HDF5 input

When building neural-fortran with CMake, Keras HDF5 input feature will not be
built by default. To build it, use the `-DUSE_KERAS_HDF5=1` option.

```
cmake .. -DSERIAL=1 -DUSE_KERAS_HDF5=1
```

#### Building in parallel mode

If you use GFortran and want to run neural-fortran in parallel,
Expand Down
12 changes: 6 additions & 6 deletions cmake/compilers.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ if(CMAKE_Fortran_COMPILER_ID STREQUAL "GNU")
message(STATUS "Configuring build to use BLAS from ${BLAS}")
endif()

add_compile_options("$<$<AND:$<COMPILE_LANGUAGE:Fortran>,$<CONFIG:Debug>>:-fcheck=bounds;-fbacktrace>")
add_compile_options("$<$<AND:$<COMPILE_LANGUAGE:Fortran>,$<CONFIG:Release>>:-Ofast;-fno-frontend-optimize;-fno-backtrace>")
add_compile_options("$<$<AND:$<COMPILE_LANGUAGE:Fortran>,$<CONFIG:Debug>>:-cpp;-O0;-fcheck=bounds;-fbacktrace>")
add_compile_options("$<$<AND:$<COMPILE_LANGUAGE:Fortran>,$<CONFIG:Release>>:-cpp;-Ofast;-fno-backtrace>")

elseif(CMAKE_Fortran_COMPILER_ID MATCHES "^Intel")
# compiler flags for ifort
Expand Down Expand Up @@ -44,12 +44,12 @@ elseif(CMAKE_Fortran_COMPILER_ID MATCHES "^Intel")
else()
string(APPEND CMAKE_Fortran_FLAGS " -assume byterecl")
endif()
add_compile_options("$<$<AND:$<COMPILE_LANGUAGE:Fortran>,$<CONFIG:Debug>>:-check;-traceback>")
# add_compile_options("$<$<AND:$<COMPILE_LANGUAGE:Fortran>,$<CONFIG:Release>>:-O3>")
add_compile_options("$<$<AND:$<COMPILE_LANGUAGE:Fortran>,$<CONFIG:Debug>>:-fpp;-O0;-check;-traceback>")
add_compile_options("$<$<AND:$<COMPILE_LANGUAGE:Fortran>,$<CONFIG:Release>>:-fpp;-O3>")

elseif(CMAKE_Fortran_COMPILER_ID STREQUAL "Cray")
# compiler flags for Cray ftn
string(APPEND CMAKE_Fortran_FLAGS " -h noomp")
add_compile_options("$<$<AND:$<COMPILE_LANGUAGE:Fortran>,$<CONFIG:Debug>>:-O0;-g>")
add_compile_options("$<$<AND:$<COMPILE_LANGUAGE:Fortran>,$<CONFIG:Release>>:-O3>")
add_compile_options("$<$<AND:$<COMPILE_LANGUAGE:Fortran>,$<CONFIG:Debug>>:-e Z;-O0;-g>")
add_compile_options("$<$<AND:$<COMPILE_LANGUAGE:Fortran>,$<CONFIG:Release>>:-e Z;-O3>")
endif()
7 changes: 7 additions & 0 deletions cmake/options.cmake
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
option(SERIAL "Serial execution")
option(USE_KERAS_HDF5 "Enable modules to load Keras HDF5 models")
option(${PROJECT_NAME}_BUILD_TESTING "build ${PROJECT_NAME} tests" true)
option(${PROJECT_NAME}_BUILD_EXAMPLES "build ${PROJECT_NAME} examples" true)

Expand All @@ -14,6 +15,12 @@ else()
message(STATUS "Configuring build for parallel execution")
endif()

if(USE_KERAS_HDF5)
message(STATUS "Configuring build with Keras HDF5 support")
else()
message(STATUS "Configuring build without Keras HDF5 support")
endif()

# --- Generally useful CMake project options

# Rpath options necessary for shared library install to work correctly in user projects
Expand Down
17 changes: 11 additions & 6 deletions example/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,15 @@ foreach(execid
quadratic
)
add_executable(${execid} ${execid}.f90)
target_link_libraries(${execid} PRIVATE
neural-fortran
h5fortran::h5fortran
jsonfortran::jsonfortran
${LIBS}
)
if(USE_KERAS_HDF5)
target_compile_definitions(${execid} PRIVATE USE_KERAS_HDF5)
target_link_libraries(${execid} PRIVATE
neural-fortran
h5fortran::h5fortran
jsonfortran::jsonfortran
${LIBS}
)
else()
target_link_libraries(${execid} PRIVATE neural-fortran ${LIBS})
endif()
endforeach()
4 changes: 3 additions & 1 deletion example/cnn_from_keras.f90
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
program cnn_from_keras
#ifdef USE_KERAS_HDF5

! This example demonstrates loading a convolutional model
! pre-trained on the MNIST dataset from a Keras HDF5
Expand Down Expand Up @@ -55,4 +56,5 @@ real function accuracy(net, x, y)
accuracy = real(good) / size(x, dim=4)
end function accuracy

end program cnn_from_keras
#endif
end program cnn_from_keras
4 changes: 3 additions & 1 deletion example/dense_from_keras.f90
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
program dense_from_keras
#ifdef USE_KERAS_HDF5

! This example demonstrates loading a dense model
! pre-trained on the MNIST dataset from a Keras HDF5
Expand Down Expand Up @@ -49,4 +50,5 @@ real function accuracy(net, x, y)
accuracy = real(good) / size(x, dim=2)
end function accuracy

end program dense_from_keras
#endif
end program dense_from_keras
2 changes: 1 addition & 1 deletion src/nf/io/nf_io_hdf5.f90
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,4 @@ end subroutine get_hdf5_dataset_real32_4d

end interface get_hdf5_dataset

end module nf_io_hdf5
end module nf_io_hdf5
2 changes: 2 additions & 0 deletions src/nf/io/nf_io_hdf5_submodule.f90
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#ifdef USE_KERAS_HDF5
submodule(nf_io_hdf5) nf_io_hdf5_submodule

use iso_fortran_env, only: int64, real32, stderr => error_unit
Expand Down Expand Up @@ -109,3 +110,4 @@ module subroutine get_hdf5_dataset_real32_4d(filename, object_name, values)
end subroutine get_hdf5_dataset_real32_4d

end submodule nf_io_hdf5_submodule
#endif
2 changes: 2 additions & 0 deletions src/nf/nf_keras_submodule.f90
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#ifdef USE_KERAS_HDF5
submodule(nf_keras) nf_keras_submodule

use functional, only: reverse
Expand Down Expand Up @@ -102,3 +103,4 @@ module function get_keras_h5_layers(filename) result(res)
end function get_keras_h5_layers

end submodule nf_keras_submodule
#endif
4 changes: 3 additions & 1 deletion src/nf/nf_network_submodule.f90
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ module function network_from_layers(layers) result(res)
end function network_from_layers


#ifdef USE_KERAS_HDF5
module function network_from_keras(filename) result(res)
character(*), intent(in) :: filename
type(network) :: res
Expand Down Expand Up @@ -222,9 +223,10 @@ module function network_from_keras(filename) result(res)

end select

end do
end do

end function network_from_keras
#endif


pure function get_activation_by_name(activation_name) result(res)
Expand Down
8 changes: 7 additions & 1 deletion test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,13 @@ foreach(execid
metrics
)
add_executable(test_${execid} test_${execid}.f90)
target_link_libraries(test_${execid} PRIVATE neural-fortran h5fortran::h5fortran jsonfortran::jsonfortran ${LIBS})

if (USE_KERAS_HDF5)
target_compile_definitions(test_${execid} PRIVATE USE_KERAS_HDF5)
target_link_libraries(test_${execid} PRIVATE neural-fortran h5fortran::h5fortran jsonfortran::jsonfortran ${LIBS})
else()
target_link_libraries(test_${execid} PRIVATE neural-fortran ${LIBS})
endif()

add_test(NAME test_${execid} COMMAND test_${execid})
endforeach()
Expand Down
2 changes: 2 additions & 0 deletions test/test_cnn_from_keras.f90
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
program test_cnn_from_keras
#ifdef USE_KERAS_HDF5

use iso_fortran_env, only: stderr => error_unit
use nf, only: network
Expand Down Expand Up @@ -66,4 +67,5 @@ real function accuracy(net, x, y)
accuracy = real(good) / size(x, dim=4)
end function accuracy

#endif
end program test_cnn_from_keras
6 changes: 3 additions & 3 deletions test/test_conv2d_network.f90
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ program test_conv2d_network

type(network) :: cnn
real :: y(1)
real :: tolerance = 1e-5
real :: tolerance = 1e-4
integer :: n
integer, parameter :: num_iterations = 1000

Expand Down Expand Up @@ -76,7 +76,7 @@ program test_conv2d_network
type(network) :: cnn
real :: x(1, 8, 8)
real :: y(1)
real :: tolerance = 1e-5
real :: tolerance = 1e-4
integer :: n
integer, parameter :: num_iterations = 1000

Expand Down Expand Up @@ -111,7 +111,7 @@ program test_conv2d_network
type(network) :: cnn
real :: x(1, 12, 12)
real :: y(9)
real :: tolerance = 1e-5
real :: tolerance = 1e-4
integer :: n
integer, parameter :: num_iterations = 5000

Expand Down
2 changes: 2 additions & 0 deletions test/test_dense_network_from_keras.f90
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
program test_dense_network_from_keras
#ifdef USE_KERAS_HDF5

use iso_fortran_env, only: stderr => error_unit
use nf, only: network
Expand Down Expand Up @@ -97,4 +98,5 @@ real function accuracy(net, x, y)
accuracy = real(good) / size(x, dim=2)
end function accuracy

#endif
end program test_dense_network_from_keras
4 changes: 3 additions & 1 deletion test/test_io_hdf5.f90
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
program test_io_hdf5
#ifdef USE_KERAS_HDF5

use iso_fortran_env, only: stderr => error_unit
use nf_datasets, only: download_and_unpack, keras_dense_mnist_url
Expand Down Expand Up @@ -54,4 +55,5 @@ program test_io_hdf5
stop 1
end if

end program test_io_hdf5
#endif
end program test_io_hdf5
4 changes: 3 additions & 1 deletion test/test_keras_read_model.f90
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
program test_keras_read_model
#ifdef USE_KERAS_HDF5

use iso_fortran_env, only: stderr => error_unit
use nf_datasets, only: download_and_unpack, keras_dense_mnist_url, &
Expand Down Expand Up @@ -105,4 +106,5 @@ program test_keras_read_model
stop 1
end if

end program test_keras_read_model
#endif
end program test_keras_read_model
7 changes: 6 additions & 1 deletion test/test_reshape_layer.f90
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ program test_reshape_layer

use iso_fortran_env, only: stderr => error_unit
use nf, only: input, network, reshape_layer => reshape
use nf_datasets, only: download_and_unpack, keras_reshape_url
use nf_datasets, only: download_and_unpack
#ifdef USE_KERAS_HDF5
use nf_datasets, only: keras_reshape_url
#endif

implicit none

Expand Down Expand Up @@ -43,6 +46,7 @@ program test_reshape_layer
ok = .false.
end if

#ifdef USE_KERAS_HDF5
! Now test reading the reshape layer from a Keras h5 model.
inquire(file=keras_reshape_path, exist=file_exists)
if (.not. file_exists) call download_and_unpack(keras_reshape_url)
Expand All @@ -67,6 +71,7 @@ program test_reshape_layer
write(stderr, '(a)') 'the target shape of the reshape layer is correct.. failed'
ok = .false.
end if
#endif

if (ok) then
print '(a)', 'test_reshape_layer: All tests passed.'
Expand Down
Loading