From 58722029ff04d8a79210b131ead45d00c88b164f Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Sun, 28 Sep 2025 13:45:05 -0700 Subject: [PATCH 1/5] doc(README): explain parallel testing with flang This commit adds instructions for building and running neural-fortran (specifcally the test suite) using the experimental multi-image capabilities of LLVM flang 22 + the Caffeine parallel runtime library as an alternative to gfortran + OpenCoarrays. --- README.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/README.md b/README.md index 5dbda06b..b475d8a1 100644 --- a/README.md +++ b/README.md @@ -93,6 +93,20 @@ in parallel, respectively: fpm build --compiler caf --profile release --flag "-cpp -DPARALLEL" ``` +An experimental capability exists for parallel runs when building with LLVM `flang-new` +version 22 or later and [Caffeine](https://go.lbl.gov/caffeine). Steps for installing +LLVM 22.0.0git (the llvm-project main branch as of this writing) and Caffeine are +outlined in [parallel-testing-with-flang.md]. Once installed, an `fpm` command of the +following form should launch the neural-fortran test suite with two executing images: + +``` +GASNET_PSHM_NODES=2 \ + fpm test \ + --compiler flang-new \ + --flag "-O3 -fcoarray -DPARALLEL" \ + --link-flag "-lcaffeine -lgasnet-smp-seq -L/lib -L/lib" +``` + #### Testing with fpm ``` @@ -305,3 +319,5 @@ group. Neural-fortran has been used successfully in over a dozen published studies. See all papers that cite it [here](https://scholar.google.com/scholar?cites=7315840714744905948). + +https://github.com/BerkeleyLab/julienne/blob/e9f7ea8069206bfc4abf6a9e6dbbd7d07bda075a/doc/parallel-testing-with-flang.md From 701dd5332c21dfe7d7c18cfd55821f8398391a06 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Mon, 29 Sep 2025 12:43:58 -0700 Subject: [PATCH 2/5] test(linear_2d_layer): demo julienne This commit demonstrates running the linear_2d_layer unit tests using the Julienne correctness-checking framework (https://go.lbl.gov/julienne). The commit adds * test/driver.f90 - main program * test/linear_2d_layer_test_m.f90 - test module and adds the Julienne 3.2.1 release as a development dependency so that it is only downloaded and built if the tests are being run. --- fpm.toml | 3 + test/driver.f90 | 14 +++++ test/linear_2d_layer_test_m.f90 | 97 +++++++++++++++++++++++++++++++++ 3 files changed, 114 insertions(+) create mode 100644 test/driver.f90 create mode 100644 test/linear_2d_layer_test_m.f90 diff --git a/fpm.toml b/fpm.toml index 0d85b9dc..64f2ac58 100644 --- a/fpm.toml +++ b/fpm.toml @@ -5,5 +5,8 @@ author = "Milan Curcic" maintainer = "mcurcic@miami.edu" copyright = "Copyright 2018-2025, neural-fortran contributors" +[dev-dependencies] +julienne = {git = "https://github.com/berkeleylab/julienne", tag = "3.2.1"} + [preprocess] [preprocess.cpp] diff --git a/test/driver.f90 b/test/driver.f90 new file mode 100644 index 00000000..fd4bf1a6 --- /dev/null +++ b/test/driver.f90 @@ -0,0 +1,14 @@ +! Copyright (c) 2024-2025, The Regents of the University of California and Sourcery Institute +! Terms of use are as specified in LICENSE.txt + +program test_suite_driver + use julienne_m, only : test_fixture_t, test_harness_t + use linear_2d_layer_test_m, only : linear_2d_layer_test_t + implicit none + + associate(test_harness => test_harness_t([ & + test_fixture_t(linear_2d_layer_test_t()) & + ])) + call test_harness%report_results + end associate +end program test_suite_driver diff --git a/test/linear_2d_layer_test_m.f90 b/test/linear_2d_layer_test_m.f90 new file mode 100644 index 00000000..ee5b51ee --- /dev/null +++ b/test/linear_2d_layer_test_m.f90 @@ -0,0 +1,97 @@ +! Copyright (c) 2024-2025, The Regents of the University of California and Sourcery Institute +! Terms of use are as specified in LICENSE.txt + +module linear_2d_layer_test_m + use julienne_m, only : & + test_t, test_description_t, test_diagnosis_t, test_result_t & + ,operator(.equalsExpected.), operator(//), operator(.approximates.), operator(.within.), operator(.also.), operator(.all.) + use nf_linear2d_layer, only: linear2d_layer + implicit none + + type, extends(test_t) :: linear_2d_layer_test_t + contains + procedure, nopass :: subject + procedure, nopass :: results + end type + +contains + + pure function subject() result(test_subject) + character(len=:), allocatable :: test_subject + test_subject = 'A linear_2d_layer' + end function + + function results() result(test_results) + type(linear_2d_layer_test_t) linear_2d_layer_test + type(test_result_t), allocatable :: test_results(:) + test_results = linear_2d_layer_test%run( & + [test_description_t('updating gradients', check_gradient_updates) & + ]) + end function + + function check_gradient_updates() result(test_diagnosis) + type(test_diagnosis_t) test_diagnosis + + real :: input(3, 4) = reshape([0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 0.11, 0.12], [3, 4]) + real :: gradient(3, 2) = reshape([0.0, 10., 0.2, 3., 0.4, 1.], [3, 2]) + type(linear2d_layer) :: linear + real, pointer :: w_ptr(:) + real, pointer :: b_ptr(:) + + integer :: num_parameters + real, allocatable :: parameters(:) ! Remove the fixed size + real :: expected_parameters(10) = [& + 0.100000001, 0.100000001, 0.100000001, 0.100000001, 0.100000001, 0.100000001, 0.100000001, 0.100000001,& + 0.109999999, 0.109999999& + ] + real :: gradients(10) + real :: expected_gradients(10) = [& + 1.03999996, 4.09999990, 7.15999985, 1.12400007, 0.240000010, 1.56000006, 2.88000011, 2.86399961,& + 10.1999998, 4.40000010& + ] + real :: updated_parameters(10) + real :: updated_weights(8) + real :: updated_biases(2) + real :: expected_weights(8) = [& + 0.203999996, 0.509999990, 0.816000044, 0.212400019, 0.124000005, 0.256000012, 0.388000011, 0.386399955& + ] + real :: expected_biases(2) = [1.13000000, 0.550000012] + + integer :: i + real, parameter :: tolerance = 0. + + linear = linear2d_layer(out_features=2) + call linear % init([3, 4]) + linear % weights = 0.1 + linear % biases = 0.11 + call linear % forward(input) + call linear % backward(input, gradient) + num_parameters = linear % get_num_params() + + test_diagnosis = (num_parameters .equalsExpected. 10) // " (number of parameters)" + + call linear % get_params_ptr(w_ptr, b_ptr) ! Change this_layer to linear + allocate(parameters(size(w_ptr) + size(b_ptr))) + parameters(1:size(w_ptr)) = w_ptr + parameters(size(w_ptr)+1:) = b_ptr + test_diagnosis = test_diagnosis .also. (.all. (parameters .approximates. expected_parameters .within. tolerance) // " (parameters)") + + gradients = linear % get_gradients() + test_diagnosis = test_diagnosis .also. (.all. (gradients .approximates. expected_gradients .within. tolerance) // " (gradients)") + + do i = 1, num_parameters + updated_parameters(i) = parameters(i) + 0.1 * gradients(i) + end do + + call linear % get_params_ptr(w_ptr, b_ptr) ! Change this_layer to linear + w_ptr = updated_parameters(1:size(w_ptr)) + b_ptr = updated_parameters(size(w_ptr)+1:) + updated_weights = reshape(linear % weights, shape(expected_weights)) + test_diagnosis = test_diagnosis .also. (.all. (updated_weights .approximates. expected_weights .within. tolerance) // " (updated weights)") + + updated_biases = linear % biases + test_diagnosis = test_diagnosis .also. (.all. (updated_biases .approximates. expected_biases .within. tolerance) // " (updated biases)") + + end function + +end module linear_2d_layer_test_m \ No newline at end of file From 32258a7922bf3b786ba5756ebc18fbe1f0ac181a Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Mon, 29 Sep 2025 13:44:21 -0700 Subject: [PATCH 3/5] fix(CI): add flag for GCC 13 to accept long lines --- .github/workflows/ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cc1ae458..bc556693 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,9 +51,9 @@ jobs: fpm-version: "v0.10.1" - uses: actions/checkout@v4 - name: Compile - run: fpm build --profile debug + run: fpm build --profile debug --flag -ffree-line-length-none - name: Test - run: fpm test --profile debug + run: fpm test --profile debug --flag -ffree-line-length-none gnu-fpm-release: name: gnu-fpm-release @@ -64,6 +64,6 @@ jobs: fpm-version: "v0.10.1" - uses: actions/checkout@v4 - name: Compile - run: fpm build --profile release + run: fpm build --profile release --flag -ffree-line-length-none - name: Test - run: fpm test --profile release + run: fpm test --profile release --flag -ffree-line-length-none From a156df5d9c295798bf1e25d58369c3844c000785 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Mon, 29 Sep 2025 15:14:00 -0700 Subject: [PATCH 4/5] chore: rm copyright from Julienne-generated files --- test/driver.f90 | 3 --- test/linear_2d_layer_test_m.f90 | 3 --- 2 files changed, 6 deletions(-) diff --git a/test/driver.f90 b/test/driver.f90 index fd4bf1a6..c441a7f3 100644 --- a/test/driver.f90 +++ b/test/driver.f90 @@ -1,6 +1,3 @@ -! Copyright (c) 2024-2025, The Regents of the University of California and Sourcery Institute -! Terms of use are as specified in LICENSE.txt - program test_suite_driver use julienne_m, only : test_fixture_t, test_harness_t use linear_2d_layer_test_m, only : linear_2d_layer_test_t diff --git a/test/linear_2d_layer_test_m.f90 b/test/linear_2d_layer_test_m.f90 index ee5b51ee..7536ef79 100644 --- a/test/linear_2d_layer_test_m.f90 +++ b/test/linear_2d_layer_test_m.f90 @@ -1,6 +1,3 @@ -! Copyright (c) 2024-2025, The Regents of the University of California and Sourcery Institute -! Terms of use are as specified in LICENSE.txt - module linear_2d_layer_test_m use julienne_m, only : & test_t, test_description_t, test_diagnosis_t, test_result_t & From 9b6d6cf17c0a9de6bfdda2b32d10677207b9fa53 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Mon, 29 Sep 2025 15:17:46 -0700 Subject: [PATCH 5/5] test(linear_2d_layer): work around GCC 13 Fortran 2008 allowed for a procedure name to be passed as the actual argument to a dummy argument that is a procedure pointer. This feature was added to gfortran 14.3. This commit works around the lack of the feature in older versions. --- test/linear_2d_layer_test_m.f90 | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/test/linear_2d_layer_test_m.f90 b/test/linear_2d_layer_test_m.f90 index 7536ef79..2f6dd078 100644 --- a/test/linear_2d_layer_test_m.f90 +++ b/test/linear_2d_layer_test_m.f90 @@ -1,8 +1,15 @@ +#include "language-support.F90" + ! This include and the macros below are only required for gfortran versions older than 14.3 + ! because those versions lacked a Fortran 2008 feature that facilitates more concise code. + module linear_2d_layer_test_m + use nf_linear2d_layer, only: linear2d_layer use julienne_m, only : & test_t, test_description_t, test_diagnosis_t, test_result_t & ,operator(.equalsExpected.), operator(//), operator(.approximates.), operator(.within.), operator(.also.), operator(.all.) - use nf_linear2d_layer, only: linear2d_layer +#if ! HAVE_PROCEDURE_ACTUAL_FOR_POINTER_DUMMY + use julienne_m, only : diagnosis_function_i +#endif implicit none type, extends(test_t) :: linear_2d_layer_test_t @@ -18,6 +25,8 @@ pure function subject() result(test_subject) test_subject = 'A linear_2d_layer' end function +#if HAVE_PROCEDURE_ACTUAL_FOR_POINTER_DUMMY + function results() result(test_results) type(linear_2d_layer_test_t) linear_2d_layer_test type(test_result_t), allocatable :: test_results(:) @@ -26,6 +35,23 @@ function results() result(test_results) ]) end function +#else + ! Work around a missing Fortran 2008 feature that was added to gfortran in version 14.3 + + function results() result(test_results) + type(linear_2d_layer_test_t) linear_2d_layer_test + type(test_result_t), allocatable :: test_results(:) + procedure(diagnosis_function_i), pointer :: & + check_gradient_updates_ptr => check_gradient_updates + + test_results = linear_2d_layer_test%run( & + [test_description_t('updating gradients', check_gradient_updates_ptr) & + ]) + end function + +#endif + + function check_gradient_updates() result(test_diagnosis) type(test_diagnosis_t) test_diagnosis