From e717d93e5502fa0b6a11f0664e392c636c666f8a Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Tue, 27 Aug 2024 19:35:14 -0700 Subject: [PATCH 001/105] refac(neuron_t): add kind parameter --- src/inference_engine/kind_parameters_m.f90 | 4 ++++ src/inference_engine/neuron_m.f90 | 20 ++++++++++---------- src/inference_engine/neuron_s.f90 | 2 +- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/inference_engine/kind_parameters_m.f90 b/src/inference_engine/kind_parameters_m.f90 index 17afad275..973319d4d 100644 --- a/src/inference_engine/kind_parameters_m.f90 +++ b/src/inference_engine/kind_parameters_m.f90 @@ -4,6 +4,10 @@ module kind_parameters_m implicit none private public :: rkind + public :: default_real + public :: double_precision integer, parameter :: rkind = kind(1.0) + integer, parameter :: default_real = kind(1.) + integer, parameter :: double_precision = kind(1D0) end module kind_parameters_m diff --git a/src/inference_engine/neuron_m.f90 b/src/inference_engine/neuron_m.f90 index bdfb5e42f..45dcc652b 100644 --- a/src/inference_engine/neuron_m.f90 +++ b/src/inference_engine/neuron_m.f90 @@ -2,18 +2,18 @@ ! Terms of use are as specified in LICENSE.txt module neuron_m use julienne_string_m, only : string_t - use kind_parameters_m, only : rkind + use kind_parameters_m, only : default_real implicit none private public :: neuron_t - type neuron_t + type neuron_t(k) !! linked list of neurons - private - real(rkind), allocatable :: weights_(:) - real(rkind) bias_ - type(neuron_t), allocatable :: next + integer, kind :: k = default_real + real(k), allocatable, private :: weights_(:) + real(k), private :: bias_ + type(neuron_t(k)), allocatable, private :: next contains procedure :: to_json procedure :: weights @@ -35,8 +35,8 @@ pure recursive module function from_json(neuron_lines, start) result(neuron) pure module function from_components(weights, bias) result(neuron) !! construct single neuron_t object from an array of weights and a bias - real(rkind), intent(in) :: weights(:) - real(rkind), intent(in) :: bias + real, intent(in) :: weights(:) + real, intent(in) :: bias type(neuron_t) neuron end function @@ -53,13 +53,13 @@ pure module function to_json(self) result(lines) module function weights(self) result(my_weights) implicit none class(neuron_t), intent(in) :: self - real(rkind), allocatable :: my_weights(:) + real, allocatable :: my_weights(:) end function module function bias(self) result(my_bias) implicit none class(neuron_t), intent(in) :: self - real(rkind) my_bias + real my_bias end function module function next_allocated(self) result(next_is_allocated) diff --git a/src/inference_engine/neuron_s.f90 b/src/inference_engine/neuron_s.f90 index 2d9502278..eb195a38f 100644 --- a/src/inference_engine/neuron_s.f90 +++ b/src/inference_engine/neuron_s.f90 @@ -14,7 +14,7 @@ call assert(allocated(self%weights_), "neuron_s(to_json): allocated weights_") - csv_format = separated_values(separator=",", mold=[real(rkind)::]) + csv_format = separated_values(separator=",", mold=[real::]) allocate(character(len=size(self%weights_)*(characters_per_value+1)-1)::weights_string) allocate(character(len=characters_per_value)::bias_string) write(weights_string, fmt = csv_format) self%weights_ From 8efec6f3789b18c05be1912810bbf8fb6981a56d Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Tue, 27 Aug 2024 19:42:39 -0700 Subject: [PATCH 002/105] refac(layer_t): add kind parameter --- src/inference_engine/layer_m.f90 | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/inference_engine/layer_m.f90 b/src/inference_engine/layer_m.f90 index 78fccbd89..788cf2f63 100644 --- a/src/inference_engine/layer_m.f90 +++ b/src/inference_engine/layer_m.f90 @@ -5,16 +5,17 @@ module layer_m use julienne_string_m, only : string_t use inference_engine_m_, only : inference_engine_t use tensor_map_m, only : tensor_map_t + use kind_parameters_m, only : default_real implicit none private public :: layer_t - type layer_t + type layer_t(k) !! linked list of layers, each comprised of a linked list of neurons - private - type(neuron_t) neuron !! linked list of this layer's neurons - type(layer_t), allocatable :: next !! next layer + integer, kind :: k = default_real + type(neuron_t(k)), private :: neuron !! linked list of this layer's neurons + type(layer_t(k)), allocatable, private :: next !! next layer contains procedure :: inference_engine procedure :: count_layers From 1e25cd6d515a6db3a40b97ad86ebe7cde9d9e711 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Tue, 27 Aug 2024 19:48:05 -0700 Subject: [PATCH 003/105] refac(tensor_map): add kind parameter --- src/inference_engine/tensor_map_m.f90 | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/inference_engine/tensor_map_m.f90 b/src/inference_engine/tensor_map_m.f90 index 5560cf168..247e4d86e 100644 --- a/src/inference_engine/tensor_map_m.f90 +++ b/src/inference_engine/tensor_map_m.f90 @@ -3,16 +3,16 @@ module tensor_map_m use tensor_m, only : tensor_t use julienne_m, only : string_t - use kind_parameters_m, only : rkind + use kind_parameters_m, only : default_real implicit none private public :: tensor_map_t - type tensor_map_t - private - character(len=:), allocatable :: layer_ - real(rkind), allocatable, dimension(:) :: intercept_, slope_ + type tensor_map_t(k) + integer, kind :: k = default_real + character(len=:), allocatable, private :: layer_ + real(k), dimension(:), allocatable, private :: intercept_, slope_ contains procedure map_to_training_range procedure map_from_training_range @@ -27,7 +27,7 @@ module tensor_map_m pure module function from_component_ranges(layer, minima, maxima) result(tensor_map) implicit none character(len=*), intent(in) :: layer - real(rkind), dimension(:), intent(in) :: minima, maxima + real, dimension(:), intent(in) :: minima, maxima type(tensor_map_t) tensor_map end function From c63d9bbba880b276df69152d41f55f46672df9b7 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Tue, 27 Aug 2024 19:51:06 -0700 Subject: [PATCH 004/105] refac(tensor_m): add kind parameter --- src/inference_engine/tensor_m.f90 | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/inference_engine/tensor_m.f90 b/src/inference_engine/tensor_m.f90 index e79b6deab..d1faee13c 100644 --- a/src/inference_engine/tensor_m.f90 +++ b/src/inference_engine/tensor_m.f90 @@ -1,15 +1,15 @@ ! Copyright (c), The Regents of the University of California ! Terms of use are as specified in LICENSE.txt module tensor_m - use kind_parameters_m, only : rkind + use kind_parameters_m, only : default_real implicit none private public :: tensor_t - type tensor_t - private - real(rkind), allocatable :: values_(:) + type tensor_t(k) + integer, kind :: k = default_real + real, allocatable, private :: values_(:) contains procedure values procedure num_components @@ -19,7 +19,7 @@ module tensor_m pure module function construct_from_components(values) result(tensor) implicit none - real(rkind), intent(in) :: values(:) + real, intent(in) :: values(:) type(tensor_t) tensor end function @@ -30,7 +30,7 @@ pure module function construct_from_components(values) result(tensor) pure module function values(self) result(tensor_values) implicit none class(tensor_t), intent(in) :: self - real(rkind), allocatable :: tensor_values(:) + real, allocatable :: tensor_values(:) end function pure module function num_components(self) result(n) From 8bd7ae799b4ba6d92a4170dbe60e9e2f23cfa7ce Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Tue, 27 Aug 2024 19:57:47 -0700 Subject: [PATCH 005/105] refac(inference_engine_t): add kind parameter --- src/inference_engine/inference_engine_m_.f90 | 35 ++++++++++---------- src/inference_engine/inference_engine_s.F90 | 11 +++--- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/inference_engine/inference_engine_m_.f90 b/src/inference_engine/inference_engine_m_.f90 index ef7fe1ade..c4dbe2be9 100644 --- a/src/inference_engine/inference_engine_m_.f90 +++ b/src/inference_engine/inference_engine_m_.f90 @@ -5,7 +5,7 @@ module inference_engine_m_ use activation_strategy_m, only : activation_strategy_t use julienne_file_m, only : file_t use julienne_string_m, only : string_t - use kind_parameters_m, only : rkind + use kind_parameters_m, only : default_real use metadata_m, only : metadata_t use tensor_m, only : tensor_t use tensor_map_m, only : tensor_map_t @@ -17,14 +17,14 @@ module inference_engine_m_ public :: exchange_t public :: infer - type inference_engine_t + type inference_engine_t(k) !! Encapsulate the minimal information needed to perform inference - private - type(tensor_map_t) input_map_, output_map_ - type(metadata_t) metadata_ - real(rkind), allocatable :: weights_(:,:,:), biases_(:,:) - integer, allocatable :: nodes_(:) - class(activation_strategy_t), allocatable :: activation_strategy_ ! Strategy Pattern facilitates elemental activation + integer, kind :: k = default_real + type(tensor_map_t(k)), private :: input_map_, output_map_ + type(metadata_t), private :: metadata_ + real(k), allocatable, private :: weights_(:,:,:), biases_(:,:) + integer, allocatable, private :: nodes_(:) + class(activation_strategy_t), allocatable, private :: activation_strategy_ ! Strategy Pattern facilitates elemental activation contains procedure :: infer procedure :: to_json @@ -42,18 +42,19 @@ module inference_engine_m_ procedure :: to_exchange end type - type exchange_t - type(tensor_map_t) input_map_, output_map_ + type exchange_t(k) + integer, kind :: k = default_real + type(tensor_map_t(k)) input_map_, output_map_ type(metadata_t) metadata_ - real(rkind), allocatable :: weights_(:,:,:), biases_(:,:) + real(k), allocatable :: weights_(:,:,:), biases_(:,:) integer, allocatable :: nodes_(:) class(activation_strategy_t), allocatable :: activation_strategy_ ! Strategy Pattern facilitates elemental activation end type - type difference_t - private - real(rkind), allocatable :: weights_difference_(:,:,:), biases_difference_(:,:) - integer, allocatable :: nodes_difference_(:) + type difference_t(k) + integer, kind :: k = default_real + real(k), allocatable, private :: weights_difference_(:,:,:), biases_difference_(:,:) + integer, allocatable, private :: nodes_difference_(:) contains procedure :: norm end type @@ -64,7 +65,7 @@ impure module function construct_from_padded_arrays(metadata, weights, biases, n result(inference_engine) implicit none type(string_t), intent(in) :: metadata(:) - real(rkind), intent(in) :: weights(:,:,:), biases(:,:) + real, intent(in) :: weights(:,:,:), biases(:,:) integer, intent(in) :: nodes(0:) type(tensor_map_t), intent(in), optional :: input_map, output_map type(inference_engine_t) inference_engine @@ -111,7 +112,7 @@ impure elemental module function to_json(self) result(json_file) elemental module function norm(self) result(norm_of_self) implicit none class(difference_t), intent(in) :: self - real(rkind) norm_of_self + real norm_of_self end function elemental module function subtract(self, rhs) result(difference) diff --git a/src/inference_engine/inference_engine_s.F90 b/src/inference_engine/inference_engine_s.F90 index bc95cfd7b..1b3fba5d7 100644 --- a/src/inference_engine/inference_engine_s.F90 +++ b/src/inference_engine/inference_engine_s.F90 @@ -17,7 +17,6 @@ end interface character(len=*), parameter :: acceptable_engine_tag = "0.13.0" ! git tag capable of reading the current json file format - real, parameter :: zero = 0._rkind, one = 1._rkind contains @@ -43,7 +42,7 @@ module procedure infer - real(rkind), allocatable :: a(:,:) + real, allocatable :: a(:,:) integer, parameter :: input_layer = 0 integer l @@ -197,9 +196,9 @@ impure function activation_factory_method(activation_name) result(activation) type(tensor_map_t) proto_map type(metadata_t) proto_meta type(neuron_t) proto_neuron - proto_map = tensor_map_t("",[zero],[one]) + proto_map = tensor_map_t("",[0.],[1.]) proto_meta = metadata_t(string_t(""),string_t(""),string_t(""),string_t(""),string_t("")) - proto_neuron = neuron_t([zero],one) + proto_neuron = neuron_t([0.],1.) #endif call assert_consistency(self) @@ -211,9 +210,9 @@ impure function activation_factory_method(activation_name) result(activation) ,first_hidden => lbound(self%nodes_,1) + 1 & ,last_hidden => ubound(self%nodes_,1) - 1 & #ifndef _CRAYFTN - ,proto_map => tensor_map_t("",[zero],[one]) & + ,proto_map => tensor_map_t("",[0.],[1.]) & ,proto_meta => metadata_t(string_t(""),string_t(""),string_t(""),string_t(""),string_t("")) & - ,proto_neuron => neuron_t([zero],zero) & + ,proto_neuron => neuron_t([0.],0.) & #endif ) associate( & From dff93d9ed68260bbf88a90a30b1de21d92be7da1 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Tue, 27 Aug 2024 21:21:55 -0700 Subject: [PATCH 006/105] test(inference_engine_t): use eqv op for compare This commit replaces the inference_engine_t type-bound, generic subtraction operator(-) with an user-defined approximate equivalence operator(==), which simplifies comparisons by eliminating the diffence_t type. The new operator is used in inference_engine_test_m instead of the difference_t, operator(-), and the norm() function. --- src/inference_engine/inference_engine_m_.f90 | 33 +++-------- src/inference_engine/inference_engine_s.F90 | 60 +++++--------------- src/inference_engine_m.f90 | 2 +- test/inference_engine_test_m.F90 | 12 +--- 4 files changed, 28 insertions(+), 79 deletions(-) diff --git a/src/inference_engine/inference_engine_m_.f90 b/src/inference_engine/inference_engine_m_.f90 index c4dbe2be9..f7413dd12 100644 --- a/src/inference_engine/inference_engine_m_.f90 +++ b/src/inference_engine/inference_engine_m_.f90 @@ -13,7 +13,6 @@ module inference_engine_m_ private public :: inference_engine_t - public :: difference_t public :: exchange_t public :: infer @@ -36,8 +35,8 @@ module inference_engine_m_ procedure :: nodes_per_layer procedure :: assert_conformable_with procedure :: skip - procedure, private :: subtract - generic :: operator(-) => subtract + procedure, private :: approximately_equal + generic :: operator(==) => approximately_equal procedure :: activation_function_name procedure :: to_exchange end type @@ -51,14 +50,6 @@ module inference_engine_m_ class(activation_strategy_t), allocatable :: activation_strategy_ ! Strategy Pattern facilitates elemental activation end type - type difference_t(k) - integer, kind :: k = default_real - real(k), allocatable, private :: weights_difference_(:,:,:), biases_difference_(:,:) - integer, allocatable, private :: nodes_difference_(:) - contains - procedure :: norm - end type - interface inference_engine_t impure module function construct_from_padded_arrays(metadata, weights, biases, nodes, input_map, output_map) & @@ -81,6 +72,13 @@ impure elemental module function from_json(file_) result(inference_engine) interface + elemental module function approximately_equal(lhs, rhs) result(lhs_eq_rhs) + !! The result is true if lhs and rhs are the same to within a tolerance + implicit none + class(inference_engine_t), intent(in) :: lhs, rhs + logical lhs_eq_rhs + end function + elemental module function map_to_input_range(self, tensor) result(normalized_tensor) !! The result contains the input tensor values normalized to fall on the range used during training implicit none @@ -109,19 +107,6 @@ impure elemental module function to_json(self) result(json_file) type(file_t) json_file end function - elemental module function norm(self) result(norm_of_self) - implicit none - class(difference_t), intent(in) :: self - real norm_of_self - end function - - elemental module function subtract(self, rhs) result(difference) - implicit none - class(inference_engine_t), intent(in) :: self - type(inference_engine_t), intent(in) :: rhs - type(difference_t) difference - end function - elemental module subroutine assert_conformable_with(self, inference_engine) implicit none class(inference_engine_t), intent(in) :: self diff --git a/src/inference_engine/inference_engine_s.F90 b/src/inference_engine/inference_engine_s.F90 index 1b3fba5d7..9ffa67580 100644 --- a/src/inference_engine/inference_engine_s.F90 +++ b/src/inference_engine/inference_engine_s.F90 @@ -13,7 +13,6 @@ interface assert_consistency procedure inference_engine_consistency - procedure difference_consistency end interface character(len=*), parameter :: acceptable_engine_tag = "0.13.0" ! git tag capable of reading the current json file format @@ -114,24 +113,6 @@ pure subroutine inference_engine_consistency(self) end subroutine - pure subroutine difference_consistency(self) - - type(difference_t), intent(in) :: self - - integer, parameter :: input_layer=0 - - associate( & - all_allocated=>[allocated(self%weights_difference_),allocated(self%biases_difference_),allocated(self%nodes_difference_)] & - ) - call assert(all(all_allocated),"inference_engine_s(difference_consistency): fully_allocated",intrinsic_array_t(all_allocated)) - end associate - - call assert(all(size(self%biases_difference_,1)==[size(self%weights_difference_,1), size(self%weights_difference_,2)]), & - "inference_engine_s(difference_consistency): conformable arrays" & - ) - - end subroutine - impure function activation_factory_method(activation_name) result(activation) character(len=*), intent(in) :: activation_name class(activation_strategy_t), allocatable :: activation @@ -424,32 +405,26 @@ impure function activation_factory_method(activation_name) result(activation) end procedure - module procedure subtract + module procedure approximately_equal - call assert_consistency(self) + logical nodes_eq + + nodes_eq = all(lhs%nodes_ == rhs%nodes_) + + call assert_consistency(lhs) call assert_consistency(rhs) - call self%assert_conformable_with(rhs) + call lhs%assert_conformable_with(rhs) block integer l + logical layer_eq(ubound(lhs%nodes_,1)) + real, parameter :: tolerance = 1.E-06 - allocate(difference%weights_difference_, mold = self%weights_) - allocate(difference%biases_difference_, mold = self%biases_) - allocate(difference%nodes_difference_, mold = self%nodes_) - - difference%weights_difference_ = 0. - difference%biases_difference_ = 0. - difference%nodes_difference_ = 0. - - l = 0 - difference%nodes_difference_(l) = self%nodes_(l) - rhs%nodes_(l) - - associate(n => self%nodes_) + associate(n => lhs%nodes_) #ifndef __INTEL_COMPILER do concurrent(l = 1:ubound(n,1)) - difference%weights_difference_(1:n(l),1:n(l-1),l) = self%weights_(1:n(l),1:n(l-1),l) - rhs%weights_(1:n(l),1:n(l-1),l) - difference%biases_difference_(1:n(l),l) = self%biases_(1:n(l),l) - rhs%biases_(1:n(l),l) - difference%nodes_difference_(l) = self%nodes_(l) - rhs%nodes_(l) + layer_eq(l) = all(abs(lhs%weights_(1:n(l),1:n(l-1),l) - rhs%weights_(1:n(l),1:n(l-1),l)) < tolerance) .and. & + all(abs(lhs%biases_(1:n(l),l) - rhs%biases_(1:n(l),l)) < tolerance) end do #else block @@ -457,9 +432,8 @@ impure function activation_factory_method(activation_name) result(activation) do l = 1, ubound(n,1) do j = 1, n(l) do k = 1, n(l-1) - difference%weights_difference_(j,k,l) = self%weights_(j,k,l) - rhs%weights_(j,k,l) - difference%biases_difference_(j,l) = self%biases_(j,l) - rhs%biases_(j,l) - difference%nodes_difference_(l) = self%nodes_(l) - rhs%nodes_(l) + layer_eq(l) = all(abs(lhs%weights_(j,k,l) - rhs%weights_(j,k,l)) < tolerance) .and. & + all(abs(lhs%biases_(j,l) - rhs%biases_(j,l)) < tolerance) end do end do end do @@ -467,13 +441,9 @@ impure function activation_factory_method(activation_name) result(activation) #endif end associate + lhs_eq_rhs = nodes_eq .and. all(layer_eq) end block - call assert_consistency(difference) - end procedure - - module procedure norm - norm_of_self = maxval([abs(self%weights_difference_), abs(self%biases_difference_), real(abs(self%nodes_difference_))]) end procedure module procedure num_outputs diff --git a/src/inference_engine_m.f90 b/src/inference_engine_m.f90 index 50f3cff31..efd0562bc 100644 --- a/src/inference_engine_m.f90 +++ b/src/inference_engine_m.f90 @@ -6,7 +6,7 @@ module inference_engine_m use differentiable_activation_strategy_m, only : differentiable_activation_strategy_t use hyperparameters_m, only : hyperparameters_t use input_output_pair_m, only : input_output_pair_t, shuffle, write_to_stdout - use inference_engine_m_, only : inference_engine_t, difference_t, infer + use inference_engine_m_, only : inference_engine_t, infer use kind_parameters_m, only : rkind use metadata_m, only : metadata_t use mini_batch_m, only : mini_batch_t diff --git a/test/inference_engine_test_m.F90 b/test/inference_engine_test_m.F90 index 435bd9ced..a2ecb10b1 100644 --- a/test/inference_engine_test_m.F90 +++ b/test/inference_engine_test_m.F90 @@ -12,7 +12,7 @@ module inference_engine_test_m #endif ! Internal dependencies - use inference_engine_m, only : inference_engine_t, tensor_t, difference_t + use inference_engine_m, only : inference_engine_t, tensor_t implicit none @@ -163,25 +163,19 @@ function multi_hidden_layer_net_to_from_json() result(test_passes) logical test_passes type(inference_engine_t) inference_engine, from_json type(file_t) json_file - type(difference_t) difference - real, parameter :: tolerance = 1.0E-06 inference_engine = distinct_parameters() json_file = inference_engine%to_json() from_json = inference_engine_t(json_file) - difference = inference_engine - from_json - test_passes = difference%norm() < tolerance + test_passes = inference_engine == from_json end function function varying_width_net_to_from_json() result(test_passes) logical test_passes - real, parameter :: tolerance = 1.0E-06 - type(difference_t) difference associate(inference_engine => varying_width()) associate(from_json => inference_engine_t( inference_engine%to_json() )) - difference = inference_engine - from_json - test_passes = difference%norm() < tolerance + test_passes = inference_engine == from_json end associate end associate end function From 1c1a93eff4927efdd8ba5a27601a792fb57092a5 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Tue, 27 Aug 2024 22:35:49 -0700 Subject: [PATCH 007/105] refac: define generic activation/derivative This commit sets up for adding double-precision support by making the activation and activation-derivative bindings generic. --- src/inference_engine/activation_strategy_m.f90 | 9 ++++----- .../differentiable_activation_strategy_m.f90 | 3 ++- src/inference_engine/gelu_m.f90 | 17 ++++++++--------- src/inference_engine/gelu_s.f90 | 15 +++++++-------- src/inference_engine/relu_m.f90 | 17 ++++++++--------- src/inference_engine/relu_s.f90 | 11 ++++------- src/inference_engine/sigmoid_m.f90 | 17 ++++++++--------- src/inference_engine/sigmoid_s.f90 | 4 ++-- src/inference_engine/step_m.f90 | 9 ++++----- src/inference_engine/step_s.f90 | 4 ++-- src/inference_engine/swish_m.f90 | 17 ++++++++--------- src/inference_engine/swish_s.f90 | 4 ++-- 12 files changed, 59 insertions(+), 68 deletions(-) diff --git a/src/inference_engine/activation_strategy_m.f90 b/src/inference_engine/activation_strategy_m.f90 index ff2bffa4d..80b754882 100644 --- a/src/inference_engine/activation_strategy_m.f90 +++ b/src/inference_engine/activation_strategy_m.f90 @@ -3,7 +3,6 @@ module activation_strategy_m ! External dependencies - use kind_parameters_m, only : rkind use julienne_string_m, only : string_t implicit none @@ -14,17 +13,17 @@ module activation_strategy_m type, abstract :: activation_strategy_t contains - procedure(activation_i), nopass, deferred :: activation + procedure(activation_i), nopass, deferred :: default_real_activation + generic :: activation => default_real_activation procedure(function_name_i), deferred :: function_name end type abstract interface elemental function activation_i(x) result(y) - import rkind implicit none - real(rkind), intent(in) :: x - real(rkind) y + real, intent(in) :: x + real y end function elemental function function_name_i(self) result(string) diff --git a/src/inference_engine/differentiable_activation_strategy_m.f90 b/src/inference_engine/differentiable_activation_strategy_m.f90 index 3b373bf39..33b62fc23 100644 --- a/src/inference_engine/differentiable_activation_strategy_m.f90 +++ b/src/inference_engine/differentiable_activation_strategy_m.f90 @@ -9,7 +9,8 @@ module differentiable_activation_strategy_m type, extends(activation_strategy_t), abstract :: differentiable_activation_strategy_t contains - procedure(activation_i), nopass, deferred :: activation_derivative + procedure(activation_i), nopass, deferred :: default_real_activation_derivative + generic :: activation_derivative => default_real_activation_derivative end type end module differentiable_activation_strategy_m diff --git a/src/inference_engine/gelu_m.f90 b/src/inference_engine/gelu_m.f90 index 8bd8ab06b..f32078ca8 100644 --- a/src/inference_engine/gelu_m.f90 +++ b/src/inference_engine/gelu_m.f90 @@ -2,7 +2,6 @@ ! Terms of use are as specified in LICENSE.txt module gelu_m use differentiable_activation_strategy_m, only : differentiable_activation_strategy_t - use kind_parameters_m, only : rkind use julienne_string_m, only : string_t implicit none @@ -11,23 +10,23 @@ module gelu_m type, extends(differentiable_activation_strategy_t) :: gelu_t contains - procedure, nopass :: activation - procedure, nopass :: activation_derivative + procedure, nopass :: default_real_activation + procedure, nopass :: default_real_activation_derivative procedure :: function_name end type interface - elemental module function activation(x) result(y) + elemental module function default_real_activation(x) result(y) implicit none - real(rkind), intent(in) :: x - real(rkind) y + real, intent(in) :: x + real y end function - elemental module function activation_derivative(x) result(y) + elemental module function default_real_activation_derivative(x) result(y) implicit none - real(rkind), intent(in) :: x - real(rkind) y + real, intent(in) :: x + real y end function elemental module function function_name(self) result(string) diff --git a/src/inference_engine/gelu_s.f90 b/src/inference_engine/gelu_s.f90 index 416fd3699..586955e08 100644 --- a/src/inference_engine/gelu_s.f90 +++ b/src/inference_engine/gelu_s.f90 @@ -1,21 +1,20 @@ ! Copyright (c), The Regents of the University of California ! Terms of use are as specified in LICENSE.txt submodule(gelu_m) gelu_s - use kind_parameters_m, only : rkind implicit none - real(rkind), parameter :: pi = 3.141592653589793_rkind - real(rkind), parameter :: half = 0.5_rkind, one = 1._rkind, two=2._rkind - real(rkind), parameter :: sqrt_2_pi = sqrt(two*pi), sqrt_2 = sqrt(two) + real, parameter :: pi = 3.141592653589793 + real, parameter :: half = 0.5, one = 1., two=2. + real, parameter :: sqrt_2_pi = sqrt(two*pi), sqrt_2 = sqrt(two) contains - module procedure activation - y = half*x*(one + erf(x/sqrt_2)) + module procedure default_real_activation + y = half*x*(1. + erf(x/sqrt_2)) end procedure - module procedure activation_derivative - y = half*(one + erf(x/sqrt_2)) + x*exp(-x**2/two)/sqrt_2_pi + module procedure default_real_activation_derivative + y = half*(1. + erf(x/sqrt_2)) + x*exp(-x**2/two)/sqrt_2_pi end procedure module procedure function_name diff --git a/src/inference_engine/relu_m.f90 b/src/inference_engine/relu_m.f90 index a303c1280..c0f938785 100644 --- a/src/inference_engine/relu_m.f90 +++ b/src/inference_engine/relu_m.f90 @@ -2,7 +2,6 @@ ! Terms of use are as specified in LICENSE.txt module relu_m use differentiable_activation_strategy_m, only : differentiable_activation_strategy_t - use kind_parameters_m, only : rkind use julienne_string_m, only : string_t implicit none @@ -11,23 +10,23 @@ module relu_m type, extends(differentiable_activation_strategy_t) :: relu_t contains - procedure, nopass :: activation - procedure, nopass :: activation_derivative + procedure, nopass :: default_real_activation + procedure, nopass :: default_real_activation_derivative procedure :: function_name end type interface - elemental module function activation(x) result(y) + elemental module function default_real_activation(x) result(y) implicit none - real(rkind), intent(in) :: x - real(rkind) y + real, intent(in) :: x + real y end function - elemental module function activation_derivative(x) result(y) + elemental module function default_real_activation_derivative(x) result(y) implicit none - real(rkind), intent(in) :: x - real(rkind) y + real, intent(in) :: x + real y end function elemental module function function_name(self) result(string) diff --git a/src/inference_engine/relu_s.f90 b/src/inference_engine/relu_s.f90 index 775b304fe..ec35cc8ab 100644 --- a/src/inference_engine/relu_s.f90 +++ b/src/inference_engine/relu_s.f90 @@ -1,19 +1,16 @@ ! Copyright (c), The Regents of the University of California ! Terms of use are as specified in LICENSE.txt submodule(relu_m) relu_s - use kind_parameters_m, only : rkind implicit none - real(rkind), parameter :: zero = 0._rkind, one = 1._rkind - contains - module procedure activation - y = max(zero, x) + module procedure default_real_activation + y = max(0., x) end procedure - module procedure activation_derivative - y = merge(one, zero, x>zero) + module procedure default_real_activation_derivative + y = merge(1., 0., x>0.) end procedure module procedure function_name diff --git a/src/inference_engine/sigmoid_m.f90 b/src/inference_engine/sigmoid_m.f90 index 67f3a2fcb..bd0626adc 100644 --- a/src/inference_engine/sigmoid_m.f90 +++ b/src/inference_engine/sigmoid_m.f90 @@ -2,7 +2,6 @@ ! Terms of use are as specified in LICENSE.txt module sigmoid_m use differentiable_activation_strategy_m, only : differentiable_activation_strategy_t - use kind_parameters_m, only : rkind use julienne_string_m, only : string_t implicit none @@ -11,23 +10,23 @@ module sigmoid_m type, extends(differentiable_activation_strategy_t) :: sigmoid_t contains - procedure, nopass :: activation - procedure, nopass :: activation_derivative + procedure, nopass :: default_real_activation + procedure, nopass :: default_real_activation_derivative procedure :: function_name end type interface - elemental module function activation(x) result(y) + elemental module function default_real_activation(x) result(y) implicit none - real(rkind), intent(in) :: x - real(rkind) y + real, intent(in) :: x + real y end function - elemental module function activation_derivative(x) result(y) + elemental module function default_real_activation_derivative(x) result(y) implicit none - real(rkind), intent(in) :: x - real(rkind) y + real, intent(in) :: x + real y end function elemental module function function_name(self) result(string) diff --git a/src/inference_engine/sigmoid_s.f90 b/src/inference_engine/sigmoid_s.f90 index 657e51609..fe7988efc 100644 --- a/src/inference_engine/sigmoid_s.f90 +++ b/src/inference_engine/sigmoid_s.f90 @@ -5,11 +5,11 @@ contains - module procedure activation + module procedure default_real_activation y = 1./(1.+exp(-x)) end procedure - module procedure activation_derivative + module procedure default_real_activation_derivative y = exp(-x)/(1.+exp(-x))**2 end procedure diff --git a/src/inference_engine/step_m.f90 b/src/inference_engine/step_m.f90 index 50f180f51..c5a1c9a96 100644 --- a/src/inference_engine/step_m.f90 +++ b/src/inference_engine/step_m.f90 @@ -2,7 +2,6 @@ ! Terms of use are as specified in LICENSE.txt module step_m use activation_strategy_m, only : activation_strategy_t - use kind_parameters_m, only : rkind use julienne_string_m, only : string_t implicit none @@ -11,16 +10,16 @@ module step_m type, extends(activation_strategy_t) :: step_t contains - procedure, nopass :: activation + procedure, nopass :: default_real_activation procedure :: function_name end type interface - elemental module function activation(x) result(y) + elemental module function default_real_activation(x) result(y) implicit none - real(rkind), intent(in) :: x - real(rkind) y + real, intent(in) :: x + real y end function elemental module function function_name(self) result(string) diff --git a/src/inference_engine/step_s.f90 b/src/inference_engine/step_s.f90 index 102824908..643530225 100644 --- a/src/inference_engine/step_s.f90 +++ b/src/inference_engine/step_s.f90 @@ -6,8 +6,8 @@ contains - module procedure activation - y = merge(1._rkind, 0._rkind, x>0._rkind) + module procedure default_real_activation + y = merge(1., 0., x>0.) end procedure module procedure function_name diff --git a/src/inference_engine/swish_m.f90 b/src/inference_engine/swish_m.f90 index 993bcbf1d..31120fde9 100644 --- a/src/inference_engine/swish_m.f90 +++ b/src/inference_engine/swish_m.f90 @@ -2,7 +2,6 @@ ! Terms of use are as specified in LICENSE.txt module swish_m use differentiable_activation_strategy_m, only : differentiable_activation_strategy_t - use kind_parameters_m, only : rkind use julienne_string_m, only : string_t implicit none @@ -11,23 +10,23 @@ module swish_m type, extends(differentiable_activation_strategy_t) :: swish_t contains - procedure, nopass :: activation - procedure, nopass :: activation_derivative + procedure, nopass :: default_real_activation + procedure, nopass :: default_real_activation_derivative procedure :: function_name end type interface - elemental module function activation(x) result(y) + elemental module function default_real_activation(x) result(y) implicit none - real(rkind), intent(in) :: x - real(rkind) y + real, intent(in) :: x + real y end function - elemental module function activation_derivative(x) result(y) + elemental module function default_real_activation_derivative(x) result(y) implicit none - real(rkind), intent(in) :: x - real(rkind) y + real, intent(in) :: x + real y end function elemental module function function_name(self) result(string) diff --git a/src/inference_engine/swish_s.f90 b/src/inference_engine/swish_s.f90 index 410b3b8ca..0d968798b 100644 --- a/src/inference_engine/swish_s.f90 +++ b/src/inference_engine/swish_s.f90 @@ -6,12 +6,12 @@ contains - module procedure activation + module procedure default_real_activation type(sigmoid_t) sigmoid y = x*sigmoid%activation(x) end procedure - module procedure activation_derivative + module procedure default_real_activation_derivative type(sigmoid_t) sigmoid y = sigmoid%activation(x) + x * sigmoid%activation_derivative(x) end procedure From 216e59b8ba0e1f9baf4c8cb0b214cc5e2bbab62e Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Wed, 28 Aug 2024 10:51:24 -0700 Subject: [PATCH 008/105] chore(tensor_map_test): match file name to module --- test/{tensor_range_test_m.F90 => tensor_map_test_m.F90} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename test/{tensor_range_test_m.F90 => tensor_map_test_m.F90} (100%) diff --git a/test/tensor_range_test_m.F90 b/test/tensor_map_test_m.F90 similarity index 100% rename from test/tensor_range_test_m.F90 rename to test/tensor_map_test_m.F90 From 72b6348921fc648812798278dcff732ce6580090 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Wed, 28 Aug 2024 21:03:19 -0700 Subject: [PATCH 009/105] feat(tensor_t):dble-prec constructor/accessor/test --- src/inference_engine/tensor_m.f90 | 32 +++++++++++--- src/inference_engine/tensor_s.f90 | 30 +++++++++---- test/main.F90 | 3 ++ test/tensor_test_m.f90 | 72 +++++++++++++++++++++++++++++++ 4 files changed, 122 insertions(+), 15 deletions(-) create mode 100644 test/tensor_test_m.f90 diff --git a/src/inference_engine/tensor_m.f90 b/src/inference_engine/tensor_m.f90 index d1faee13c..bea936ad3 100644 --- a/src/inference_engine/tensor_m.f90 +++ b/src/inference_engine/tensor_m.f90 @@ -1,7 +1,7 @@ ! Copyright (c), The Regents of the University of California ! Terms of use are as specified in LICENSE.txt module tensor_m - use kind_parameters_m, only : default_real + use kind_parameters_m, only : default_real, double_precision implicit none private @@ -11,34 +11,54 @@ module tensor_m integer, kind :: k = default_real real, allocatable, private :: values_(:) contains - procedure values - procedure num_components + generic :: values => default_real_values, double_precision_values + procedure, private :: default_real_values, double_precision_values + generic :: num_components => default_real_num_components, double_precision_num_components + procedure, private :: default_real_num_components, double_precision_num_components end type interface tensor_t - pure module function construct_from_components(values) result(tensor) + pure module function construct_default_real(values) result(tensor) implicit none real, intent(in) :: values(:) type(tensor_t) tensor end function + pure module function construct_double_precision(values) result(tensor) + implicit none + double precision, intent(in) :: values(:) + type(tensor_t(double_precision)) tensor + end function + end interface interface - pure module function values(self) result(tensor_values) + pure module function default_real_values(self) result(tensor_values) implicit none class(tensor_t), intent(in) :: self real, allocatable :: tensor_values(:) end function - pure module function num_components(self) result(n) + pure module function double_precision_values(self) result(tensor_values) + implicit none + class(tensor_t(double_precision)), intent(in) :: self + double precision, allocatable :: tensor_values(:) + end function + + pure module function default_real_num_components(self) result(n) implicit none class(tensor_t), intent(in) :: self integer n end function + pure module function double_precision_num_components(self) result(n) + implicit none + class(tensor_t(double_precision)), intent(in) :: self + integer n + end function + end interface end module tensor_m diff --git a/src/inference_engine/tensor_s.f90 b/src/inference_engine/tensor_s.f90 index 9760fe767..c0cf3fbb3 100644 --- a/src/inference_engine/tensor_s.f90 +++ b/src/inference_engine/tensor_s.f90 @@ -5,16 +5,28 @@ contains - module procedure construct_from_components - tensor%values_ = values - end procedure + module procedure construct_default_real + tensor%values_ = values + end procedure - module procedure values - tensor_values = self%values_ - end procedure + module procedure construct_double_precision + tensor%values_ = values + end procedure - module procedure num_components - n = size(self%values_) - end procedure + module procedure default_real_values + tensor_values = self%values_ + end procedure + + module procedure double_precision_values + tensor_values = self%values_ + end procedure + + module procedure default_real_num_components + n = size(self%values_) + end procedure + + module procedure double_precision_num_components + n = size(self%values_) + end procedure end submodule tensor_s diff --git a/test/main.F90 b/test/main.F90 index ba9fe4f40..1b7d8d4ec 100644 --- a/test/main.F90 +++ b/test/main.F90 @@ -9,6 +9,7 @@ program main use network_configuration_test_m, only : network_configuration_test_t use training_configuration_test_m, only : training_configuration_test_t use tensor_map_test_m, only : tensor_map_test_t + use tensor_test_m, only : tensor_test_t use julienne_m, only : command_line_t implicit none @@ -20,6 +21,7 @@ program main type(network_configuration_test_t) network_configuration_test type(training_configuration_test_t) training_configuration_test type(tensor_map_test_t) tensor_map_test + type(tensor_test_t) tensor_test real t_start, t_finish integer :: passes=0, tests=0 @@ -44,6 +46,7 @@ program main call metadata_test%report(passes, tests) call training_configuration_test%report(passes, tests) call tensor_map_test%report(passes, tests) + call tensor_test%report(passes, tests) call asymmetric_engine_test%report(passes, tests) call inference_engine_test%report(passes, tests) call trainable_engine_test%report(passes, tests) diff --git a/test/tensor_test_m.f90 b/test/tensor_test_m.f90 new file mode 100644 index 000000000..f7e321504 --- /dev/null +++ b/test/tensor_test_m.f90 @@ -0,0 +1,72 @@ +! Copyright (c), The Regents of the University of California +! Terms of use are as specified in LICENSE.txt +module tensor_test_m + !! Define inference tests and procedures required for reporting results + + ! External dependencies + use kind_parameters_m, only : double_precision + use julienne_m, only : test_t, test_result_t, test_description_t, test_description_substring, string_t, file_t +#ifdef __GFORTRAN__ + use julienne_m, only : test_function_i +#endif + + ! Internal dependencies + use inference_engine_m, only : tensor_t + + implicit none + + private + public :: tensor_test_t + + type, extends(test_t) :: tensor_test_t + contains + procedure, nopass :: subject + procedure, nopass :: results + end type + +contains + + pure function subject() result(specimen) + character(len=:), allocatable :: specimen + specimen = "An tensor_t object" + end function + + function results() result(test_results) + type(test_result_t), allocatable :: test_results(:) + type(test_description_t), allocatable :: test_descriptions(:) + +#ifndef __GFORTRAN__ + test_descriptions = [ & + test_description_t("double-precision construction and value extraction", double_precision_construction) & + ] +#else + procedure(test_function_i), pointer :: double_precision_construction_ptr + + double_precision_construction_ptr => double_precision_construction + + test_descriptions = [ & + test_description_t("double-precision construction and value extraction", double_precision_construction_ptr) & + ] +#endif + associate( & + substring_in_subject => index(subject(), test_description_substring) /= 0, & + substring_in_description => test_descriptions%contains_text(string_t(test_description_substring)) & + ) + test_descriptions = pack(test_descriptions, substring_in_subject .or. substring_in_description) + end associate + test_results = test_descriptions%run() + end function + + function double_precision_construction() result(test_passes) + logical test_passes + type(tensor_t(double_precision)) tensor + double precision, parameter :: tolerance = 1.0D-12 + double precision, parameter :: values(*) = [1.000000000001D-12] + + tensor = tensor_t(values) ! this will fail to compile if no double_precision constructor exists and the + associate(tensor_values => tensor%values()) + test_passes = kind(tensor_values) == double_precision .and. all(abs(tensor_values - values) < tolerance) + end associate + end function + +end module tensor_test_m From dd0cb91995afaab9c8ab019d448b8b1ea57a27b6 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Wed, 28 Aug 2024 21:19:07 -0700 Subject: [PATCH 010/105] feat(step): add double-precision activation --- src/inference_engine/step_m.f90 | 8 +++++++- src/inference_engine/step_s.f90 | 4 ++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/inference_engine/step_m.f90 b/src/inference_engine/step_m.f90 index c5a1c9a96..90abab9cb 100644 --- a/src/inference_engine/step_m.f90 +++ b/src/inference_engine/step_m.f90 @@ -10,7 +10,7 @@ module step_m type, extends(activation_strategy_t) :: step_t contains - procedure, nopass :: default_real_activation + procedure, nopass :: default_real_activation, double_precision_activation procedure :: function_name end type @@ -22,6 +22,12 @@ elemental module function default_real_activation(x) result(y) real y end function + elemental module function double_precision_activation(x) result(y) + implicit none + double precision, intent(in) :: x + double precision y + end function + elemental module function function_name(self) result(string) implicit none class(step_t), intent(in) :: self diff --git a/src/inference_engine/step_s.f90 b/src/inference_engine/step_s.f90 index 643530225..380dbf617 100644 --- a/src/inference_engine/step_s.f90 +++ b/src/inference_engine/step_s.f90 @@ -10,6 +10,10 @@ y = merge(1., 0., x>0.) end procedure + module procedure double_precision_activation + y = merge(1.D0, 0.D0, x>0.D0) + end procedure + module procedure function_name string = string_t("step") end procedure From 6ceabf0d0ecad4563113dd54da7e7eecdc794a7b Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Wed, 28 Aug 2024 21:58:13 -0700 Subject: [PATCH 011/105] feat(sigmoid):double-precision activation/derivat --- src/inference_engine/sigmoid_m.f90 | 16 ++++++++++++++-- src/inference_engine/sigmoid_s.f90 | 8 ++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/inference_engine/sigmoid_m.f90 b/src/inference_engine/sigmoid_m.f90 index bd0626adc..64160bc73 100644 --- a/src/inference_engine/sigmoid_m.f90 +++ b/src/inference_engine/sigmoid_m.f90 @@ -10,8 +10,8 @@ module sigmoid_m type, extends(differentiable_activation_strategy_t) :: sigmoid_t contains - procedure, nopass :: default_real_activation - procedure, nopass :: default_real_activation_derivative + procedure, nopass :: default_real_activation, double_precision_activation + procedure, nopass :: default_real_activation_derivative, double_precision_activation_derivative procedure :: function_name end type @@ -23,12 +23,24 @@ elemental module function default_real_activation(x) result(y) real y end function + elemental module function double_precision_activation(x) result(y) + implicit none + double precision, intent(in) :: x + double precision y + end function + elemental module function default_real_activation_derivative(x) result(y) implicit none real, intent(in) :: x real y end function + elemental module function double_precision_activation_derivative(x) result(y) + implicit none + double precision, intent(in) :: x + double precision y + end function + elemental module function function_name(self) result(string) implicit none class(sigmoid_t), intent(in) :: self diff --git a/src/inference_engine/sigmoid_s.f90 b/src/inference_engine/sigmoid_s.f90 index fe7988efc..ebbb4efbe 100644 --- a/src/inference_engine/sigmoid_s.f90 +++ b/src/inference_engine/sigmoid_s.f90 @@ -9,10 +9,18 @@ y = 1./(1.+exp(-x)) end procedure + module procedure double_precision_activation + y = 1./(1.+exp(-x)) + end procedure + module procedure default_real_activation_derivative y = exp(-x)/(1.+exp(-x))**2 end procedure + module procedure double_precision_activation_derivative + y = exp(-x)/(1.+exp(-x))**2 + end procedure + module procedure function_name string = string_t("sigmoid") end procedure From 0d69cce2aab65ddc297544f805c1e42768d6de96 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Wed, 28 Aug 2024 22:05:27 -0700 Subject: [PATCH 012/105] feat(relu): add double-precision activation/deriv --- src/inference_engine/relu_m.f90 | 16 ++++++++++++++-- src/inference_engine/relu_s.f90 | 8 ++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/inference_engine/relu_m.f90 b/src/inference_engine/relu_m.f90 index c0f938785..d537dee01 100644 --- a/src/inference_engine/relu_m.f90 +++ b/src/inference_engine/relu_m.f90 @@ -10,8 +10,8 @@ module relu_m type, extends(differentiable_activation_strategy_t) :: relu_t contains - procedure, nopass :: default_real_activation - procedure, nopass :: default_real_activation_derivative + procedure, nopass :: default_real_activation, double_precision_activation + procedure, nopass :: default_real_activation_derivative, double_precision_activation_derivative procedure :: function_name end type @@ -23,12 +23,24 @@ elemental module function default_real_activation(x) result(y) real y end function + elemental module function double_precision_activation(x) result(y) + implicit none + double precision, intent(in) :: x + double precision y + end function + elemental module function default_real_activation_derivative(x) result(y) implicit none real, intent(in) :: x real y end function + elemental module function double_precision_activation_derivative(x) result(y) + implicit none + double precision, intent(in) :: x + double precision y + end function + elemental module function function_name(self) result(string) implicit none class(relu_t), intent(in) :: self diff --git a/src/inference_engine/relu_s.f90 b/src/inference_engine/relu_s.f90 index ec35cc8ab..29c3980e5 100644 --- a/src/inference_engine/relu_s.f90 +++ b/src/inference_engine/relu_s.f90 @@ -9,10 +9,18 @@ y = max(0., x) end procedure + module procedure double_precision_activation + y = max(0.D0, x) + end procedure + module procedure default_real_activation_derivative y = merge(1., 0., x>0.) end procedure + module procedure double_precision_activation_derivative + y = merge(1.D0, 0.D0, x>0.D0) + end procedure + module procedure function_name string = string_t("relu") end procedure From 59547e7f3d79177ddc804a604714e7ba1a132f19 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Wed, 28 Aug 2024 22:41:12 -0700 Subject: [PATCH 013/105] feat(gelu): add double-prec activation/derivatie --- src/inference_engine/gelu_m.f90 | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/inference_engine/gelu_m.f90 b/src/inference_engine/gelu_m.f90 index f32078ca8..51a872c46 100644 --- a/src/inference_engine/gelu_m.f90 +++ b/src/inference_engine/gelu_m.f90 @@ -10,8 +10,8 @@ module gelu_m type, extends(differentiable_activation_strategy_t) :: gelu_t contains - procedure, nopass :: default_real_activation - procedure, nopass :: default_real_activation_derivative + procedure, nopass :: default_real_activation, double_precision_activation + procedure, nopass :: default_real_activation_derivative, double_precision_activation_derivative procedure :: function_name end type @@ -23,12 +23,24 @@ elemental module function default_real_activation(x) result(y) real y end function + elemental module function double_precision_activation(x) result(y) + implicit none + double precision, intent(in) :: x + double precision y + end function + elemental module function default_real_activation_derivative(x) result(y) implicit none real, intent(in) :: x real y end function + elemental module function double_precision_activation_derivative(x) result(y) + implicit none + double precision, intent(in) :: x + double precision y + end function + elemental module function function_name(self) result(string) implicit none class(gelu_t), intent(in) :: self From fc54a18de2ac829987f2089957d2878a2f9d2b71 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Wed, 28 Aug 2024 23:17:26 -0700 Subject: [PATCH 014/105] feat: finish double-prec activation/derivs --- src/inference_engine/activation_strategy_m.f90 | 16 ++++++++++++---- .../differentiable_activation_strategy_m.f90 | 7 ++++--- src/inference_engine/gelu_s.f90 | 16 ++++++++++++++-- src/inference_engine/swish_m.f90 | 18 +++++++++++++++--- src/inference_engine/swish_s.f90 | 10 ++++++++++ 5 files changed, 55 insertions(+), 12 deletions(-) diff --git a/src/inference_engine/activation_strategy_m.f90 b/src/inference_engine/activation_strategy_m.f90 index 80b754882..8bca0a026 100644 --- a/src/inference_engine/activation_strategy_m.f90 +++ b/src/inference_engine/activation_strategy_m.f90 @@ -8,24 +8,32 @@ module activation_strategy_m private public :: activation_strategy_t - public :: activation_i + public :: default_real_activation_i + public :: double_precision_activation_i public :: function_name_i type, abstract :: activation_strategy_t contains - procedure(activation_i), nopass, deferred :: default_real_activation - generic :: activation => default_real_activation + procedure(default_real_activation_i), nopass, deferred :: default_real_activation + procedure(double_precision_activation_i), nopass, deferred :: double_precision_activation + generic :: activation => default_real_activation, double_precision_activation procedure(function_name_i), deferred :: function_name end type abstract interface - elemental function activation_i(x) result(y) + elemental function default_real_activation_i(x) result(y) implicit none real, intent(in) :: x real y end function + elemental function double_precision_activation_i(x) result(y) + implicit none + double precision, intent(in) :: x + double precision y + end function + elemental function function_name_i(self) result(string) import string_t, activation_strategy_t implicit none diff --git a/src/inference_engine/differentiable_activation_strategy_m.f90 b/src/inference_engine/differentiable_activation_strategy_m.f90 index 33b62fc23..b1e226472 100644 --- a/src/inference_engine/differentiable_activation_strategy_m.f90 +++ b/src/inference_engine/differentiable_activation_strategy_m.f90 @@ -1,7 +1,7 @@ ! Copyright (c), The Regents of the University of California ! Terms of use are as specified in LICENSE.txt module differentiable_activation_strategy_m - use activation_strategy_m, only : activation_strategy_t, activation_i + use activation_strategy_m, only : activation_strategy_t, default_real_activation_i, double_precision_activation_i implicit none private @@ -9,8 +9,9 @@ module differentiable_activation_strategy_m type, extends(activation_strategy_t), abstract :: differentiable_activation_strategy_t contains - procedure(activation_i), nopass, deferred :: default_real_activation_derivative - generic :: activation_derivative => default_real_activation_derivative + procedure(default_real_activation_i), nopass, deferred :: default_real_activation_derivative + procedure(double_precision_activation_i), nopass, deferred :: double_precision_activation_derivative + generic :: activation_derivative => default_real_activation_derivative, double_precision_activation_derivative end type end module differentiable_activation_strategy_m diff --git a/src/inference_engine/gelu_s.f90 b/src/inference_engine/gelu_s.f90 index 586955e08..c684a1721 100644 --- a/src/inference_engine/gelu_s.f90 +++ b/src/inference_engine/gelu_s.f90 @@ -4,8 +4,12 @@ implicit none real, parameter :: pi = 3.141592653589793 - real, parameter :: half = 0.5, one = 1., two=2. - real, parameter :: sqrt_2_pi = sqrt(two*pi), sqrt_2 = sqrt(two) + real, parameter :: half = 0.5, two = 2.D0 + real, parameter :: sqrt_2_pi = sqrt(2*pi), sqrt_2 = sqrt(2.) + + real, parameter :: pi_dp = 3.141592653589793D0 + real, parameter :: half_dp = 0.5D0, two_dp = 2.D0 + real, parameter :: sqrt_2_pi_dp = sqrt(two_dp*pi_dp), sqrt_2_dp = sqrt(2.D0) contains @@ -13,10 +17,18 @@ y = half*x*(1. + erf(x/sqrt_2)) end procedure + module procedure double_precision_activation + y = half_dp*x*(1.D0 + erf(x/sqrt_2_dp)) + end procedure + module procedure default_real_activation_derivative y = half*(1. + erf(x/sqrt_2)) + x*exp(-x**2/two)/sqrt_2_pi end procedure + module procedure double_precision_activation_derivative + y = half_dp*(1.D0 + erf(x/sqrt_2_dp)) + x*exp(-x**2/2.D0)/sqrt_2_pi_dp + end procedure + module procedure function_name string = string_t("gelu") end procedure diff --git a/src/inference_engine/swish_m.f90 b/src/inference_engine/swish_m.f90 index 31120fde9..476764004 100644 --- a/src/inference_engine/swish_m.f90 +++ b/src/inference_engine/swish_m.f90 @@ -10,8 +10,8 @@ module swish_m type, extends(differentiable_activation_strategy_t) :: swish_t contains - procedure, nopass :: default_real_activation - procedure, nopass :: default_real_activation_derivative + procedure, nopass :: default_real_activation , double_precision_activation + procedure, nopass :: default_real_activation_derivative, double_precision_activation_derivative procedure :: function_name end type @@ -23,12 +23,24 @@ elemental module function default_real_activation(x) result(y) real y end function + elemental module function double_precision_activation(x) result(y) + implicit none + double precision, intent(in) :: x + double precision y + end function + elemental module function default_real_activation_derivative(x) result(y) implicit none - real, intent(in) :: x + real , intent(in) :: x real y end function + elemental module function double_precision_activation_derivative(x) result(y) + implicit none + double precision , intent(in) :: x + double precision y + end function + elemental module function function_name(self) result(string) implicit none class(swish_t), intent(in) :: self diff --git a/src/inference_engine/swish_s.f90 b/src/inference_engine/swish_s.f90 index 0d968798b..d85f020cf 100644 --- a/src/inference_engine/swish_s.f90 +++ b/src/inference_engine/swish_s.f90 @@ -11,11 +11,21 @@ y = x*sigmoid%activation(x) end procedure + module procedure double_precision_activation + type(sigmoid_t) sigmoid + y = x*sigmoid%activation(x) + end procedure + module procedure default_real_activation_derivative type(sigmoid_t) sigmoid y = sigmoid%activation(x) + x * sigmoid%activation_derivative(x) end procedure + module procedure double_precision_activation_derivative + type(sigmoid_t) sigmoid + y = sigmoid%activation(x) + x * sigmoid%activation_derivative(x) + end procedure + module procedure function_name string = string_t("swish") end procedure From 84d21f710641c655fd39aadd875a15563db47b3d Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Wed, 28 Aug 2024 23:35:55 -0700 Subject: [PATCH 015/105] refac(tensor_map):mk type-bound proc names generic --- src/inference_engine/tensor_map_m.f90 | 27 ++++++++++++++++++--------- src/inference_engine/tensor_map_s.f90 | 21 ++++++++++++++------- 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/src/inference_engine/tensor_map_m.f90 b/src/inference_engine/tensor_map_m.f90 index 247e4d86e..49660c613 100644 --- a/src/inference_engine/tensor_map_m.f90 +++ b/src/inference_engine/tensor_map_m.f90 @@ -3,7 +3,7 @@ module tensor_map_m use tensor_m, only : tensor_t use julienne_m, only : string_t - use kind_parameters_m, only : default_real + use kind_parameters_m, only : default_real, double_precision implicit none private @@ -14,23 +14,32 @@ module tensor_map_m character(len=:), allocatable, private :: layer_ real(k), dimension(:), allocatable, private :: intercept_, slope_ contains - procedure map_to_training_range - procedure map_from_training_range + generic :: map_to_training_range => default_real_map_to_training_range + procedure, private :: default_real_map_to_training_range + generic :: map_from_training_range => default_real_map_from_training_range + procedure, private :: default_real_map_from_training_range procedure to_json - generic :: operator(==) => equals - procedure, private :: equals + generic :: operator(==) => default_real_equals + procedure, private :: default_real_equals end type interface tensor_map_t - pure module function from_component_ranges(layer, minima, maxima) result(tensor_map) + pure module function construct_default_real(layer, minima, maxima) result(tensor_map) implicit none character(len=*), intent(in) :: layer real, dimension(:), intent(in) :: minima, maxima type(tensor_map_t) tensor_map end function + pure module function construct_double_precision(layer, minima, maxima) result(tensor_map) + implicit none + character(len=*), intent(in) :: layer + double precision, dimension(:), intent(in) :: minima, maxima + type(tensor_map_t(double_precision)) tensor_map + end function + module function from_json(lines) result(tensor_map) implicit none type(string_t), intent(in) :: lines(:) @@ -41,14 +50,14 @@ module function from_json(lines) result(tensor_map) interface - elemental module function map_to_training_range(self, tensor) result(normalized_tensor) + elemental module function default_real_map_to_training_range(self, tensor) result(normalized_tensor) implicit none class(tensor_map_t), intent(in) :: self type(tensor_t), intent(in) :: tensor type(tensor_t) normalized_tensor end function - elemental module function map_from_training_range(self, tensor) result(unnormalized_tensor) + elemental module function default_real_map_from_training_range(self, tensor) result(unnormalized_tensor) implicit none class(tensor_map_t), intent(in) :: self type(tensor_t), intent(in) :: tensor @@ -61,7 +70,7 @@ pure module function to_json(self) result(lines) type(string_t), allocatable :: lines(:) end function - elemental module function equals(lhs, rhs) result(lhs_equals_rhs) + elemental module function default_real_equals(lhs, rhs) result(lhs_equals_rhs) implicit none class(tensor_map_t), intent(in) :: lhs, rhs logical lhs_equals_rhs diff --git a/src/inference_engine/tensor_map_s.f90 b/src/inference_engine/tensor_map_s.f90 index dd52ab364..e02fc6675 100644 --- a/src/inference_engine/tensor_map_s.f90 +++ b/src/inference_engine/tensor_map_s.f90 @@ -3,13 +3,20 @@ submodule(tensor_map_m) tensor_map_s use assert_m, only : assert use julienne_m, only : separated_values - use kind_parameters_m, only : rkind + use kind_parameters_m, only : default_real implicit none contains - module procedure from_component_ranges - call assert(size(minima)==size(maxima),"tensor_map_s(from_components): size(minima)==size(maxima)") + module procedure construct_default_real + call assert(size(minima)==size(maxima),"tensor_map_s(construct_default_real): size(minima)==size(maxima)") + tensor_map%layer_ = layer + tensor_map%intercept_ = minima + tensor_map%slope_ = maxima - minima + end procedure + + module procedure construct_double_precision + call assert(size(minima)==size(maxima),"tensor_map_s(construct_double_precision): size(minima)==size(maxima)") tensor_map%layer_ = layer tensor_map%intercept_ = minima tensor_map%slope_ = maxima - minima @@ -34,7 +41,7 @@ call assert(tensor_map_key_found, "tensor_map_s(from_json): 'tensor_map' key found") end procedure - module procedure equals + module procedure default_real_equals real, parameter :: tolerance = 1.E-08 call assert(allocated(lhs%layer_) .and. allocated(rhs%layer_), "tensor_map_s(equals): allocated layer_ components") @@ -57,7 +64,7 @@ call assert(allocated(self%layer_), "tensor_map_s(to_json): allocated layer_") call assert(allocated(self%intercept_) .and. allocated(self%slope_), "tensor_map_s(to_json): allocated intercept_/slope_") - csv_format = separated_values(separator=",", mold=[real(rkind)::]) + csv_format = separated_values(separator=",", mold=[real(default_real)::]) allocate(character(len=size(self%intercept_)*(characters_per_value+1)-1)::intercept_string) allocate(character(len=size(self%slope_)*(characters_per_value+1)-1)::slope_string) write(intercept_string, fmt = csv_format) self%intercept_ @@ -75,7 +82,7 @@ end block end procedure - module procedure map_to_training_range + module procedure default_real_map_to_training_range associate(tensor_values => tensor%values()) associate(normalized_values => (tensor_values - self%intercept_)/self%slope_) normalized_tensor = tensor_t(normalized_values) @@ -83,7 +90,7 @@ end associate end procedure - module procedure map_from_training_range + module procedure default_real_map_from_training_range associate(tensor_values => tensor%values()) associate(unnormalized_values => self%intercept_ + tensor_values*self%slope_) unnormalized_tensor = tensor_t(unnormalized_values) From d878713ede301c1c5d3473c9abff117252ec759b Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Wed, 28 Aug 2024 23:56:26 -0700 Subject: [PATCH 016/105] feat(tensor_map): add double-precision procedures This commit adds double-precision versions of all tensor_map_t type-bound and constructor procedures except the default_real_from_json constructor function. --- src/inference_engine/tensor_map_m.f90 | 43 ++++++++++--- src/inference_engine/tensor_map_s.f90 | 89 ++++++++++++++++++++++++--- 2 files changed, 116 insertions(+), 16 deletions(-) diff --git a/src/inference_engine/tensor_map_m.f90 b/src/inference_engine/tensor_map_m.f90 index 49660c613..792327f00 100644 --- a/src/inference_engine/tensor_map_m.f90 +++ b/src/inference_engine/tensor_map_m.f90 @@ -14,13 +14,14 @@ module tensor_map_m character(len=:), allocatable, private :: layer_ real(k), dimension(:), allocatable, private :: intercept_, slope_ contains - generic :: map_to_training_range => default_real_map_to_training_range - procedure, private :: default_real_map_to_training_range - generic :: map_from_training_range => default_real_map_from_training_range - procedure, private :: default_real_map_from_training_range - procedure to_json - generic :: operator(==) => default_real_equals - procedure, private :: default_real_equals + generic :: map_to_training_range => default_real_map_to_training_range, double_precision_map_to_training_range + procedure, private :: default_real_map_to_training_range, double_precision_map_to_training_range + generic :: map_from_training_range => default_real_map_from_training_range, double_precision_map_from_training_range + procedure, private :: default_real_map_from_training_range, double_precision_map_from_training_range + generic :: to_json => default_real_to_json, double_precision_to_json + procedure, private :: default_real_to_json, double_precision_to_json + generic :: operator(==) => default_real_equals, double_precision_equals + procedure, private :: default_real_equals, double_precision_equals end type @@ -57,6 +58,13 @@ elemental module function default_real_map_to_training_range(self, tensor) resul type(tensor_t) normalized_tensor end function + elemental module function double_precision_map_to_training_range(self, tensor) result(normalized_tensor) + implicit none + class(tensor_map_t(double_precision)), intent(in) :: self + type(tensor_t(double_precision)), intent(in) :: tensor + type(tensor_t(double_precision)) normalized_tensor + end function + elemental module function default_real_map_from_training_range(self, tensor) result(unnormalized_tensor) implicit none class(tensor_map_t), intent(in) :: self @@ -64,18 +72,37 @@ elemental module function default_real_map_from_training_range(self, tensor) res type(tensor_t) unnormalized_tensor end function - pure module function to_json(self) result(lines) + elemental module function double_precision_map_from_training_range(self, tensor) result(unnormalized_tensor) + implicit none + class(tensor_map_t(double_precision)), intent(in) :: self + type(tensor_t(double_precision)), intent(in) :: tensor + type(tensor_t(double_precision)) unnormalized_tensor + end function + + pure module function default_real_to_json(self) result(lines) implicit none class(tensor_map_t), intent(in) :: self type(string_t), allocatable :: lines(:) end function + pure module function double_precision_to_json(self) result(lines) + implicit none + class(tensor_map_t(double_precision)), intent(in) :: self + type(string_t), allocatable :: lines(:) + end function + elemental module function default_real_equals(lhs, rhs) result(lhs_equals_rhs) implicit none class(tensor_map_t), intent(in) :: lhs, rhs logical lhs_equals_rhs end function + elemental module function double_precision_equals(lhs, rhs) result(lhs_equals_rhs) + implicit none + class(tensor_map_t(double_precision)), intent(in) :: lhs, rhs + logical lhs_equals_rhs + end function + end interface end module tensor_map_m diff --git a/src/inference_engine/tensor_map_s.f90 b/src/inference_engine/tensor_map_s.f90 index e02fc6675..92ea17daa 100644 --- a/src/inference_engine/tensor_map_s.f90 +++ b/src/inference_engine/tensor_map_s.f90 @@ -44,11 +44,16 @@ module procedure default_real_equals real, parameter :: tolerance = 1.E-08 - call assert(allocated(lhs%layer_) .and. allocated(rhs%layer_), "tensor_map_s(equals): allocated layer_ components") - call assert(allocated(lhs%intercept_) .and. allocated(rhs%intercept_), "tensor_map_s(equals): allocated intercept_ components)") - call assert(allocated(lhs%slope_) .and. allocated(rhs%slope_), "tensor_map_s(equals): allocated slope_ components)") - call assert(size(lhs%intercept_) == size(rhs%intercept_), "tensor_map_s(equals): size(lhs%intercept_) == size(rhs%intercept_)") - call assert(size(lhs%slope_) == size(rhs%slope_), "tensor_map_s(equals): size(lhs%slope_) == size(rhs%slope_)") + call assert(allocated(lhs%layer_) .and. allocated(rhs%layer_), & + "tensor_map_s(default_real_equals): allocated layer_ components") + call assert(allocated(lhs%intercept_) .and. allocated(rhs%intercept_), & + "tensor_map_s(default_real_equals): allocated intercept_ components)") + call assert(allocated(lhs%slope_) .and. allocated(rhs%slope_), & + "tensor_map_s(default_real_equals): allocated slope_ components)") + call assert(size(lhs%intercept_) == size(rhs%intercept_), & + "tensor_map_s(default_real_equals): size(lhs%intercept_) == size(rhs%intercept_)") + call assert(size(lhs%slope_) == size(rhs%slope_), & + "tensor_map_s(default_real_equals): size(lhs%slope_) == size(rhs%slope_)") lhs_equals_rhs = & lhs%layer_ == rhs%layer_ .and. & @@ -56,19 +61,71 @@ all(abs(lhs%slope_ - rhs%slope_) <= tolerance) end procedure - module procedure to_json + module procedure double_precision_equals + double precision, parameter :: tolerance = 1.D-015 + + call assert(allocated(lhs%layer_) .and. allocated(rhs%layer_), & + "tensor_map_s(double_precision_equals): allocated layer_ components") + call assert(allocated(lhs%intercept_) .and. allocated(rhs%intercept_), & + "tensor_map_s(double_precision_equals): allocated intercept_ components)") + call assert(allocated(lhs%slope_) .and. allocated(rhs%slope_), & + "tensor_map_s(double_precision_equals): allocated slope_ components)") + call assert(size(lhs%intercept_) == size(rhs%intercept_), & + "tensor_map_s(double_precision_equals): size(lhs%intercept_) == size(rhs%intercept_)") + call assert(size(lhs%slope_) == size(rhs%slope_), & + "tensor_map_s(double_precision_equals): size(lhs%slope_) == size(rhs%slope_)") + + lhs_equals_rhs = & + lhs%layer_ == rhs%layer_ .and. & + all(abs(lhs%intercept_ - rhs%intercept_) <= tolerance).and. & + all(abs(lhs%slope_ - rhs%slope_) <= tolerance) + end procedure + + module procedure default_real_to_json integer, parameter :: characters_per_value=17 character(len=*), parameter :: indent = repeat(" ",ncopies=4) character(len=:), allocatable :: csv_format, intercept_string, slope_string - call assert(allocated(self%layer_), "tensor_map_s(to_json): allocated layer_") - call assert(allocated(self%intercept_) .and. allocated(self%slope_), "tensor_map_s(to_json): allocated intercept_/slope_") + call assert(allocated(self%layer_), & + "tensor_map_s(default_real_to_json): allocated layer_") + call assert(allocated(self%intercept_) .and. allocated(self%slope_), & + "tensor_map_s(default_real_to_json): allocated intercept_/slope_") csv_format = separated_values(separator=",", mold=[real(default_real)::]) allocate(character(len=size(self%intercept_)*(characters_per_value+1)-1)::intercept_string) allocate(character(len=size(self%slope_)*(characters_per_value+1)-1)::slope_string) write(intercept_string, fmt = csv_format) self%intercept_ write(slope_string, fmt = csv_format) self%slope_ + + block + character(len=:), allocatable :: layer + layer = trim(adjustl(self%layer_)) + lines = [ & + string_t(indent // '"'//layer//'_map": {'), & + string_t(indent // ' "layer": "' // layer // '",'), & + string_t(indent // ' "intercept": [' // trim(adjustl(intercept_string)) // '],'), & + string_t(indent // ' "slope": [' // trim(adjustl(slope_string)) // ']'), & + string_t(indent // '}') & + ] + end block + end procedure + + module procedure double_precision_to_json + integer, parameter :: characters_per_value=34 + character(len=*), parameter :: indent = repeat(" ",ncopies=4) + character(len=:), allocatable :: csv_format, intercept_string, slope_string + + call assert(allocated(self%layer_), & + "tensor_map_s(default_real_to_json): allocated layer_") + call assert(allocated(self%intercept_) .and. allocated(self%slope_), & + "tensor_map_s(default_real_to_json): allocated intercept_/slope_") + + csv_format = separated_values(separator=",", mold=[double precision::]) + allocate(character(len=size(self%intercept_)*(characters_per_value+1)-1)::intercept_string) + allocate(character(len=size(self%slope_)*(characters_per_value+1)-1)::slope_string) + write(intercept_string, fmt = csv_format) self%intercept_ + write(slope_string, fmt = csv_format) self%slope_ + block character(len=:), allocatable :: layer layer = trim(adjustl(self%layer_)) @@ -90,6 +147,14 @@ end associate end procedure + module procedure double_precision_map_to_training_range + associate(tensor_values => tensor%values()) + associate(normalized_values => (tensor_values - self%intercept_)/self%slope_) + normalized_tensor = tensor_t(normalized_values) + end associate + end associate + end procedure + module procedure default_real_map_from_training_range associate(tensor_values => tensor%values()) associate(unnormalized_values => self%intercept_ + tensor_values*self%slope_) @@ -98,4 +163,12 @@ end associate end procedure + module procedure double_precision_map_from_training_range + associate(tensor_values => tensor%values()) + associate(unnormalized_values => self%intercept_ + tensor_values*self%slope_) + unnormalized_tensor = tensor_t(unnormalized_values) + end associate + end associate + end procedure + end submodule tensor_map_s From d011f0dd132a8a37324864cfac5e73dde5116630 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Thu, 29 Aug 2024 00:13:25 -0700 Subject: [PATCH 017/105] refac(neuron): mk procedure names generic bindings --- src/inference_engine/neuron_m.f90 | 30 ++++++++++++++++++------------ src/inference_engine/neuron_s.f90 | 12 ++++++------ 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/src/inference_engine/neuron_m.f90 b/src/inference_engine/neuron_m.f90 index 45dcc652b..4042bb6f6 100644 --- a/src/inference_engine/neuron_m.f90 +++ b/src/inference_engine/neuron_m.f90 @@ -15,12 +15,18 @@ module neuron_m real(k), private :: bias_ type(neuron_t(k)), allocatable, private :: next contains - procedure :: to_json - procedure :: weights - procedure :: bias - procedure :: next_allocated - procedure :: next_pointer - procedure :: num_inputs + generic :: to_json => default_real_to_json + procedure, private :: default_real_to_json + generic :: weights => default_real_weights + procedure, private :: default_real_weights + generic :: bias => default_real_bias + procedure, private :: default_real_bias + generic :: next_allocated => default_real_next_allocated + procedure, private :: default_real_next_allocated + generic :: next_pointer => default_real_next_pointer + procedure, private :: default_real_next_pointer + generic :: num_inputs => default_real_num_inputs + procedure, private :: default_real_num_inputs end type interface neuron_t @@ -44,37 +50,37 @@ pure module function from_components(weights, bias) result(neuron) interface - pure module function to_json(self) result(lines) + pure module function default_real_to_json(self) result(lines) implicit none class(neuron_t), intent(in) :: self type(string_t), allocatable :: lines(:) end function - module function weights(self) result(my_weights) + module function default_real_weights(self) result(my_weights) implicit none class(neuron_t), intent(in) :: self real, allocatable :: my_weights(:) end function - module function bias(self) result(my_bias) + module function default_real_bias(self) result(my_bias) implicit none class(neuron_t), intent(in) :: self real my_bias end function - module function next_allocated(self) result(next_is_allocated) + module function default_real_next_allocated(self) result(next_is_allocated) implicit none class(neuron_t), intent(in) :: self logical next_is_allocated end function - module function next_pointer(self) result(next_ptr) + module function default_real_next_pointer(self) result(next_ptr) implicit none class(neuron_t), intent(in), target :: self type(neuron_t), pointer :: next_ptr end function - pure module function num_inputs(self) result(size_weights) + pure module function default_real_num_inputs(self) result(size_weights) implicit none class(neuron_t), intent(in) :: self integer size_weights diff --git a/src/inference_engine/neuron_s.f90 b/src/inference_engine/neuron_s.f90 index eb195a38f..2fd1f3f0c 100644 --- a/src/inference_engine/neuron_s.f90 +++ b/src/inference_engine/neuron_s.f90 @@ -7,7 +7,7 @@ contains - module procedure to_json + module procedure default_real_to_json integer, parameter :: characters_per_value=17 character(len=*), parameter :: indent = repeat(" ",ncopies=12) character(len=:), allocatable :: csv_format, weights_string, bias_string @@ -67,19 +67,19 @@ neuron%bias_ = bias end procedure - module procedure weights + module procedure default_real_weights my_weights = self%weights_ end procedure - module procedure bias + module procedure default_real_bias my_bias = self%bias_ end procedure - module procedure next_allocated + module procedure default_real_next_allocated next_is_allocated = allocated(self%next) end procedure - module procedure next_pointer + module procedure default_real_next_pointer if (allocated(self%next)) then next_ptr => self%next else @@ -87,7 +87,7 @@ end if end procedure - module procedure num_inputs + module procedure default_real_num_inputs size_weights = size(self%weights_) end procedure From c496147cfc9524a5ce54ddc88edf65cb8ac38d98 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Thu, 29 Aug 2024 00:20:44 -0700 Subject: [PATCH 018/105] refac(layer):make procedure names generic bindings --- src/inference_engine/layer_m.f90 | 37 +++++++++++++++++++------------- src/inference_engine/layer_s.f90 | 20 ++++++++--------- 2 files changed, 32 insertions(+), 25 deletions(-) diff --git a/src/inference_engine/layer_m.f90 b/src/inference_engine/layer_m.f90 index 788cf2f63..7842fd834 100644 --- a/src/inference_engine/layer_m.f90 +++ b/src/inference_engine/layer_m.f90 @@ -17,18 +17,25 @@ module layer_m type(neuron_t(k)), private :: neuron !! linked list of this layer's neurons type(layer_t(k)), allocatable, private :: next !! next layer contains - procedure :: inference_engine - procedure :: count_layers - procedure :: count_neurons - procedure :: count_inputs - procedure :: neurons_per_layer - procedure :: next_allocated - procedure :: next_pointer + generic :: inference_engine => default_real_inference_engine + procedure, private :: default_real_inference_engine + generic :: count_layers => default_real_count_layers + procedure, private :: default_real_count_layers + generic :: count_neurons => default_real_count_neurons + procedure, private :: default_real_count_neurons + generic :: count_inputs => default_real_count_inputs + procedure, private :: default_real_count_inputs + generic :: neurons_per_layer => default_real_neurons_per_layer + procedure, private :: default_real_neurons_per_layer + generic :: next_allocated => default_real_next_allocated + procedure, private :: default_real_next_allocated + generic :: next_pointer => default_real_next_pointer + procedure, private :: default_real_next_pointer end type interface layer_t - recursive module function construct_layer(layer_lines, start) result(layer) + recursive module function default_real_construct_layer(layer_lines, start) result(layer) !! construct a linked list of layer_t objects from an array of JSON-formatted text lines implicit none type(string_t), intent(in) :: layer_lines(:) @@ -40,7 +47,7 @@ recursive module function construct_layer(layer_lines, start) result(layer) interface - module function inference_engine(hidden_layers, metadata, output_layer, input_map, output_map) result(inference_engine_) + module function default_real_inference_engine(hidden_layers, metadata, output_layer, input_map, output_map) result(inference_engine_) implicit none class(layer_t), intent(in), target :: hidden_layers type(layer_t), intent(in), target :: output_layer @@ -49,37 +56,37 @@ module function inference_engine(hidden_layers, metadata, output_layer, input_ma type(inference_engine_t) inference_engine_ end function - module function count_layers(layer) result(num_layers) + module function default_real_count_layers(layer) result(num_layers) implicit none class(layer_t), intent(in), target :: layer integer num_layers end function - module function count_neurons(layer) result(neurons_per_layer_result) + module function default_real_count_neurons(layer) result(neurons_per_layer_result) implicit none class(layer_t), intent(in), target :: layer integer, allocatable :: neurons_per_layer_result(:) end function - module function count_inputs(layer) result(num_inputs) + module function default_real_count_inputs(layer) result(num_inputs) implicit none class(layer_t), intent(in) :: layer integer num_inputs end function - module function neurons_per_layer(self) result(num_neurons) + module function default_real_neurons_per_layer(self) result(num_neurons) implicit none class(layer_t), intent(in), target :: self integer num_neurons end function - module function next_allocated(self) result(next_is_allocated) + module function default_real_next_allocated(self) result(next_is_allocated) implicit none class(layer_t), intent(in) :: self logical next_is_allocated end function - module function next_pointer(self) result(next_ptr) + module function default_real_next_pointer(self) result(next_ptr) implicit none class(layer_t), intent(in), target :: self type(layer_t), pointer :: next_ptr diff --git a/src/inference_engine/layer_s.f90 b/src/inference_engine/layer_s.f90 index 1dc847d74..d08b1b689 100644 --- a/src/inference_engine/layer_s.f90 +++ b/src/inference_engine/layer_s.f90 @@ -7,7 +7,7 @@ contains - module procedure construct_layer + module procedure default_real_construct_layer type(neuron_t), pointer :: neuron integer num_inputs, neurons_in_layer @@ -34,11 +34,11 @@ line = trim(adjustl(layer_lines(start+4*neurons_in_layer+1)%string())) call assert(line(1:1)==']', "read_layer_list: hidden layer end") - if (line(len(line):len(line)) == ",") layer%next = construct_layer(layer_lines, start+4*neurons_in_layer+2) + if (line(len(line):len(line)) == ",") layer%next = default_real_construct_layer(layer_lines, start+4*neurons_in_layer+2) end procedure - module procedure inference_engine + module procedure default_real_inference_engine associate( & num_inputs => hidden_layers%count_inputs(), & @@ -47,7 +47,7 @@ num_hidden_layers => hidden_layers%count_layers(), & num_output_layers => output_layer%count_layers() & ) - call assert(num_output_layers==1, "inference_engine_s(construct_from_json): 1 output layer", num_output_layers) + call assert(num_output_layers==1, "inference_engine_s(default_real_construct_from_json): 1 output layer", num_output_layers) associate(nodes => [num_inputs, neurons_per_hidden_layer, num_outputs]) associate(n_max => maxval(nodes)) @@ -110,7 +110,7 @@ end procedure - module procedure count_layers + module procedure default_real_count_layers type(layer_t), pointer :: layer_ptr @@ -124,7 +124,7 @@ end procedure - module procedure count_neurons + module procedure default_real_count_neurons type(layer_t), pointer :: layer_ptr type(neuron_t), pointer :: neuron_ptr @@ -149,11 +149,11 @@ end procedure - module procedure count_inputs + module procedure default_real_count_inputs num_inputs = layer%neuron%num_inputs() ! assume fully connected input layer end procedure - module procedure neurons_per_layer + module procedure default_real_neurons_per_layer type(neuron_t), pointer :: neuron @@ -167,11 +167,11 @@ end procedure - module procedure next_allocated + module procedure default_real_next_allocated next_is_allocated = allocated(self%next) end procedure - module procedure next_pointer + module procedure default_real_next_pointer if (allocated(self%next)) then next_ptr => self%next else From 85fba964ededb281f3c81776e8b6193e7ca54569 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Thu, 29 Aug 2024 00:58:17 -0700 Subject: [PATCH 019/105] feat(neuron_t): add double-precision procedures --- src/inference_engine/neuron_m.f90 | 71 +++++++++++++++++++++++++------ src/inference_engine/neuron_s.f90 | 51 +++++++++++++++++++++- 2 files changed, 107 insertions(+), 15 deletions(-) diff --git a/src/inference_engine/neuron_m.f90 b/src/inference_engine/neuron_m.f90 index 4042bb6f6..b078a7b29 100644 --- a/src/inference_engine/neuron_m.f90 +++ b/src/inference_engine/neuron_m.f90 @@ -2,7 +2,7 @@ ! Terms of use are as specified in LICENSE.txt module neuron_m use julienne_string_m, only : string_t - use kind_parameters_m, only : default_real + use kind_parameters_m, only : default_real, double_precision implicit none private @@ -15,18 +15,18 @@ module neuron_m real(k), private :: bias_ type(neuron_t(k)), allocatable, private :: next contains - generic :: to_json => default_real_to_json - procedure, private :: default_real_to_json - generic :: weights => default_real_weights - procedure, private :: default_real_weights - generic :: bias => default_real_bias - procedure, private :: default_real_bias - generic :: next_allocated => default_real_next_allocated - procedure, private :: default_real_next_allocated - generic :: next_pointer => default_real_next_pointer - procedure, private :: default_real_next_pointer - generic :: num_inputs => default_real_num_inputs - procedure, private :: default_real_num_inputs + generic :: to_json => default_real_to_json, double_precision_to_json + procedure, private :: default_real_to_json, double_precision_to_json + generic :: weights => default_real_weights, double_precision_weights + procedure, private :: default_real_weights, double_precision_weights + generic :: bias => default_real_bias, double_precision_bias + procedure, private :: default_real_bias, double_precision_bias + generic :: next_allocated => default_real_next_allocated, double_precision_next_allocated + procedure, private :: default_real_next_allocated, double_precision_next_allocated + generic :: next_pointer => default_real_next_pointer, double_precision_next_pointer + procedure, private :: default_real_next_pointer, double_precision_next_pointer + generic :: num_inputs => default_real_num_inputs, double_precision_num_inputs + procedure, private :: default_real_num_inputs, double_precision_num_inputs end type interface neuron_t @@ -39,13 +39,20 @@ pure recursive module function from_json(neuron_lines, start) result(neuron) type(neuron_t) neuron end function - pure module function from_components(weights, bias) result(neuron) + pure module function default_real_from_components(weights, bias) result(neuron) !! construct single neuron_t object from an array of weights and a bias real, intent(in) :: weights(:) real, intent(in) :: bias type(neuron_t) neuron end function + pure module function double_precision_from_components(weights, bias) result(neuron) + !! construct single neuron_t object from an array of weights and a bias + double precision, intent(in) :: weights(:) + double precision, intent(in) :: bias + type(neuron_t(double_precision)) neuron + end function + end interface interface @@ -56,36 +63,72 @@ pure module function default_real_to_json(self) result(lines) type(string_t), allocatable :: lines(:) end function + pure module function double_precision_to_json(self) result(lines) + implicit none + class(neuron_t(double_precision)), intent(in) :: self + type(string_t), allocatable :: lines(:) + end function + module function default_real_weights(self) result(my_weights) implicit none class(neuron_t), intent(in) :: self real, allocatable :: my_weights(:) end function + module function double_precision_weights(self) result(my_weights) + implicit none + class(neuron_t(double_precision)), intent(in) :: self + double precision, allocatable :: my_weights(:) + end function + module function default_real_bias(self) result(my_bias) implicit none class(neuron_t), intent(in) :: self real my_bias end function + module function double_precision_bias(self) result(my_bias) + implicit none + class(neuron_t(double_precision)), intent(in) :: self + double precision my_bias + end function + module function default_real_next_allocated(self) result(next_is_allocated) implicit none class(neuron_t), intent(in) :: self logical next_is_allocated end function + module function double_precision_next_allocated(self) result(next_is_allocated) + implicit none + class(neuron_t(double_precision)), intent(in) :: self + logical next_is_allocated + end function + module function default_real_next_pointer(self) result(next_ptr) implicit none class(neuron_t), intent(in), target :: self type(neuron_t), pointer :: next_ptr end function + module function double_precision_next_pointer(self) result(next_ptr) + implicit none + class(neuron_t(double_precision)), intent(in), target :: self + type(neuron_t(double_precision)), pointer :: next_ptr + end function + pure module function default_real_num_inputs(self) result(size_weights) implicit none class(neuron_t), intent(in) :: self integer size_weights end function + pure module function double_precision_num_inputs(self) result(size_weights) + implicit none + class(neuron_t(double_precision)), intent(in) :: self + integer size_weights + end function + end interface end module diff --git a/src/inference_engine/neuron_s.f90 b/src/inference_engine/neuron_s.f90 index 2fd1f3f0c..b985cf8bd 100644 --- a/src/inference_engine/neuron_s.f90 +++ b/src/inference_engine/neuron_s.f90 @@ -27,6 +27,26 @@ ] end procedure + module procedure double_precision_to_json + integer, parameter :: characters_per_value=34 + character(len=*), parameter :: indent = repeat(" ",ncopies=12) + character(len=:), allocatable :: csv_format, weights_string, bias_string + + call assert(allocated(self%weights_), "neuron_s(to_json): allocated weights_") + + csv_format = separated_values(separator=",", mold=[double precision::]) + allocate(character(len=size(self%weights_)*(characters_per_value+1)-1)::weights_string) + allocate(character(len=characters_per_value)::bias_string) + write(weights_string, fmt = csv_format) self%weights_ + write(bias_string,*) self%bias_ + lines = [ & + string_t(indent // '{'), & + string_t(indent // ' "weights": [' // trim(adjustl(weights_string)) // '],'), & + string_t(indent // ' "bias": ' // trim(adjustl(bias_string))), & + string_t(indent // '}') & + ] + end procedure + module procedure from_json character(len=:), allocatable :: line @@ -62,7 +82,12 @@ end procedure - module procedure from_components + module procedure default_real_from_components + neuron%weights_ = weights + neuron%bias_ = bias + end procedure + + module procedure double_precision_from_components neuron%weights_ = weights neuron%bias_ = bias end procedure @@ -71,14 +96,26 @@ my_weights = self%weights_ end procedure + module procedure double_precision_weights + my_weights = self%weights_ + end procedure + module procedure default_real_bias my_bias = self%bias_ end procedure + module procedure double_precision_bias + my_bias = self%bias_ + end procedure + module procedure default_real_next_allocated next_is_allocated = allocated(self%next) end procedure + module procedure double_precision_next_allocated + next_is_allocated = allocated(self%next) + end procedure + module procedure default_real_next_pointer if (allocated(self%next)) then next_ptr => self%next @@ -87,8 +124,20 @@ end if end procedure + module procedure double_precision_next_pointer + if (allocated(self%next)) then + next_ptr => self%next + else + next_ptr => null() + end if + end procedure + module procedure default_real_num_inputs size_weights = size(self%weights_) end procedure + module procedure double_precision_num_inputs + size_weights = size(self%weights_) + end procedure + end submodule neuron_s From 71bbff872a359049e7a5757f5cc8dbe9c043c209 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Thu, 29 Aug 2024 09:45:03 -0700 Subject: [PATCH 020/105] feat(layer_t): add double-precision procedures --- src/inference_engine/layer_m.f90 | 62 +++++++++++++++++++------ src/inference_engine/layer_s.f90 | 80 +++++++++++++++++++++++++++++--- 2 files changed, 123 insertions(+), 19 deletions(-) diff --git a/src/inference_engine/layer_m.f90 b/src/inference_engine/layer_m.f90 index 7842fd834..f831eaadb 100644 --- a/src/inference_engine/layer_m.f90 +++ b/src/inference_engine/layer_m.f90 @@ -5,7 +5,7 @@ module layer_m use julienne_string_m, only : string_t use inference_engine_m_, only : inference_engine_t use tensor_map_m, only : tensor_map_t - use kind_parameters_m, only : default_real + use kind_parameters_m, only : default_real, double_precision implicit none private @@ -19,18 +19,18 @@ module layer_m contains generic :: inference_engine => default_real_inference_engine procedure, private :: default_real_inference_engine - generic :: count_layers => default_real_count_layers - procedure, private :: default_real_count_layers - generic :: count_neurons => default_real_count_neurons - procedure, private :: default_real_count_neurons - generic :: count_inputs => default_real_count_inputs - procedure, private :: default_real_count_inputs - generic :: neurons_per_layer => default_real_neurons_per_layer - procedure, private :: default_real_neurons_per_layer - generic :: next_allocated => default_real_next_allocated - procedure, private :: default_real_next_allocated - generic :: next_pointer => default_real_next_pointer - procedure, private :: default_real_next_pointer + generic :: count_layers => default_real_count_layers, double_precision_count_layers + procedure, private :: default_real_count_layers, double_precision_count_layers + generic :: count_neurons => default_real_count_neurons, double_precision_count_neurons + procedure, private :: default_real_count_neurons, double_precision_count_neurons + generic :: count_inputs => default_real_count_inputs, double_precision_count_inputs + procedure, private :: default_real_count_inputs, double_precision_count_inputs + generic :: neurons_per_layer => default_real_neurons_per_layer, double_precision_neurons_per_layer + procedure, private :: default_real_neurons_per_layer, double_precision_neurons_per_layer + generic :: next_allocated => default_real_next_allocated, double_precision_next_allocated + procedure, private :: default_real_next_allocated, double_precision_next_allocated + generic :: next_pointer => default_real_next_pointer, double_precision_next_pointer + procedure, private :: default_real_next_pointer, double_precision_next_pointer end type interface layer_t @@ -62,36 +62,72 @@ module function default_real_count_layers(layer) result(num_layers) integer num_layers end function + module function double_precision_count_layers(layer) result(num_layers) + implicit none + class(layer_t(double_precision)), intent(in), target :: layer + integer num_layers + end function + module function default_real_count_neurons(layer) result(neurons_per_layer_result) implicit none class(layer_t), intent(in), target :: layer integer, allocatable :: neurons_per_layer_result(:) end function + module function double_precision_count_neurons(layer) result(neurons_per_layer_result) + implicit none + class(layer_t(double_precision)), intent(in), target :: layer + integer, allocatable :: neurons_per_layer_result(:) + end function + module function default_real_count_inputs(layer) result(num_inputs) implicit none class(layer_t), intent(in) :: layer integer num_inputs end function + module function double_precision_count_inputs(layer) result(num_inputs) + implicit none + class(layer_t(double_precision)), intent(in) :: layer + integer num_inputs + end function + module function default_real_neurons_per_layer(self) result(num_neurons) implicit none class(layer_t), intent(in), target :: self integer num_neurons end function + module function double_precision_neurons_per_layer(self) result(num_neurons) + implicit none + class(layer_t(double_precision)), intent(in), target :: self + integer num_neurons + end function + module function default_real_next_allocated(self) result(next_is_allocated) implicit none class(layer_t), intent(in) :: self logical next_is_allocated end function + module function double_precision_next_allocated(self) result(next_is_allocated) + implicit none + class(layer_t(double_precision)), intent(in) :: self + logical next_is_allocated + end function + module function default_real_next_pointer(self) result(next_ptr) implicit none class(layer_t), intent(in), target :: self type(layer_t), pointer :: next_ptr end function + module function double_precision_next_pointer(self) result(next_ptr) + implicit none + class(layer_t(double_precision)), intent(in), target :: self + type(layer_t(double_precision)), pointer :: next_ptr + end function + end interface end module diff --git a/src/inference_engine/layer_s.f90 b/src/inference_engine/layer_s.f90 index d08b1b689..ac034d7aa 100644 --- a/src/inference_engine/layer_s.f90 +++ b/src/inference_engine/layer_s.f90 @@ -2,7 +2,6 @@ ! Terms of use are as specified in LICENSE.txt submodule(layer_m) layer_s use assert_m, only : assert - use kind_parameters_m, only : rkind implicit none contains @@ -17,7 +16,7 @@ line = adjustl(layer_lines(start)%string()) hidden_layers = line == '[' output_layer = line == '"output_layer": [' - call assert(hidden_layers .or. output_layer, "layer_t construct_layer: layer start", line) + call assert(hidden_layers .or. output_layer, "layer_s(default_real_construct_layer): layer start", line) layer%neuron = neuron_t(layer_lines, start+1) num_inputs = size(layer%neuron%weights()) @@ -27,12 +26,12 @@ do if (.not. neuron%next_allocated()) exit neuron => neuron%next_pointer() - call assert(size(neuron%weights()) == num_inputs, "layer_t construct_layer: constant number of inputs") + call assert(size(neuron%weights()) == num_inputs, "layer_s(default_real_construct_layer): constant number of inputs") neurons_in_layer = neurons_in_layer + 1 end do line = trim(adjustl(layer_lines(start+4*neurons_in_layer+1)%string())) - call assert(line(1:1)==']', "read_layer_list: hidden layer end") + call assert(line(1:1)==']', "layer_s(default_real_construct_layer): hidden layer end") if (line(len(line):len(line)) == ",") layer%next = default_real_construct_layer(layer_lines, start+4*neurons_in_layer+2) @@ -47,12 +46,12 @@ num_hidden_layers => hidden_layers%count_layers(), & num_output_layers => output_layer%count_layers() & ) - call assert(num_output_layers==1, "inference_engine_s(default_real_construct_from_json): 1 output layer", num_output_layers) + call assert(num_output_layers==1, "inference_engine_s(default_real_inference_engine): 1 output layer", num_output_layers) associate(nodes => [num_inputs, neurons_per_hidden_layer, num_outputs]) associate(n_max => maxval(nodes)) block - real(rkind), allocatable :: weights(:,:,:), biases(:,:) + real, allocatable :: weights(:,:,:), biases(:,:) type(layer_t), pointer :: layer_ptr type(neuron_t), pointer :: neuron_ptr integer j, l @@ -124,6 +123,20 @@ end procedure + module procedure double_precision_count_layers + + type(layer_t(double_precision)), pointer :: layer_ptr + + layer_ptr => layer + num_layers = 1 + do + if (.not. allocated(layer_ptr%next)) exit + layer_ptr => layer_ptr%next + num_layers = num_layers + 1 + end do + + end procedure + module procedure default_real_count_neurons type(layer_t), pointer :: layer_ptr @@ -149,10 +162,39 @@ end procedure + module procedure double_precision_count_neurons + + type(layer_t(double_precision)), pointer :: layer_ptr + type(neuron_t(double_precision)), pointer :: neuron_ptr + integer num_neurons + + layer_ptr => layer + + allocate(neurons_per_layer_result(0)) + + do + num_neurons = 1 + neuron_ptr => layer_ptr%neuron + do + if (.not. neuron_ptr%next_allocated()) exit + neuron_ptr => neuron_ptr%next_pointer() + num_neurons = num_neurons + 1 + end do + neurons_per_layer_result = [neurons_per_layer_result, num_neurons] + if (.not. allocated(layer_ptr%next)) exit + layer_ptr => layer_ptr%next + end do + + end procedure + module procedure default_real_count_inputs num_inputs = layer%neuron%num_inputs() ! assume fully connected input layer end procedure + module procedure double_precision_count_inputs + num_inputs = layer%neuron%num_inputs() ! assume fully connected input layer + end procedure + module procedure default_real_neurons_per_layer type(neuron_t), pointer :: neuron @@ -167,10 +209,28 @@ end procedure + module procedure double_precision_neurons_per_layer + + type(neuron_t(double_precision)), pointer :: neuron + + neuron => self%neuron + num_neurons = 1 + do + if (.not. neuron%next_allocated()) exit + neuron => neuron%next_pointer() + num_neurons = num_neurons + 1 + end do + + end procedure + module procedure default_real_next_allocated next_is_allocated = allocated(self%next) end procedure + module procedure double_precision_next_allocated + next_is_allocated = allocated(self%next) + end procedure + module procedure default_real_next_pointer if (allocated(self%next)) then next_ptr => self%next @@ -179,4 +239,12 @@ end if end procedure + module procedure double_precision_next_pointer + if (allocated(self%next)) then + next_ptr => self%next + else + next_ptr => null() + end if + end procedure + end submodule layer_s From 9c99739fdc320e86b14eabd9edeaf5e6d6db534c Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Thu, 29 Aug 2024 09:45:03 -0700 Subject: [PATCH 021/105] feat(layer_t): add double-precision procedures --- .../double_precision_string_m.f90 | 8 ++ src/inference_engine/layer_m.f90 | 71 +++++++++-- src/inference_engine/layer_s.f90 | 113 ++++++++++++++++-- src/inference_engine/neuron_m.f90 | 9 ++ src/inference_engine/neuron_s.f90 | 36 ++++++ 5 files changed, 217 insertions(+), 20 deletions(-) create mode 100644 src/inference_engine/double_precision_string_m.f90 diff --git a/src/inference_engine/double_precision_string_m.f90 b/src/inference_engine/double_precision_string_m.f90 new file mode 100644 index 000000000..54edde09d --- /dev/null +++ b/src/inference_engine/double_precision_string_m.f90 @@ -0,0 +1,8 @@ +module double_precision_string_m + use julienne_m, only : string_t + implicit none + + type, extends(string_t) :: double_precision_string_t + end type + +end module double_precision_string_m diff --git a/src/inference_engine/layer_m.f90 b/src/inference_engine/layer_m.f90 index 7842fd834..47065eef1 100644 --- a/src/inference_engine/layer_m.f90 +++ b/src/inference_engine/layer_m.f90 @@ -5,7 +5,8 @@ module layer_m use julienne_string_m, only : string_t use inference_engine_m_, only : inference_engine_t use tensor_map_m, only : tensor_map_t - use kind_parameters_m, only : default_real + use kind_parameters_m, only : default_real, double_precision + use double_precision_string_m, only : double_precision_string_t implicit none private @@ -19,18 +20,18 @@ module layer_m contains generic :: inference_engine => default_real_inference_engine procedure, private :: default_real_inference_engine - generic :: count_layers => default_real_count_layers - procedure, private :: default_real_count_layers - generic :: count_neurons => default_real_count_neurons - procedure, private :: default_real_count_neurons - generic :: count_inputs => default_real_count_inputs - procedure, private :: default_real_count_inputs - generic :: neurons_per_layer => default_real_neurons_per_layer - procedure, private :: default_real_neurons_per_layer - generic :: next_allocated => default_real_next_allocated - procedure, private :: default_real_next_allocated - generic :: next_pointer => default_real_next_pointer - procedure, private :: default_real_next_pointer + generic :: count_layers => default_real_count_layers, double_precision_count_layers + procedure, private :: default_real_count_layers, double_precision_count_layers + generic :: count_neurons => default_real_count_neurons, double_precision_count_neurons + procedure, private :: default_real_count_neurons, double_precision_count_neurons + generic :: count_inputs => default_real_count_inputs, double_precision_count_inputs + procedure, private :: default_real_count_inputs, double_precision_count_inputs + generic :: neurons_per_layer => default_real_neurons_per_layer, double_precision_neurons_per_layer + procedure, private :: default_real_neurons_per_layer, double_precision_neurons_per_layer + generic :: next_allocated => default_real_next_allocated, double_precision_next_allocated + procedure, private :: default_real_next_allocated, double_precision_next_allocated + generic :: next_pointer => default_real_next_pointer, double_precision_next_pointer + procedure, private :: default_real_next_pointer, double_precision_next_pointer end type interface layer_t @@ -43,6 +44,14 @@ recursive module function default_real_construct_layer(layer_lines, start) resul type(layer_t), target :: layer end function + recursive module function double_precision_construct_layer(layer_lines, start) result(layer) + !! construct a linked list of layer_t objects from an array of JSON-formatted text lines + implicit none + type(double_precision_string_t), intent(in) :: layer_lines(:) + integer, intent(in) :: start + type(layer_t(double_precision)), target :: layer + end function + end interface interface @@ -62,36 +71,72 @@ module function default_real_count_layers(layer) result(num_layers) integer num_layers end function + module function double_precision_count_layers(layer) result(num_layers) + implicit none + class(layer_t(double_precision)), intent(in), target :: layer + integer num_layers + end function + module function default_real_count_neurons(layer) result(neurons_per_layer_result) implicit none class(layer_t), intent(in), target :: layer integer, allocatable :: neurons_per_layer_result(:) end function + module function double_precision_count_neurons(layer) result(neurons_per_layer_result) + implicit none + class(layer_t(double_precision)), intent(in), target :: layer + integer, allocatable :: neurons_per_layer_result(:) + end function + module function default_real_count_inputs(layer) result(num_inputs) implicit none class(layer_t), intent(in) :: layer integer num_inputs end function + module function double_precision_count_inputs(layer) result(num_inputs) + implicit none + class(layer_t(double_precision)), intent(in) :: layer + integer num_inputs + end function + module function default_real_neurons_per_layer(self) result(num_neurons) implicit none class(layer_t), intent(in), target :: self integer num_neurons end function + module function double_precision_neurons_per_layer(self) result(num_neurons) + implicit none + class(layer_t(double_precision)), intent(in), target :: self + integer num_neurons + end function + module function default_real_next_allocated(self) result(next_is_allocated) implicit none class(layer_t), intent(in) :: self logical next_is_allocated end function + module function double_precision_next_allocated(self) result(next_is_allocated) + implicit none + class(layer_t(double_precision)), intent(in) :: self + logical next_is_allocated + end function + module function default_real_next_pointer(self) result(next_ptr) implicit none class(layer_t), intent(in), target :: self type(layer_t), pointer :: next_ptr end function + module function double_precision_next_pointer(self) result(next_ptr) + implicit none + class(layer_t(double_precision)), intent(in), target :: self + type(layer_t(double_precision)), pointer :: next_ptr + end function + end interface end module diff --git a/src/inference_engine/layer_s.f90 b/src/inference_engine/layer_s.f90 index d08b1b689..1fdb858f9 100644 --- a/src/inference_engine/layer_s.f90 +++ b/src/inference_engine/layer_s.f90 @@ -2,7 +2,6 @@ ! Terms of use are as specified in LICENSE.txt submodule(layer_m) layer_s use assert_m, only : assert - use kind_parameters_m, only : rkind implicit none contains @@ -17,7 +16,7 @@ line = adjustl(layer_lines(start)%string()) hidden_layers = line == '[' output_layer = line == '"output_layer": [' - call assert(hidden_layers .or. output_layer, "layer_t construct_layer: layer start", line) + call assert(hidden_layers .or. output_layer, "layer_s(default_real_construct_layer): layer start", line) layer%neuron = neuron_t(layer_lines, start+1) num_inputs = size(layer%neuron%weights()) @@ -27,14 +26,45 @@ do if (.not. neuron%next_allocated()) exit neuron => neuron%next_pointer() - call assert(size(neuron%weights()) == num_inputs, "layer_t construct_layer: constant number of inputs") + call assert(size(neuron%weights()) == num_inputs, "layer_s(default_real_construct_layer): constant number of inputs") neurons_in_layer = neurons_in_layer + 1 end do line = trim(adjustl(layer_lines(start+4*neurons_in_layer+1)%string())) - call assert(line(1:1)==']', "read_layer_list: hidden layer end") + call assert(line(1:1)==']', "layer_s(default_real_construct_layer): hidden layer end") - if (line(len(line):len(line)) == ",") layer%next = default_real_construct_layer(layer_lines, start+4*neurons_in_layer+2) + if (line(len(line):len(line)) == ",") layer%next = layer_t(layer_lines, start+4*neurons_in_layer+2) + + end procedure + + module procedure double_precision_construct_layer + + type(neuron_t(double_precision)), pointer :: neuron + integer num_inputs, neurons_in_layer + character(len=:), allocatable :: line + logical hidden_layers, output_layer + + line = adjustl(layer_lines(start)%string()) + hidden_layers = line == '[' + output_layer = line == '"output_layer": [' + call assert(hidden_layers .or. output_layer, "layer_s(double_precision_construct_layer): layer start", line) + + layer%neuron = neuron_t(layer_lines, start+1) + num_inputs = size(layer%neuron%weights()) + + neuron => layer%neuron + neurons_in_layer = 1 + do + if (.not. neuron%next_allocated()) exit + neuron => neuron%next_pointer() + call assert(size(neuron%weights()) == num_inputs, "layer_s(double_precision_construct_layer): constant number of inputs") + neurons_in_layer = neurons_in_layer + 1 + end do + + line = trim(adjustl(layer_lines(start+4*neurons_in_layer+1)%string())) + call assert(line(1:1)==']', "layer_s(double_precision_construct_layer): hidden layer end") + + if (line(len(line):len(line)) == ",") layer%next = layer_t(layer_lines, start+4*neurons_in_layer+2) end procedure @@ -47,12 +77,12 @@ num_hidden_layers => hidden_layers%count_layers(), & num_output_layers => output_layer%count_layers() & ) - call assert(num_output_layers==1, "inference_engine_s(default_real_construct_from_json): 1 output layer", num_output_layers) + call assert(num_output_layers==1, "inference_engine_s(default_real_inference_engine): 1 output layer", num_output_layers) associate(nodes => [num_inputs, neurons_per_hidden_layer, num_outputs]) associate(n_max => maxval(nodes)) block - real(rkind), allocatable :: weights(:,:,:), biases(:,:) + real, allocatable :: weights(:,:,:), biases(:,:) type(layer_t), pointer :: layer_ptr type(neuron_t), pointer :: neuron_ptr integer j, l @@ -124,6 +154,20 @@ end procedure + module procedure double_precision_count_layers + + type(layer_t(double_precision)), pointer :: layer_ptr + + layer_ptr => layer + num_layers = 1 + do + if (.not. allocated(layer_ptr%next)) exit + layer_ptr => layer_ptr%next + num_layers = num_layers + 1 + end do + + end procedure + module procedure default_real_count_neurons type(layer_t), pointer :: layer_ptr @@ -149,10 +193,39 @@ end procedure + module procedure double_precision_count_neurons + + type(layer_t(double_precision)), pointer :: layer_ptr + type(neuron_t(double_precision)), pointer :: neuron_ptr + integer num_neurons + + layer_ptr => layer + + allocate(neurons_per_layer_result(0)) + + do + num_neurons = 1 + neuron_ptr => layer_ptr%neuron + do + if (.not. neuron_ptr%next_allocated()) exit + neuron_ptr => neuron_ptr%next_pointer() + num_neurons = num_neurons + 1 + end do + neurons_per_layer_result = [neurons_per_layer_result, num_neurons] + if (.not. allocated(layer_ptr%next)) exit + layer_ptr => layer_ptr%next + end do + + end procedure + module procedure default_real_count_inputs num_inputs = layer%neuron%num_inputs() ! assume fully connected input layer end procedure + module procedure double_precision_count_inputs + num_inputs = layer%neuron%num_inputs() ! assume fully connected input layer + end procedure + module procedure default_real_neurons_per_layer type(neuron_t), pointer :: neuron @@ -167,10 +240,28 @@ end procedure + module procedure double_precision_neurons_per_layer + + type(neuron_t(double_precision)), pointer :: neuron + + neuron => self%neuron + num_neurons = 1 + do + if (.not. neuron%next_allocated()) exit + neuron => neuron%next_pointer() + num_neurons = num_neurons + 1 + end do + + end procedure + module procedure default_real_next_allocated next_is_allocated = allocated(self%next) end procedure + module procedure double_precision_next_allocated + next_is_allocated = allocated(self%next) + end procedure + module procedure default_real_next_pointer if (allocated(self%next)) then next_ptr => self%next @@ -179,4 +270,12 @@ end if end procedure + module procedure double_precision_next_pointer + if (allocated(self%next)) then + next_ptr => self%next + else + next_ptr => null() + end if + end procedure + end submodule layer_s diff --git a/src/inference_engine/neuron_m.f90 b/src/inference_engine/neuron_m.f90 index b078a7b29..45852606a 100644 --- a/src/inference_engine/neuron_m.f90 +++ b/src/inference_engine/neuron_m.f90 @@ -3,6 +3,7 @@ module neuron_m use julienne_string_m, only : string_t use kind_parameters_m, only : default_real, double_precision + use double_precision_string_m, only : double_precision_string_t implicit none private @@ -39,6 +40,14 @@ pure recursive module function from_json(neuron_lines, start) result(neuron) type(neuron_t) neuron end function + pure recursive module function double_precision_from_json(neuron_lines, start) result(neuron) + !! construct linked list of neuron_t objects from an array of JSON-formatted text lines + implicit none + type(double_precision_string_t), intent(in) :: neuron_lines(:) + integer, intent(in) :: start + type(neuron_t(double_precision)) neuron + end function + pure module function default_real_from_components(weights, bias) result(neuron) !! construct single neuron_t object from an array of weights and a bias real, intent(in) :: weights(:) diff --git a/src/inference_engine/neuron_s.f90 b/src/inference_engine/neuron_s.f90 index b985cf8bd..1226f1bf3 100644 --- a/src/inference_engine/neuron_s.f90 +++ b/src/inference_engine/neuron_s.f90 @@ -82,6 +82,42 @@ end procedure + module procedure double_precision_from_json + + character(len=:), allocatable :: line + integer i + + call assert(adjustl(neuron_lines(start)%string())=='{', & + "neuron_s(double_precison_from_json): neuron object start",neuron_lines(start)%string()) + + line = neuron_lines(start+1)%string() + associate(colon => index(line, ":")) + call assert(adjustl(line(:colon-1))=='"weights"', "neuron_s(double_precision_from_json): neuron weights", line) + associate(opening_bracket => colon + index(line(colon+1:), "[")) + associate(closing_bracket => opening_bracket + index(line(opening_bracket+1:), "]")) + associate(commas => count("," == [(line(i:i), i=opening_bracket+1,closing_bracket-1)])) + associate(num_inputs => commas + 1) + allocate(neuron%weights_(num_inputs)) + read(line(opening_bracket+1:closing_bracket-1), fmt=*) neuron%weights_ + end associate + end associate + end associate + end associate + end associate + + line = neuron_lines(start+2)%string() + associate(colon => index(line, ":")) + call assert(adjustl(line(:colon-1))=='"bias"', "neuron_s(double_precision_from_json): neuron bias", line) + read(line(colon+1:), fmt=*) neuron%bias_ + end associate + + line = adjustl(neuron_lines(start+3)%string()) + call assert(line(1:1)=='}', "neuron_s(double_precision_from_json): neuron object end", line) + line = adjustr(neuron_lines(start+3)%string()) + if (line(len(line):len(line)) == ",") neuron%next = double_precision_from_json(neuron_lines, start+4) + + end procedure + module procedure default_real_from_components neuron%weights_ = weights neuron%bias_ = bias From d919e443b9fd51a55d192ae5fc24c583a49adba7 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Sat, 31 Aug 2024 12:48:09 -0700 Subject: [PATCH 022/105] feat(trainable_engine_t): add real kind parameter --- src/inference_engine/trainable_engine_m.F90 | 41 +++++++++++---------- src/inference_engine/trainable_engine_s.F90 | 25 ++++++------- 2 files changed, 34 insertions(+), 32 deletions(-) diff --git a/src/inference_engine/trainable_engine_m.F90 b/src/inference_engine/trainable_engine_m.F90 index 74ffb04b1..cb6f8fe2f 100644 --- a/src/inference_engine/trainable_engine_m.F90 +++ b/src/inference_engine/trainable_engine_m.F90 @@ -5,7 +5,7 @@ module trainable_engine_m use julienne_string_m, only : string_t use inference_engine_m_, only : inference_engine_t use differentiable_activation_strategy_m, only : differentiable_activation_strategy_t - use kind_parameters_m, only : rkind + use kind_parameters_m, only : default_real use metadata_m, only : metadata_t use tensor_m, only : tensor_t use tensor_map_m, only : tensor_map_t @@ -17,22 +17,25 @@ module trainable_engine_m private public :: trainable_engine_t - type trainable_engine_t + type trainable_engine_t(k) !! Encapsulate the information needed to perform training - private - type(tensor_map_t) input_map_, output_map_ - type(metadata_t) metadata_ - real(rkind), allocatable :: w(:,:,:) ! weights - real(rkind), allocatable :: b(:,:) ! biases - integer, allocatable :: n(:) ! nodes per layer + integer, kind :: k = default_real + type(tensor_map_t(k)), private :: input_map_, output_map_ !! mappings to/from data ranges used during training class(differentiable_activation_strategy_t), allocatable :: differentiable_activation_strategy_ - real(rkind), allocatable, dimension(:,:) :: a - real(rkind), allocatable, dimension(:,:,:) :: dcdw, vdw, sdw, vdwc, sdwc - real(rkind), allocatable, dimension(:,:) :: z, delta, dcdb, vdb, sdb, vdbc, sdbc + type(metadata_t) metadata_ !! metadata_ encapsulates strings for which default-kind suffices + !! stateless and thus not paremeterized; generic resolution supports different kinds + real(k), allocatable :: w(:,:,:) !! weights + real(k), allocatable :: b(:,:) !! biases + integer, allocatable :: n(:) !! nodes per layer + real(k), allocatable, dimension(:,:) :: a + real(k), allocatable, dimension(:,:,:) :: dcdw, vdw, sdw, vdwc, sdwc + real(k), allocatable, dimension(:,:) :: z, delta, dcdb, vdb, sdb, vdbc, sdbc contains + generic :: train => default_precision_train + procedure, private :: default_precision_train + generic :: infer => default_precision_infer + procedure, private :: default_precision_infer procedure :: assert_consistent - procedure :: train - procedure :: infer procedure :: num_layers procedure :: num_inputs procedure :: num_outputs @@ -59,7 +62,7 @@ impure module function construct_from_padded_arrays( & result(trainable_engine) implicit none integer, intent(in) :: nodes(input_layer:) - real(rkind), intent(in) :: weights(:,:,:), biases(:,:) + real, intent(in) :: weights(:,:,:), biases(:,:) class(differentiable_activation_strategy_t), intent(in) :: differentiable_activation_strategy type(string_t), intent(in) :: metadata(:) type(tensor_map_t), intent(in), optional :: input_map, output_map @@ -77,7 +80,7 @@ module function perturbed_identity_network(training_configuration, perturbation_ implicit none type(training_configuration_t), intent(in) :: training_configuration type(string_t), intent(in) :: metadata(:) - real(rkind), intent(in) :: perturbation_magnitude + real, intent(in) :: perturbation_magnitude type(tensor_map_t) input_map, output_map type(trainable_engine_t) trainable_engine end function @@ -91,16 +94,16 @@ pure module subroutine assert_consistent(self) class(trainable_engine_t), intent(in) :: self end subroutine - pure module subroutine train(self, mini_batches_arr, cost, adam, learning_rate) + pure module subroutine default_precision_train(self, mini_batches_arr, cost, adam, learning_rate) implicit none class(trainable_engine_t), intent(inout) :: self type(mini_batch_t), intent(in) :: mini_batches_arr(:) - real(rkind), intent(out), allocatable, optional :: cost(:) + real, intent(out), allocatable, optional :: cost(:) logical, intent(in) :: adam - real(rkind), intent(in) :: learning_rate + real, intent(in) :: learning_rate end subroutine - elemental module function infer(self, inputs) result(outputs) + elemental module function default_precision_infer(self, inputs) result(outputs) implicit none class(trainable_engine_t), intent(in) :: self type(tensor_t), intent(in) :: inputs diff --git a/src/inference_engine/trainable_engine_s.F90 b/src/inference_engine/trainable_engine_s.F90 index 45affdca8..09c71f8dd 100644 --- a/src/inference_engine/trainable_engine_s.F90 +++ b/src/inference_engine/trainable_engine_s.F90 @@ -10,7 +10,6 @@ implicit none integer, parameter :: input_layer = 0 - real(rkind), parameter :: two = 2._rkind contains @@ -71,9 +70,9 @@ end procedure - module procedure infer + module procedure default_precision_infer - real(rkind), allocatable :: a(:,:) + real, allocatable :: a(:,:) integer l call self%assert_consistent @@ -111,7 +110,7 @@ end procedure - module procedure train + module procedure default_precision_train integer l, batch, mini_batch_size, pair type(tensor_t), allocatable :: inputs(:), expected_outputs(:) @@ -166,7 +165,7 @@ end block #endif block - real(rkind), allocatable :: pair_cost(:) + real, allocatable :: pair_cost(:) if (present(cost)) allocate(pair_cost(mini_batch_size)) iterate_through_batch: & @@ -209,15 +208,15 @@ end do iterate_through_batch - if (present(cost)) cost(batch) = sum(pair_cost)/(two*mini_batch_size) + if (present(cost)) cost(batch) = sum(pair_cost)/(2*mini_batch_size) end block if (adam) then block ! Adam parameters - real, parameter :: beta(*) = [.9_rkind, .999_rkind] - real, parameter :: obeta(*) = [1._rkind - beta(1), 1._rkind - beta(2)] - real, parameter :: epsilon = real(1.D-08,rkind) + real, parameter :: beta(*) = [.9, .999] + real, parameter :: obeta(*) = [1.- beta(1), 1.- beta(2)] + real, parameter :: epsilon = 1.E-08 associate(alpha => learning_rate) adam_adjust_weights_and_biases: & @@ -225,16 +224,16 @@ dcdw(1:n(l),1:n(l-1),l) = dcdw(1:n(l),1:n(l-1),l)/(mini_batch_size) vdw(1:n(l),1:n(l-1),l) = beta(1)*vdw(1:n(l),1:n(l-1),l) + obeta(1)*dcdw(1:n(l),1:n(l-1),l) sdw (1:n(l),1:n(l-1),l) = beta(2)*sdw(1:n(l),1:n(l-1),l) + obeta(2)*(dcdw(1:n(l),1:n(l-1),l)**2) - vdwc(1:n(l),1:n(l-1),l) = vdw(1:n(l),1:n(l-1),l)/(1._rkind - beta(1)**num_mini_batches) - sdwc(1:n(l),1:n(l-1),l) = sdw(1:n(l),1:n(l-1),l)/(1._rkind - beta(2)**num_mini_batches) + vdwc(1:n(l),1:n(l-1),l) = vdw(1:n(l),1:n(l-1),l)/(1.- beta(1)**num_mini_batches) + sdwc(1:n(l),1:n(l-1),l) = sdw(1:n(l),1:n(l-1),l)/(1.- beta(2)**num_mini_batches) w(1:n(l),1:n(l-1),l) = w(1:n(l),1:n(l-1),l) & - alpha*vdwc(1:n(l),1:n(l-1),l)/(sqrt(sdwc(1:n(l),1:n(l-1),l))+epsilon) ! Adjust weights dcdb(1:n(l),l) = dcdb(1:n(l),l)/mini_batch_size vdb(1:n(l),l) = beta(1)*vdb(1:n(l),l) + obeta(1)*dcdb(1:n(l),l) sdb(1:n(l),l) = beta(2)*sdb(1:n(l),l) + obeta(2)*(dcdb(1:n(l),l)**2) - vdbc(1:n(l),l) = vdb(1:n(l),l)/(1._rkind - beta(1)**num_mini_batches) - sdbc(1:n(l),l) = sdb(1:n(l),l)/(1._rkind - beta(2)**num_mini_batches) + vdbc(1:n(l),l) = vdb(1:n(l),l)/(1. - beta(1)**num_mini_batches) + sdbc(1:n(l),l) = sdb(1:n(l),l)/(1. - beta(2)**num_mini_batches) b(1:n(l),l) = b(1:n(l),l) - alpha*vdbc(1:n(l),l)/(sqrt(sdbc(1:n(l),l))+epsilon) ! Adjust weights end do adam_adjust_weights_and_biases end associate From 070b4c31a36a74ef401260a77f95f5c0061e0069 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Sat, 31 Aug 2024 12:53:55 -0700 Subject: [PATCH 023/105] feat(trainable_engine_t): add generic "predict" This commit adds a generic "predict" binding to follow PyTorch nomenclature. For now, the equivalent "infer" generic binding remains to support existing code. --- src/inference_engine/trainable_engine_m.F90 | 13 +++++++------ src/inference_engine/trainable_engine_s.F90 | 4 ++-- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/inference_engine/trainable_engine_m.F90 b/src/inference_engine/trainable_engine_m.F90 index cb6f8fe2f..341ebe9d7 100644 --- a/src/inference_engine/trainable_engine_m.F90 +++ b/src/inference_engine/trainable_engine_m.F90 @@ -31,10 +31,11 @@ module trainable_engine_m real(k), allocatable, dimension(:,:,:) :: dcdw, vdw, sdw, vdwc, sdwc real(k), allocatable, dimension(:,:) :: z, delta, dcdb, vdb, sdb, vdbc, sdbc contains - generic :: train => default_precision_train - procedure, private :: default_precision_train - generic :: infer => default_precision_infer - procedure, private :: default_precision_infer + generic :: train => default_real_train + procedure, private :: default_real_train + generic :: predict => default_real_predict + generic :: infer => default_real_predict + procedure, private :: default_real_predict procedure :: assert_consistent procedure :: num_layers procedure :: num_inputs @@ -94,7 +95,7 @@ pure module subroutine assert_consistent(self) class(trainable_engine_t), intent(in) :: self end subroutine - pure module subroutine default_precision_train(self, mini_batches_arr, cost, adam, learning_rate) + pure module subroutine default_real_train(self, mini_batches_arr, cost, adam, learning_rate) implicit none class(trainable_engine_t), intent(inout) :: self type(mini_batch_t), intent(in) :: mini_batches_arr(:) @@ -103,7 +104,7 @@ pure module subroutine default_precision_train(self, mini_batches_arr, cost, ada real, intent(in) :: learning_rate end subroutine - elemental module function default_precision_infer(self, inputs) result(outputs) + elemental module function default_real_predict(self, inputs) result(outputs) implicit none class(trainable_engine_t), intent(in) :: self type(tensor_t), intent(in) :: inputs diff --git a/src/inference_engine/trainable_engine_s.F90 b/src/inference_engine/trainable_engine_s.F90 index 09c71f8dd..702b7a821 100644 --- a/src/inference_engine/trainable_engine_s.F90 +++ b/src/inference_engine/trainable_engine_s.F90 @@ -70,7 +70,7 @@ end procedure - module procedure default_precision_infer + module procedure default_real_predict real, allocatable :: a(:,:) integer l @@ -110,7 +110,7 @@ end procedure - module procedure default_precision_train + module procedure default_real_train integer l, batch, mini_batch_size, pair type(tensor_t), allocatable :: inputs(:), expected_outputs(:) From de372925fae8b20488356ab2796162d36bfb4ce5 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Sat, 31 Aug 2024 13:45:11 -0700 Subject: [PATCH 024/105] refac(trainable_engine): make bindings generic --- src/inference_engine/trainable_engine_m.F90 | 50 ++++++++++++--------- src/inference_engine/trainable_engine_s.F90 | 20 ++++----- 2 files changed, 40 insertions(+), 30 deletions(-) diff --git a/src/inference_engine/trainable_engine_m.F90 b/src/inference_engine/trainable_engine_m.F90 index 341ebe9d7..13ae2baf8 100644 --- a/src/inference_engine/trainable_engine_m.F90 +++ b/src/inference_engine/trainable_engine_m.F90 @@ -36,16 +36,26 @@ module trainable_engine_m generic :: predict => default_real_predict generic :: infer => default_real_predict procedure, private :: default_real_predict - procedure :: assert_consistent - procedure :: num_layers - procedure :: num_inputs - procedure :: num_outputs - procedure :: to_inference_engine - procedure :: map_to_input_training_range - procedure :: map_from_input_training_range - procedure :: map_to_output_training_range - procedure :: map_from_output_training_range - procedure :: map_to_training_ranges + generic :: assert_consistent => default_real_assert_consistent + procedure, private :: default_real_assert_consistent + generic :: num_layers => default_real_num_layers + procedure, private :: default_real_num_layers + generic :: num_inputs => default_real_num_inputs + procedure, private :: default_real_num_inputs + generic :: num_outputs => default_real_num_outputs + procedure, private :: default_real_num_outputs + generic :: to_inference_engine => default_real_to_inference_engine + procedure, private :: default_real_to_inference_engine + generic :: map_to_input_training_range => default_real_map_to_input_training_range + procedure, private :: default_real_map_to_input_training_range + generic :: map_from_input_training_range => default_real_map_from_input_training_range + procedure, private :: default_real_map_from_input_training_range + generic :: map_to_output_training_range => default_real_map_to_output_training_range + procedure, private :: default_real_map_to_output_training_range + generic :: map_from_output_training_range => default_real_map_from_output_training_range + procedure, private :: default_real_map_from_output_training_range + generic :: map_to_training_ranges => default_real_map_to_training_ranges + procedure, private :: default_real_map_to_training_ranges end type integer, parameter :: input_layer = 0 @@ -90,7 +100,7 @@ module function perturbed_identity_network(training_configuration, perturbation_ interface - pure module subroutine assert_consistent(self) + pure module subroutine default_real_assert_consistent(self) implicit none class(trainable_engine_t), intent(in) :: self end subroutine @@ -111,59 +121,59 @@ elemental module function default_real_predict(self, inputs) result(outputs) type(tensor_t) outputs end function - elemental module function num_inputs(self) result(n_in) + elemental module function default_real_num_inputs(self) result(n_in) implicit none class(trainable_engine_t), intent(in) :: self integer n_in end function - elemental module function num_outputs(self) result(n_out) + elemental module function default_real_num_outputs(self) result(n_out) implicit none class(trainable_engine_t), intent(in) :: self integer n_out end function - elemental module function num_layers(self) result(n_layers) + elemental module function default_real_num_layers(self) result(n_layers) implicit none class(trainable_engine_t), intent(in) :: self integer n_layers end function - module function to_inference_engine(self) result(inference_engine) + module function default_real_to_inference_engine(self) result(inference_engine) implicit none class(trainable_engine_t), intent(in) :: self type(inference_engine_t) :: inference_engine end function - elemental module function map_to_input_training_range(self, tensor) result(normalized_tensor) + elemental module function default_real_map_to_input_training_range(self, tensor) result(normalized_tensor) implicit none class(trainable_engine_t), intent(in) :: self type(tensor_t), intent(in) :: tensor type(tensor_t) normalized_tensor end function - elemental module function map_from_input_training_range(self, tensor) result(unnormalized_tensor) + elemental module function default_real_map_from_input_training_range(self, tensor) result(unnormalized_tensor) implicit none class(trainable_engine_t), intent(in) :: self type(tensor_t), intent(in) :: tensor type(tensor_t) unnormalized_tensor end function - elemental module function map_to_output_training_range(self, tensor) result(normalized_tensor) + elemental module function default_real_map_to_output_training_range(self, tensor) result(normalized_tensor) implicit none class(trainable_engine_t), intent(in) :: self type(tensor_t), intent(in) :: tensor type(tensor_t) normalized_tensor end function - elemental module function map_from_output_training_range(self, tensor) result(unnormalized_tensor) + elemental module function default_real_map_from_output_training_range(self, tensor) result(unnormalized_tensor) implicit none class(trainable_engine_t), intent(in) :: self type(tensor_t), intent(in) :: tensor type(tensor_t) unnormalized_tensor end function - elemental module function map_to_training_ranges(self, input_output_pair) result(normalized_input_output_pair) + elemental module function default_real_map_to_training_ranges(self, input_output_pair) result(normalized_input_output_pair) implicit none class(trainable_engine_t), intent(in) :: self type(input_output_pair_t), intent(in) :: input_output_pair diff --git a/src/inference_engine/trainable_engine_s.F90 b/src/inference_engine/trainable_engine_s.F90 index 702b7a821..6a4fef537 100644 --- a/src/inference_engine/trainable_engine_s.F90 +++ b/src/inference_engine/trainable_engine_s.F90 @@ -13,15 +13,15 @@ contains - module procedure num_inputs + module procedure default_real_num_inputs n_in = self%n(input_layer) end procedure - module procedure num_layers + module procedure default_real_num_layers n_layers = size(self%n,1) end procedure - module procedure num_outputs + module procedure default_real_num_outputs n_out = self%n(ubound(self%n,1)) end procedure @@ -53,7 +53,7 @@ end procedure - module procedure assert_consistent + module procedure default_real_assert_consistent associate( & fully_allocated=>[allocated(self%w),allocated(self%b),allocated(self%n),allocated(self%differentiable_activation_strategy_)] & @@ -290,7 +290,7 @@ call trainable_engine%assert_consistent end procedure - module procedure to_inference_engine + module procedure default_real_to_inference_engine inference_engine = inference_engine_t(self%metadata_%strings(), self%w, self%b, self%n, self%input_map_, self%output_map_) end procedure @@ -332,7 +332,7 @@ pure function e(j,n) result(unit_vector) end procedure - module procedure map_to_training_ranges + module procedure default_real_map_to_training_ranges associate( & inputs => input_output_pair%inputs(), & expected_outputs => input_output_pair%expected_outputs() & @@ -346,19 +346,19 @@ pure function e(j,n) result(unit_vector) end associate end procedure - module procedure map_to_input_training_range + module procedure default_real_map_to_input_training_range normalized_tensor = self%input_map_%map_to_training_range(tensor) end procedure - module procedure map_from_input_training_range + module procedure default_real_map_from_input_training_range unnormalized_tensor = self%input_map_%map_from_training_range(tensor) end procedure - module procedure map_to_output_training_range + module procedure default_real_map_to_output_training_range normalized_tensor = self%output_map_%map_to_training_range(tensor) end procedure - module procedure map_from_output_training_range + module procedure default_real_map_from_output_training_range unnormalized_tensor = self%output_map_%map_from_training_range(tensor) end procedure From a5636ddeaeb77cc06e0beff97087a12e95b7101e Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Sat, 31 Aug 2024 13:55:55 -0700 Subject: [PATCH 025/105] feat(input_output_pair_t): add kind parameter also make bindings generic --- src/inference_engine/input_output_pair_m.f90 | 30 +++++++++++++------- src/inference_engine/input_output_pair_s.f90 | 10 +++---- 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/src/inference_engine/input_output_pair_m.f90 b/src/inference_engine/input_output_pair_m.f90 index b05c8776f..c945906c9 100644 --- a/src/inference_engine/input_output_pair_m.f90 +++ b/src/inference_engine/input_output_pair_m.f90 @@ -2,6 +2,7 @@ ! Terms of use are as specified in LICENSE.txt module input_output_pair_m use tensor_m, only : tensor_t + use kind_parameters_m, only : default_real implicit none private @@ -9,17 +10,19 @@ module input_output_pair_m public :: shuffle public :: write_to_stdout - type input_output_pair_t - private - type(tensor_t) inputs_, expected_outputs_ + type input_output_pair_t(k) + integer, kind :: k = default_real + type(tensor_t(k)), private :: inputs_, expected_outputs_ contains - procedure :: inputs - procedure :: expected_outputs + generic :: inputs => default_real_inputs + procedure, private :: default_real_inputs + generic :: expected_outputs => default_real_expected_outputs + procedure, private :: default_real_expected_outputs end type interface input_output_pair_t - elemental module function construct(inputs, expected_outputs) result(input_output_pair) + elemental module function default_real_construct(inputs, expected_outputs) result(input_output_pair) implicit none type(tensor_t), intent(in) :: inputs, expected_outputs type(input_output_pair_t) input_output_pair @@ -29,24 +32,31 @@ elemental module function construct(inputs, expected_outputs) result(input_outpu interface - elemental module function inputs(self) result(my_inputs) + elemental module function default_real_inputs(self) result(my_inputs) implicit none class(input_output_pair_t), intent(in) :: self type(tensor_t) :: my_inputs end function - elemental module function expected_outputs(self) result(my_expected_outputs) + elemental module function default_real_expected_outputs(self) result(my_expected_outputs) implicit none class(input_output_pair_t), intent(in) :: self type(tensor_t) :: my_expected_outputs end function - module subroutine shuffle(pairs) + end interface + + interface shuffle + module subroutine default_real_shuffle(pairs) implicit none type(input_output_pair_t), intent(inout) :: pairs(:) end subroutine - module subroutine write_to_stdout(input_output_pairs) + end interface + + interface write_to_stdout + + module subroutine default_real_write_to_stdout(input_output_pairs) implicit none type(input_output_pair_t), intent(in) :: input_output_pairs(:) end subroutine diff --git a/src/inference_engine/input_output_pair_s.f90 b/src/inference_engine/input_output_pair_s.f90 index 359bd1772..14c910db9 100644 --- a/src/inference_engine/input_output_pair_s.f90 +++ b/src/inference_engine/input_output_pair_s.f90 @@ -5,20 +5,20 @@ contains - module procedure construct + module procedure default_real_construct input_output_pair%inputs_ = inputs input_output_pair%expected_outputs_ = expected_outputs end procedure - module procedure inputs + module procedure default_real_inputs my_inputs = self%inputs_ end procedure - module procedure expected_outputs + module procedure default_real_expected_outputs my_expected_outputs = self%expected_outputs_ end procedure - module procedure shuffle + module procedure default_real_shuffle type(input_output_pair_t) temp real harvest(2:size(pairs)) integer i, j @@ -35,7 +35,7 @@ end procedure - module procedure write_to_stdout + module procedure default_real_write_to_stdout integer i do i = 1, size(input_output_pairs) print *, input_output_pairs(i)%inputs_%values(), " | ", input_output_pairs(i)%expected_outputs_%values() From d82b49866aac12547d2493e3ec04a9dd7cfbf8b4 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Sat, 31 Aug 2024 19:33:05 -0700 Subject: [PATCH 026/105] feat(input_output_pair): support double precision --- src/inference_engine/input_output_pair_m.f90 | 39 +++++++++++++++++--- src/inference_engine/input_output_pair_s.f90 | 37 +++++++++++++++++++ 2 files changed, 71 insertions(+), 5 deletions(-) diff --git a/src/inference_engine/input_output_pair_m.f90 b/src/inference_engine/input_output_pair_m.f90 index c945906c9..fcbfab493 100644 --- a/src/inference_engine/input_output_pair_m.f90 +++ b/src/inference_engine/input_output_pair_m.f90 @@ -2,7 +2,7 @@ ! Terms of use are as specified in LICENSE.txt module input_output_pair_m use tensor_m, only : tensor_t - use kind_parameters_m, only : default_real + use kind_parameters_m, only : default_real, double_precision implicit none private @@ -14,10 +14,10 @@ module input_output_pair_m integer, kind :: k = default_real type(tensor_t(k)), private :: inputs_, expected_outputs_ contains - generic :: inputs => default_real_inputs - procedure, private :: default_real_inputs - generic :: expected_outputs => default_real_expected_outputs - procedure, private :: default_real_expected_outputs + generic :: inputs => default_real_inputs, double_precision_inputs + procedure, private :: default_real_inputs, double_precision_inputs + generic :: expected_outputs => default_real_expected_outputs, double_precision_expected_outputs + procedure, private :: default_real_expected_outputs, double_precision_expected_outputs end type interface input_output_pair_t @@ -28,6 +28,12 @@ elemental module function default_real_construct(inputs, expected_outputs) resul type(input_output_pair_t) input_output_pair end function + elemental module function double_precision_construct(inputs, expected_outputs) result(input_output_pair) + implicit none + type(tensor_t(double_precision)), intent(in) :: inputs, expected_outputs + type(input_output_pair_t(double_precision)) input_output_pair + end function + end interface interface @@ -38,20 +44,38 @@ elemental module function default_real_inputs(self) result(my_inputs) type(tensor_t) :: my_inputs end function + elemental module function double_precision_inputs(self) result(my_inputs) + implicit none + class(input_output_pair_t(double_precision)), intent(in) :: self + type(tensor_t(double_precision)) :: my_inputs + end function + elemental module function default_real_expected_outputs(self) result(my_expected_outputs) implicit none class(input_output_pair_t), intent(in) :: self type(tensor_t) :: my_expected_outputs end function + elemental module function double_precision_expected_outputs(self) result(my_expected_outputs) + implicit none + class(input_output_pair_t(double_precision)), intent(in) :: self + type(tensor_t(double_precision)) :: my_expected_outputs + end function + end interface interface shuffle + module subroutine default_real_shuffle(pairs) implicit none type(input_output_pair_t), intent(inout) :: pairs(:) end subroutine + module subroutine double_precision_shuffle(pairs) + implicit none + type(input_output_pair_t(double_precision)), intent(inout) :: pairs(:) + end subroutine + end interface interface write_to_stdout @@ -61,6 +85,11 @@ module subroutine default_real_write_to_stdout(input_output_pairs) type(input_output_pair_t), intent(in) :: input_output_pairs(:) end subroutine + module subroutine double_precision_write_to_stdout(input_output_pairs) + implicit none + type(input_output_pair_t(double_precision)), intent(in) :: input_output_pairs(:) + end subroutine + end interface end module input_output_pair_m diff --git a/src/inference_engine/input_output_pair_s.f90 b/src/inference_engine/input_output_pair_s.f90 index 14c910db9..92a960da8 100644 --- a/src/inference_engine/input_output_pair_s.f90 +++ b/src/inference_engine/input_output_pair_s.f90 @@ -10,14 +10,27 @@ input_output_pair%expected_outputs_ = expected_outputs end procedure + module procedure double_precision_construct + input_output_pair%inputs_ = inputs + input_output_pair%expected_outputs_ = expected_outputs + end procedure + module procedure default_real_inputs my_inputs = self%inputs_ end procedure + module procedure double_precision_inputs + my_inputs = self%inputs_ + end procedure + module procedure default_real_expected_outputs my_expected_outputs = self%expected_outputs_ end procedure + module procedure double_precision_expected_outputs + my_expected_outputs = self%expected_outputs_ + end procedure + module procedure default_real_shuffle type(input_output_pair_t) temp real harvest(2:size(pairs)) @@ -35,6 +48,23 @@ end procedure + module procedure double_precision_shuffle + type(input_output_pair_t(double_precision)) temp + double precision harvest(2:size(pairs)) + integer i, j + + call random_number(harvest) + + durstenfeld_shuffle: & + do i = size(pairs), 2, -1 + j = 1 + int(harvest(i)*i) + temp = pairs(i) + pairs(i) = pairs(j) + pairs(j) = temp + end do durstenfeld_shuffle + + end procedure + module procedure default_real_write_to_stdout integer i do i = 1, size(input_output_pairs) @@ -42,4 +72,11 @@ end do end procedure + module procedure double_precision_write_to_stdout + integer i + do i = 1, size(input_output_pairs) + print *, input_output_pairs(i)%inputs_%values(), " | ", input_output_pairs(i)%expected_outputs_%values() + end do + end procedure + end submodule input_output_pair_s From 0e284a6a00a92af310e925d1020ab9f5e2e23d02 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Sat, 31 Aug 2024 19:36:53 -0700 Subject: [PATCH 027/105] feat(mini_match): kind parameter +generic bindings --- src/inference_engine/mini_batch_m.f90 | 14 ++++++++------ src/inference_engine/mini_batch_s.f90 | 4 ++-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/inference_engine/mini_batch_m.f90 b/src/inference_engine/mini_batch_m.f90 index 43f5f63fa..196c002db 100644 --- a/src/inference_engine/mini_batch_m.f90 +++ b/src/inference_engine/mini_batch_m.f90 @@ -2,21 +2,23 @@ ! Terms of use are as specified in LICENSE.txt module mini_batch_m use input_output_pair_m, only : input_output_pair_t + use kind_parameters_m, only : default_real implicit none private public :: mini_batch_t - type mini_batch_t - private - type(input_output_pair_t), allocatable :: input_output_pairs_(:) + type mini_batch_t(k) + integer, kind :: k = default_real + type(input_output_pair_t(k)), private, allocatable :: input_output_pairs_(:) contains - procedure :: input_output_pairs + generic :: input_output_pairs => default_real_input_output_pairs + procedure :: default_real_input_output_pairs end type interface mini_batch_t - pure module function construct(input_output_pairs) result(mini_batch) + pure module function default_real_construct(input_output_pairs) result(mini_batch) implicit none type(input_output_pair_t), intent(in) :: input_output_pairs(:) type(mini_batch_t) mini_batch @@ -26,7 +28,7 @@ pure module function construct(input_output_pairs) result(mini_batch) interface - pure module function input_output_pairs(self) result(my_input_output_pairs) + pure module function default_real_input_output_pairs(self) result(my_input_output_pairs) implicit none class(mini_batch_t), intent(in) :: self type(input_output_pair_t), allocatable :: my_input_output_pairs(:) diff --git a/src/inference_engine/mini_batch_s.f90 b/src/inference_engine/mini_batch_s.f90 index 57a4cc27c..08db6db48 100644 --- a/src/inference_engine/mini_batch_s.f90 +++ b/src/inference_engine/mini_batch_s.f90 @@ -5,11 +5,11 @@ contains - module procedure construct + module procedure default_real_construct mini_batch%input_output_pairs_ = input_output_pairs end procedure - module procedure input_output_pairs + module procedure default_real_input_output_pairs my_input_output_pairs = self%input_output_pairs_ end procedure From 0ff2681b3a5f482087b8a7b930f87f09ef8a4c76 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Sat, 31 Aug 2024 19:40:43 -0700 Subject: [PATCH 028/105] feat(mini_match): support double precision --- src/inference_engine/mini_batch_m.f90 | 18 +++++++++++++++--- src/inference_engine/mini_batch_s.f90 | 8 ++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/inference_engine/mini_batch_m.f90 b/src/inference_engine/mini_batch_m.f90 index 196c002db..10fb0b567 100644 --- a/src/inference_engine/mini_batch_m.f90 +++ b/src/inference_engine/mini_batch_m.f90 @@ -2,7 +2,7 @@ ! Terms of use are as specified in LICENSE.txt module mini_batch_m use input_output_pair_m, only : input_output_pair_t - use kind_parameters_m, only : default_real + use kind_parameters_m, only : default_real, double_precision implicit none private @@ -12,8 +12,8 @@ module mini_batch_m integer, kind :: k = default_real type(input_output_pair_t(k)), private, allocatable :: input_output_pairs_(:) contains - generic :: input_output_pairs => default_real_input_output_pairs - procedure :: default_real_input_output_pairs + generic :: input_output_pairs => default_real_input_output_pairs, double_precision_input_output_pairs + procedure :: default_real_input_output_pairs, double_precision_input_output_pairs end type interface mini_batch_t @@ -24,6 +24,12 @@ pure module function default_real_construct(input_output_pairs) result(mini_batc type(mini_batch_t) mini_batch end function + pure module function double_precision_construct(input_output_pairs) result(mini_batch) + implicit none + type(input_output_pair_t(double_precision)), intent(in) :: input_output_pairs(:) + type(mini_batch_t(double_precision)) mini_batch + end function + end interface interface @@ -34,6 +40,12 @@ pure module function default_real_input_output_pairs(self) result(my_input_outpu type(input_output_pair_t), allocatable :: my_input_output_pairs(:) end function + pure module function double_precision_input_output_pairs(self) result(my_input_output_pairs) + implicit none + class(mini_batch_t(double_precision)), intent(in) :: self + type(input_output_pair_t(double_precision)), allocatable :: my_input_output_pairs(:) + end function + end interface end module mini_batch_m diff --git a/src/inference_engine/mini_batch_s.f90 b/src/inference_engine/mini_batch_s.f90 index 08db6db48..e4f126505 100644 --- a/src/inference_engine/mini_batch_s.f90 +++ b/src/inference_engine/mini_batch_s.f90 @@ -9,8 +9,16 @@ mini_batch%input_output_pairs_ = input_output_pairs end procedure + module procedure double_precision_construct + mini_batch%input_output_pairs_ = input_output_pairs + end procedure + module procedure default_real_input_output_pairs my_input_output_pairs = self%input_output_pairs_ end procedure + module procedure double_precision_input_output_pairs + my_input_output_pairs = self%input_output_pairs_ + end procedure + end submodule mini_batch_s From 7ea4e3bbf3cc2c2687a4a7ecd04fbe2e1a260054 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Sat, 31 Aug 2024 23:53:30 -0700 Subject: [PATCH 029/105] feat(hyperparameters): support double precision also update julienne dependency to 1.2.0 for double-precsion value extraction from JSON files. --- fpm.toml | 2 +- src/inference_engine/hyperparameters_m.f90 | 96 ++++++++++++++++------ src/inference_engine/hyperparameters_s.f90 | 87 ++++++++++++++++++-- 3 files changed, 151 insertions(+), 34 deletions(-) diff --git a/fpm.toml b/fpm.toml index e35768e80..033569f9d 100644 --- a/fpm.toml +++ b/fpm.toml @@ -8,4 +8,4 @@ maintainer = "rouson@lbl.gov" assert = {git = "https://github.com/sourceryinstitute/assert", tag = "1.7.0"} [dev-dependencies] -julienne = {git = "https://github.com/berkeleylab/julienne", tag = "1.1.1"} +julienne = {git = "https://github.com/berkeleylab/julienne", tag = "1.2.0"} diff --git a/src/inference_engine/hyperparameters_m.f90 b/src/inference_engine/hyperparameters_m.f90 index 9e55dc376..e6d41a529 100644 --- a/src/inference_engine/hyperparameters_m.f90 +++ b/src/inference_engine/hyperparameters_m.f90 @@ -1,34 +1,44 @@ module hyperparameters_m use julienne_string_m, only : string_t - use kind_parameters_m, only : rkind + use kind_parameters_m, only : default_real, double_precision + use double_precision_string_m, only : double_precision_string_t implicit none - private public :: hyperparameters_t - type hyperparameters_t - private - integer :: mini_batches_ = 10 - real :: learning_rate_ = 1.5 + type hyperparameters_t(k) + integer, kind :: k = default_real + integer, private:: mini_batches_ = 10 + real(k), private :: learning_rate_ = 1.5_k character(len=:), allocatable :: optimizer_ contains - procedure :: to_json - procedure :: equals - generic :: operator(==) => equals - procedure :: mini_batches - procedure :: optimizer_name - procedure :: learning_rate - end type + generic :: to_json => default_real_to_json, double_precision_to_json + procedure, private :: default_real_to_json, double_precision_to_json + generic :: operator(==) => default_real_equals, double_precision_equals + procedure, private :: default_real_equals, double_precision_equals + generic :: mini_batches => default_real_mini_batches, double_precision_mini_batches + procedure, private :: default_real_mini_batches, double_precision_mini_batches + generic :: optimizer_name => default_real_optimizer_name, double_precision_optimizer_name + procedure, private :: default_real_optimizer_name, double_precision_optimizer_name + generic :: learning_rate => default_real_learning_rate, double_precision_learning_rate + procedure, private :: default_real_learning_rate, double_precision_learning_rate + end type interface hyperparameters_t - pure module function from_json(lines) result(hyperparameters) + pure module function default_real_from_json(lines) result(hyperparameters) implicit none type(string_t), intent(in) :: lines(:) type(hyperparameters_t) hyperparameters end function - pure module function from_components(mini_batches, learning_rate, optimizer) result(hyperparameters) + pure module function double_precision_from_json(lines) result(hyperparameters) + implicit none + type(double_precision_string_t), intent(in) :: lines(:) + type(hyperparameters_t(double_precision)) hyperparameters + end function + + pure module function default_real_from_components(mini_batches, learning_rate, optimizer) result(hyperparameters) implicit none integer, intent(in) :: mini_batches real, intent(in) :: learning_rate @@ -36,40 +46,78 @@ pure module function from_components(mini_batches, learning_rate, optimizer) res type(hyperparameters_t) hyperparameters end function + pure module function double_precision_from_components(mini_batches, learning_rate, optimizer) result(hyperparameters) + implicit none + integer, intent(in) :: mini_batches + double precision, intent(in) :: learning_rate + character(len=*), intent(in) :: optimizer + type(hyperparameters_t(double_precision)) hyperparameters + end function + end interface interface - pure module function to_json(self) result(lines) + pure module function default_real_to_json(self) result(lines) implicit none class(hyperparameters_t), intent(in) :: self type(string_t), allocatable :: lines(:) end function - elemental module function equals(lhs, rhs) result(lhs_equals_rhs) + pure module function double_precision_to_json(self) result(lines) + implicit none + class(hyperparameters_t(double_precision)), intent(in) :: self + type(string_t), allocatable :: lines(:) + end function + + elemental module function default_real_equals(lhs, rhs) result(lhs_equals_rhs) implicit none class(hyperparameters_t), intent(in) :: lhs, rhs logical lhs_equals_rhs end function - elemental module function mini_batches(self) result(num_mini_batches) + elemental module function double_precision_equals(lhs, rhs) result(lhs_equals_rhs) + implicit none + class(hyperparameters_t(double_precision)), intent(in) :: lhs, rhs + logical lhs_equals_rhs + end function + + elemental module function default_real_mini_batches(self) result(num_mini_batches) implicit none class(hyperparameters_t), intent(in) :: self integer num_mini_batches end function - elemental module function optimizer_name(self) result(identifier) + elemental module function double_precision_mini_batches(self) result(num_mini_batches) + implicit none + class(hyperparameters_t(double_precision)), intent(in) :: self + integer num_mini_batches + end function + + elemental module function default_real_learning_rate(self) result(rate) + implicit none + class(hyperparameters_t), intent(in) :: self + real rate + end function + + elemental module function double_precision_learning_rate(self) result(rate) + implicit none + class(hyperparameters_t(double_precision)), intent(in) :: self + double precision rate + end function + + elemental module function default_real_optimizer_name(self) result(identifier) implicit none class(hyperparameters_t), intent(in) :: self type(string_t) identifier end function - - elemental module function learning_rate(self) result(rate) - implicit none - class(hyperparameters_t), intent(in) :: self - real(rkind) rate + elemental module function double_precision_optimizer_name(self) result(identifier) + implicit none + class(hyperparameters_t(double_precision)), intent(in) :: self + type(string_t) identifier end function + end interface end module diff --git a/src/inference_engine/hyperparameters_s.f90 b/src/inference_engine/hyperparameters_s.f90 index f6b442008..229d1d637 100644 --- a/src/inference_engine/hyperparameters_s.f90 +++ b/src/inference_engine/hyperparameters_s.f90 @@ -8,17 +8,38 @@ contains - module procedure from_components + module procedure default_real_from_components hyperparameters%mini_batches_ = mini_batches hyperparameters%learning_rate_ = learning_rate hyperparameters%optimizer_ = optimizer end procedure - module procedure equals + module procedure double_precision_from_components + hyperparameters%mini_batches_ = mini_batches + hyperparameters%learning_rate_ = learning_rate + hyperparameters%optimizer_ = optimizer + end procedure + + module procedure default_real_equals real, parameter :: tolerance = 1.E-08 - call assert(allocated(lhs%optimizer_) .and. allocated(rhs%optimizer_), "hyperparameters_s(equals): allocated optimizers") + call assert(allocated(lhs%optimizer_) .and. allocated(rhs%optimizer_), & + "hyperparameters_s(default_real_equals): allocated optimizers") + + lhs_equals_rhs = & + lhs%mini_batches_ == rhs%mini_batches_ .and. & + lhs%optimizer_ == rhs%optimizer_ .and. & + abs(lhs%learning_rate_ - rhs%learning_rate_) <= tolerance + + end procedure + + module procedure double_precision_equals + + double precision, parameter :: tolerance = 1.D-15 + + call assert(allocated(lhs%optimizer_) .and. allocated(rhs%optimizer_), & + "hyperparameters_s(double_precisionequals): allocated optimizers") lhs_equals_rhs = & lhs%mini_batches_ == rhs%mini_batches_ .and. & @@ -27,7 +48,7 @@ end procedure - module procedure from_json + module procedure default_real_from_json integer l logical hyperparameters_key_found @@ -43,10 +64,29 @@ end if end do - call assert(hyperparameters_key_found, "hyperparameters_s(from_json): hyperparameters_found") + call assert(hyperparameters_key_found, "hyperparameters_s(default_real_from_json): hyperparameters_found") end procedure - module procedure to_json + module procedure double_precision_from_json + integer l + logical hyperparameters_key_found + + hyperparameters_key_found = .false. + + do l=1,size(lines) + if (lines(l)%get_json_key() == "hyperparameters") then + hyperparameters_key_found = .true. + hyperparameters%mini_batches_ = lines(l+1)%get_json_value(string_t(mini_batches_key), mold=0) + hyperparameters%learning_rate_ = lines(l+2)%get_json_value(string_t(learning_rate_key), mold=0.D0) + hyperparameters%optimizer_ = lines(l+3)%get_json_value(string_t(optimizer_key), mold=string_t("")) + return + end if + end do + + call assert(hyperparameters_key_found, "hyperparameters_s(double_precision_from_json): hyperparameters_found") + end procedure + + module procedure default_real_to_json character(len=*), parameter :: indent = repeat(" ",ncopies=4) integer, parameter :: max_width= 18 character(len=max_width) mini_batches_string, learning_rate_string @@ -63,15 +103,44 @@ ] end procedure - module procedure mini_batches + module procedure double_precision_to_json + character(len=*), parameter :: indent = repeat(" ",ncopies=4) + integer, parameter :: max_width= 36 + character(len=max_width) mini_batches_string, learning_rate_string + + write(mini_batches_string,*) self%mini_batches_ + write(learning_rate_string,*) self%learning_rate_ + + lines = [ & + string_t(indent // '"hyperparameters": {'), & + string_t(indent // indent // '"' // mini_batches_key // '" : ' // trim(adjustl(mini_batches_string)) // "," ), & + string_t(indent // indent // '"' // learning_rate_key // '" : ' // trim(adjustl(learning_rate_string)) // "," ), & + string_t(indent // indent // '"' // optimizer_key // '" : "' // trim(adjustl(self%optimizer_ )) // '"'), & + string_t(indent // '}') & + ] + end procedure + + module procedure default_real_mini_batches + num_mini_batches = self%mini_batches_ + end procedure + + module procedure double_precision_mini_batches num_mini_batches = self%mini_batches_ end procedure - module procedure optimizer_name + module procedure default_real_optimizer_name identifier = string_t(self%optimizer_) end procedure - module procedure learning_rate + module procedure double_precision_optimizer_name + identifier = string_t(self%optimizer_) + end procedure + + module procedure default_real_learning_rate + rate = self%learning_rate_ + end procedure + + module procedure double_precision_learning_rate rate = self%learning_rate_ end procedure From d3635876211b8f35a80fb4af116c9e8969ccd17f Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Sun, 1 Sep 2024 00:36:08 -0700 Subject: [PATCH 030/105] fix(tensor_t): set component kind parameter --- src/inference_engine/tensor_m.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/inference_engine/tensor_m.f90 b/src/inference_engine/tensor_m.f90 index bea936ad3..859331f1c 100644 --- a/src/inference_engine/tensor_m.f90 +++ b/src/inference_engine/tensor_m.f90 @@ -9,7 +9,7 @@ module tensor_m type tensor_t(k) integer, kind :: k = default_real - real, allocatable, private :: values_(:) + real(k), allocatable, private :: values_(:) contains generic :: values => default_real_values, double_precision_values procedure, private :: default_real_values, double_precision_values From d3da4e64c59f4cd3c0633e1eeeacf93801d37bd6 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Sun, 1 Sep 2024 06:52:35 -0700 Subject: [PATCH 031/105] feat(training_configuration_t): add kind parameter --- src/inference_engine/training_configuration_m.f90 | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/inference_engine/training_configuration_m.f90 b/src/inference_engine/training_configuration_m.f90 index 7805675a3..552864630 100644 --- a/src/inference_engine/training_configuration_m.f90 +++ b/src/inference_engine/training_configuration_m.f90 @@ -3,17 +3,17 @@ module training_configuration_m use julienne_file_m, only : file_t use hyperparameters_m, only : hyperparameters_t use network_configuration_m, only : network_configuration_t - use kind_parameters_m, only : rkind + use kind_parameters_m, only : default_real use differentiable_activation_strategy_m, only : differentiable_activation_strategy_t implicit none private public :: training_configuration_t - type, extends(file_t) :: training_configuration_t - private - type(hyperparameters_t) hyperparameters_ - type(network_configuration_t) network_configuration_ + type, extends(file_t) :: training_configuration_t(k) + integer, kind :: k = default_real + type(hyperparameters_t(k)), private :: hyperparameters_ + type(network_configuration_t), private :: network_configuration_ contains procedure :: to_json procedure :: equals @@ -72,7 +72,7 @@ elemental module function optimizer_name(self) result(identifier) elemental module function learning_rate(self) result(rate) implicit none class(training_configuration_t), intent(in) :: self - real(rkind) rate + real rate end function module function differentiable_activation_strategy(self) result(strategy) From 0d6da762493f1a7a530630e232b59c78e1ca120f Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Sun, 1 Sep 2024 06:59:23 -0700 Subject: [PATCH 032/105] refac(training_configuration): mk bindings generic --- .../training_configuration_m.f90 | 45 +++++++++++-------- .../training_configuration_s.F90 | 20 ++++----- 2 files changed, 36 insertions(+), 29 deletions(-) diff --git a/src/inference_engine/training_configuration_m.f90 b/src/inference_engine/training_configuration_m.f90 index 552864630..3f658b210 100644 --- a/src/inference_engine/training_configuration_m.f90 +++ b/src/inference_engine/training_configuration_m.f90 @@ -15,27 +15,34 @@ module training_configuration_m type(hyperparameters_t(k)), private :: hyperparameters_ type(network_configuration_t), private :: network_configuration_ contains - procedure :: to_json - procedure :: equals - generic :: operator(==) => equals - procedure :: mini_batches - procedure :: optimizer_name - procedure :: learning_rate - procedure :: differentiable_activation_strategy - procedure :: nodes_per_layer - procedure :: skip_connections + generic :: operator(==) => default_real_equals + procedure :: default_real_equals + generic :: to_json => default_real_to_json + procedure, private :: default_real_to_json + generic :: mini_batches => default_real_mini_batches + procedure, private :: default_real_mini_batches + generic :: optimizer_name => default_real_optimizer_name + procedure, private :: default_real_optimizer_name + generic :: learning_rate => default_real_learning_rate + procedure, private :: default_real_learning_rate + generic :: differentiable_activation_strategy => default_real_differentiable_activation_strategy + procedure, private :: default_real_differentiable_activation_strategy + generic :: nodes_per_layer => default_real_nodes_per_layer + procedure, private :: default_real_nodes_per_layer + generic :: skip_connections => default_real_skip_connections + procedure, private :: default_real_skip_connections end type interface training_configuration_t - module function from_components(hyperparameters, network_configuration) result(training_configuration) + module function default_real_from_components(hyperparameters, network_configuration) result(training_configuration) implicit none type(hyperparameters_t), intent(in) :: hyperparameters type(network_configuration_t), intent(in) :: network_configuration type(training_configuration_t) training_configuration end function - module function from_file(file_object) result(training_configuration) + module function default_real_from_file(file_object) result(training_configuration) implicit none type(file_t), intent(in) :: file_object type(training_configuration_t) training_configuration @@ -45,49 +52,49 @@ module function from_file(file_object) result(training_configuration) interface - pure module function to_json(self) result(json_lines) + pure module function default_real_to_json(self) result(json_lines) implicit none class(training_configuration_t), intent(in) :: self type(string_t), allocatable :: json_lines(:) end function - elemental module function equals(lhs, rhs) result(lhs_eq_rhs) + elemental module function default_real_equals(lhs, rhs) result(lhs_eq_rhs) implicit none class(training_configuration_t), intent(in) :: lhs, rhs logical lhs_eq_rhs end function - elemental module function mini_batches(self) result(num_mini_batches) + elemental module function default_real_mini_batches(self) result(num_mini_batches) implicit none class(training_configuration_t), intent(in) :: self integer num_mini_batches end function - elemental module function optimizer_name(self) result(identifier) + elemental module function default_real_optimizer_name(self) result(identifier) implicit none class(training_configuration_t), intent(in) :: self type(string_t) identifier end function - elemental module function learning_rate(self) result(rate) + elemental module function default_real_learning_rate(self) result(rate) implicit none class(training_configuration_t), intent(in) :: self real rate end function - module function differentiable_activation_strategy(self) result(strategy) + module function default_real_differentiable_activation_strategy(self) result(strategy) implicit none class(training_configuration_t), intent(in) :: self class(differentiable_activation_strategy_t), allocatable :: strategy end function - pure module function nodes_per_layer(self) result(nodes) + pure module function default_real_nodes_per_layer(self) result(nodes) implicit none class(training_configuration_t), intent(in) :: self integer, allocatable :: nodes(:) end function - elemental module function skip_connections(self) result(using_skip) + elemental module function default_real_skip_connections(self) result(using_skip) implicit none class(training_configuration_t), intent(in) :: self logical using_skip diff --git a/src/inference_engine/training_configuration_s.F90 b/src/inference_engine/training_configuration_s.F90 index 3ffbe1882..9d73d8b0f 100644 --- a/src/inference_engine/training_configuration_s.F90 +++ b/src/inference_engine/training_configuration_s.F90 @@ -7,7 +7,7 @@ contains - module procedure from_components + module procedure default_real_from_components training_configuration%hyperparameters_ = hyperparameters training_configuration%network_configuration_ = network_configuration @@ -20,7 +20,7 @@ ]) end procedure - module procedure from_file + module procedure default_real_from_file integer, parameter :: hyperparameters_start=2, hyperparameters_end=6, separator_line=7 ! line numbers integer, parameter :: net_config_start=8, net_config_end=12 ! line numbers integer, parameter :: file_start=hyperparameters_start-1, file_end=net_config_end+1 ! line numbers @@ -48,37 +48,37 @@ end procedure - module procedure to_json + module procedure default_real_to_json json_lines = self%lines() end procedure - module procedure equals + module procedure default_real_equals lhs_eq_rhs = & lhs%hyperparameters_ == rhs%hyperparameters_ .and. & lhs%network_configuration_ == rhs%network_configuration_ end procedure - module procedure mini_batches + module procedure default_real_mini_batches num_mini_batches = self%hyperparameters_%mini_batches() end procedure - module procedure optimizer_name + module procedure default_real_optimizer_name identifier = self%hyperparameters_%optimizer_name() end procedure - module procedure learning_rate + module procedure default_real_learning_rate rate = self%hyperparameters_%learning_rate() end procedure - module procedure nodes_per_layer + module procedure default_real_nodes_per_layer nodes = self%network_configuration_%nodes_per_layer() end procedure - module procedure skip_connections + module procedure default_real_skip_connections using_skip = self%network_configuration_%skip_connections() end procedure - module procedure differentiable_activation_strategy + module procedure default_real_differentiable_activation_strategy #if defined __INTEL_COMPILER || _CRAYFTN type(string_t) :: activation_name activation_name = self%network_configuration_%activation_name() From 533ce135a39c5cc5fe8647bee0b476d0469b7ad1 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Sun, 1 Sep 2024 14:01:53 -0700 Subject: [PATCH 033/105] feat(dble_prec_file):extend file_t, produce dp_str --- .../double_precision_file_m.f90 | 22 +++ .../double_precision_file_s.f90 | 10 ++ .../double_precision_string_m.f90 | 10 ++ .../double_precision_string_s.f90 | 9 ++ .../network_configuration_m.f90 | 7 + .../network_configuration_s.F90 | 20 +++ .../training_configuration_m.f90 | 100 ++++++++++--- .../training_configuration_s.F90 | 134 +++++++++++++++++- 8 files changed, 290 insertions(+), 22 deletions(-) create mode 100644 src/inference_engine/double_precision_file_m.f90 create mode 100644 src/inference_engine/double_precision_file_s.f90 create mode 100644 src/inference_engine/double_precision_string_s.f90 diff --git a/src/inference_engine/double_precision_file_m.f90 b/src/inference_engine/double_precision_file_m.f90 new file mode 100644 index 000000000..90b50cd98 --- /dev/null +++ b/src/inference_engine/double_precision_file_m.f90 @@ -0,0 +1,22 @@ +module double_precision_file_m + use julienne_m, only : file_t + use double_precision_string_m, only : double_precision_string_t + + implicit none + + type, extends(file_t) :: double_precision_file_t + contains + procedure double_precision_lines + end type + + interface + + module function double_precision_lines(self) result(lines) + implicit none + class(double_precision_file_t), intent(in) :: self + type(double_precision_string_t), allocatable :: lines(:) + end function + + end interface + +end module double_precision_file_m diff --git a/src/inference_engine/double_precision_file_s.f90 b/src/inference_engine/double_precision_file_s.f90 new file mode 100644 index 000000000..9d8eec923 --- /dev/null +++ b/src/inference_engine/double_precision_file_s.f90 @@ -0,0 +1,10 @@ +submodule(double_precision_file_m) double_precision_file_s + implicit none + +contains + + module procedure double_precision_lines + lines = double_precision_string_t(self%file_t%lines()) + end procedure + +end submodule double_precision_file_s diff --git a/src/inference_engine/double_precision_string_m.f90 b/src/inference_engine/double_precision_string_m.f90 index 54edde09d..14f0cdea0 100644 --- a/src/inference_engine/double_precision_string_m.f90 +++ b/src/inference_engine/double_precision_string_m.f90 @@ -5,4 +5,14 @@ module double_precision_string_m type, extends(string_t) :: double_precision_string_t end type + interface double_precision_string_t + + elemental module function construct_from_string(string) result(double_precision_string) + implicit none + type(string_t), intent(in) :: string + type(double_precision_string_t) double_precision_string + end function + + end interface + end module double_precision_string_m diff --git a/src/inference_engine/double_precision_string_s.f90 b/src/inference_engine/double_precision_string_s.f90 new file mode 100644 index 000000000..9e4ff72d3 --- /dev/null +++ b/src/inference_engine/double_precision_string_s.f90 @@ -0,0 +1,9 @@ +submodule(double_precision_string_m)double_precision_string_s + implicit none +contains + + module procedure construct_from_string + double_precision_string%string_t = string + end procedure + +end submodule double_precision_string_s diff --git a/src/inference_engine/network_configuration_m.f90 b/src/inference_engine/network_configuration_m.f90 index 453c912d1..5d2f34cc5 100644 --- a/src/inference_engine/network_configuration_m.f90 +++ b/src/inference_engine/network_configuration_m.f90 @@ -1,5 +1,6 @@ module network_configuration_m use julienne_string_m, only : string_t + use double_precision_string_m, only : double_precision_string_t implicit none private @@ -27,6 +28,12 @@ pure module function from_json(lines) result(network_configuration) type(network_configuration_t) network_configuration end function + pure module function from_double_precision_string_json(lines) result(network_configuration) + implicit none + type(double_precision_string_t), intent(in) :: lines(:) + type(network_configuration_t) network_configuration + end function + pure module function from_components(skip_connections, nodes_per_layer, activation_name) result(network_configuration) implicit none logical, intent(in) :: skip_connections diff --git a/src/inference_engine/network_configuration_s.F90 b/src/inference_engine/network_configuration_s.F90 index ef6ff7f69..8100d9fa1 100644 --- a/src/inference_engine/network_configuration_s.F90 +++ b/src/inference_engine/network_configuration_s.F90 @@ -46,6 +46,26 @@ call assert(network_configuration_key_found, "network_configuration_s(from_json): network_configuration_found") end procedure + module procedure from_double_precision_string_json + integer l + logical network_configuration_key_found + + network_configuration_key_found = .false. + + do l=1,size(lines) + if (lines(l)%get_json_key() == "network configuration") then + network_configuration_key_found = .true. + network_configuration%skip_connections_ = lines(l+1)%get_json_value(string_t(skip_connections_key), mold=.true.) + network_configuration%nodes_per_layer_ = lines(l+2)%get_json_value(string_t(nodes_per_layer_key), mold=[integer::]) + network_configuration%activation_name_ = lines(l+3)%get_json_value(string_t(activation_name_key), mold=string_t("")) + return + end if + end do + + call assert(network_configuration_key_found, & + "network_configuration_s(from_double_precision_string_json): network_configuration_found") + end procedure + module procedure to_json character(len=*), parameter :: indent = repeat(" ",ncopies=4) integer, parameter :: max_logical_width= 6, char_per_elem = 10, brackets = 2 diff --git a/src/inference_engine/training_configuration_m.f90 b/src/inference_engine/training_configuration_m.f90 index 3f658b210..2c610b295 100644 --- a/src/inference_engine/training_configuration_m.f90 +++ b/src/inference_engine/training_configuration_m.f90 @@ -3,34 +3,37 @@ module training_configuration_m use julienne_file_m, only : file_t use hyperparameters_m, only : hyperparameters_t use network_configuration_m, only : network_configuration_t - use kind_parameters_m, only : default_real + use kind_parameters_m, only : default_real, double_precision use differentiable_activation_strategy_m, only : differentiable_activation_strategy_t + use double_precision_file_m, only : double_precision_file_t implicit none private public :: training_configuration_t - type, extends(file_t) :: training_configuration_t(k) + type, extends(double_precision_file_t) :: training_configuration_t(k) integer, kind :: k = default_real type(hyperparameters_t(k)), private :: hyperparameters_ type(network_configuration_t), private :: network_configuration_ contains - generic :: operator(==) => default_real_equals - procedure :: default_real_equals - generic :: to_json => default_real_to_json - procedure, private :: default_real_to_json - generic :: mini_batches => default_real_mini_batches - procedure, private :: default_real_mini_batches - generic :: optimizer_name => default_real_optimizer_name - procedure, private :: default_real_optimizer_name - generic :: learning_rate => default_real_learning_rate - procedure, private :: default_real_learning_rate - generic :: differentiable_activation_strategy => default_real_differentiable_activation_strategy - procedure, private :: default_real_differentiable_activation_strategy - generic :: nodes_per_layer => default_real_nodes_per_layer - procedure, private :: default_real_nodes_per_layer - generic :: skip_connections => default_real_skip_connections - procedure, private :: default_real_skip_connections + generic :: operator(==) => default_real_equals, double_precision_equals + procedure, private :: default_real_equals, double_precision_equals + generic :: to_json => default_real_to_json, double_precision_to_json + procedure, private :: default_real_to_json, double_precision_to_json + generic :: mini_batches => default_real_mini_batches, double_precision_mini_batches + procedure, private :: default_real_mini_batches, double_precision_mini_batches + generic :: optimizer_name => default_real_optimizer_name, double_precision_optimizer_name + procedure, private :: default_real_optimizer_name, double_precision_optimizer_name + generic :: learning_rate => default_real_learning_rate, double_precision_learning_rate + procedure, private :: default_real_learning_rate, double_precision_learning_rate + generic :: differentiable_activation_strategy => & + default_real_differentiable_activation_strategy, double_precision_differentiable_activation_strategy + procedure, private :: & + default_real_differentiable_activation_strategy, double_precision_differentiable_activation_strategy + generic :: nodes_per_layer => default_real_nodes_per_layer, double_precision_nodes_per_layer + procedure, private :: default_real_nodes_per_layer, double_precision_nodes_per_layer + generic :: skip_connections => default_real_skip_connections, double_precision_skip_connections + procedure, private :: default_real_skip_connections, double_precision_skip_connections end type interface training_configuration_t @@ -42,12 +45,25 @@ module function default_real_from_components(hyperparameters, network_configurat type(training_configuration_t) training_configuration end function + module function double_precision_from_components(hyperparameters, network_configuration) result(training_configuration) + implicit none + type(hyperparameters_t(double_precision)), intent(in) :: hyperparameters + type(network_configuration_t), intent(in) :: network_configuration + type(training_configuration_t(double_precision)) training_configuration + end function + module function default_real_from_file(file_object) result(training_configuration) implicit none type(file_t), intent(in) :: file_object type(training_configuration_t) training_configuration end function + module function double_precision_from_file(file_object) result(training_configuration) + implicit none + type(double_precision_file_t), intent(in) :: file_object + type(training_configuration_t(double_precision)) training_configuration + end function + end interface interface @@ -58,47 +74,95 @@ pure module function default_real_to_json(self) result(json_lines) type(string_t), allocatable :: json_lines(:) end function + pure module function double_precision_to_json(self) result(json_lines) + implicit none + class(training_configuration_t(double_precision)), intent(in) :: self + type(string_t), allocatable :: json_lines(:) + end function + elemental module function default_real_equals(lhs, rhs) result(lhs_eq_rhs) implicit none class(training_configuration_t), intent(in) :: lhs, rhs logical lhs_eq_rhs end function + elemental module function double_precision_equals(lhs, rhs) result(lhs_eq_rhs) + implicit none + class(training_configuration_t(double_precision)), intent(in) :: lhs, rhs + logical lhs_eq_rhs + end function + elemental module function default_real_mini_batches(self) result(num_mini_batches) implicit none class(training_configuration_t), intent(in) :: self integer num_mini_batches end function + elemental module function double_precision_mini_batches(self) result(num_mini_batches) + implicit none + class(training_configuration_t(double_precision)), intent(in) :: self + integer num_mini_batches + end function + elemental module function default_real_optimizer_name(self) result(identifier) implicit none class(training_configuration_t), intent(in) :: self type(string_t) identifier end function + elemental module function double_precision_optimizer_name(self) result(identifier) + implicit none + class(training_configuration_t(double_precision)), intent(in) :: self + type(string_t) identifier + end function + elemental module function default_real_learning_rate(self) result(rate) implicit none class(training_configuration_t), intent(in) :: self real rate end function + elemental module function double_precision_learning_rate(self) result(rate) + implicit none + class(training_configuration_t(double_precision)), intent(in) :: self + double precision rate + end function + module function default_real_differentiable_activation_strategy(self) result(strategy) implicit none class(training_configuration_t), intent(in) :: self class(differentiable_activation_strategy_t), allocatable :: strategy end function + + module function double_precision_differentiable_activation_strategy(self) result(strategy) + implicit none + class(training_configuration_t(double_precision)), intent(in) :: self + class(differentiable_activation_strategy_t), allocatable :: strategy + end function pure module function default_real_nodes_per_layer(self) result(nodes) implicit none class(training_configuration_t), intent(in) :: self integer, allocatable :: nodes(:) end function + + pure module function double_precision_nodes_per_layer(self) result(nodes) + implicit none + class(training_configuration_t(double_precision)), intent(in) :: self + integer, allocatable :: nodes(:) + end function elemental module function default_real_skip_connections(self) result(using_skip) implicit none class(training_configuration_t), intent(in) :: self logical using_skip end function + + elemental module function double_precision_skip_connections(self) result(using_skip) + implicit none + class(training_configuration_t(double_precision)), intent(in) :: self + logical using_skip + end function end interface diff --git a/src/inference_engine/training_configuration_s.F90 b/src/inference_engine/training_configuration_s.F90 index 9d73d8b0f..9a9651e76 100644 --- a/src/inference_engine/training_configuration_s.F90 +++ b/src/inference_engine/training_configuration_s.F90 @@ -1,6 +1,7 @@ submodule(training_configuration_m) training_configuration_s use assert_m, only : assert use inference_engine_m, only : gelu_t, relu_t, sigmoid_t, swish_t + use double_precision_string_m, only : double_precision_string_t implicit none character(len=*), parameter :: header="{", footer="}", separator = "," @@ -20,6 +21,19 @@ ]) end procedure + module procedure double_precision_from_components + + training_configuration%hyperparameters_ = hyperparameters + training_configuration%network_configuration_ = network_configuration + training_configuration%file_t = file_t([ & + string_t(header), & + training_configuration%hyperparameters_%to_json(), & + string_t(separator), & + training_configuration%network_configuration_%to_json(), & + string_t(footer) & + ]) + end procedure + module procedure default_real_from_file integer, parameter :: hyperparameters_start=2, hyperparameters_end=6, separator_line=7 ! line numbers integer, parameter :: net_config_start=8, net_config_end=12 ! line numbers @@ -32,18 +46,75 @@ #if defined __INTEL_COMPILER || _CRAYFTN lines = training_configuration%file_t%lines() + call assert(trim(adjustl(lines(file_start)%string()))==header, & + "training_configuration_s(default_precision_from_file): header",lines(file_start)) + training_configuration%hyperparameters_ = hyperparameters_t(lines(hyperparameters_start:hyperparameters_end)) + call assert(trim(adjustl(lines(separator_line)%string()))==separator, & + "training_configuration_s(default_precision_from_file): separator", & + lines(file_start)) + training_configuration%network_configuration_= network_configuration_t(lines(net_config_start:net_config_end)) + call assert(trim(adjustl(lines(file_end)%string()))==footer, & + "training_configuration_s(default_precision_from_file): footer", lines(file_end)) #else associate(lines => training_configuration%file_t%lines()) -#endif - call assert(trim(adjustl(lines(file_start)%string()))==header,"training_configuration_s(from_file): header",lines(file_start)) + call assert(trim(adjustl(lines(file_start)%string()))==header, & + "training_configuration_s(default_precision_from_file): header",lines(file_start)) training_configuration%hyperparameters_ = hyperparameters_t(lines(hyperparameters_start:hyperparameters_end)) - call assert(trim(adjustl(lines(separator_line)%string()))==separator,"training_configuration_s(from_file): separator", & + call assert(trim(adjustl(lines(separator_line)%string()))==separator, & + "training_configuration_s(default_precision_from_file): separator", & lines(file_start)) training_configuration%network_configuration_= network_configuration_t(lines(net_config_start:net_config_end)) - call assert(trim(adjustl(lines(file_end)%string()))==footer, "training_configuration_s(from_file): footer", lines(file_end)) + call assert(trim(adjustl(lines(file_end)%string()))==footer, & + "training_configuration_s(default_precision_from_file): footer", lines(file_end)) + end associate +#endif + + end procedure + + module procedure double_precision_from_file + integer, parameter :: hyperparameters_start=2, hyperparameters_end=6, separator_line=7 ! line numbers + integer, parameter :: net_config_start=8, net_config_end=12 ! line numbers + integer, parameter :: file_start=hyperparameters_start-1, file_end=net_config_end+1 ! line numbers +#if defined __INTEL_COMPILER || _CRAYFTN + type(double_precision_string_t), allocatable :: lines(:) +#endif + + training_configuration%double_precision_file_t = file_object + #if defined __INTEL_COMPILER || _CRAYFTN + lines = training_configuration%double_precision_file_t%double_precision_lines() + + call assert(adjustl(lines(file_start)%string()) == header, & + "training_configuration_s(double_precision_from_file): header",lines(file_start)) + + training_configuration%hyperparameters_ = hyperparameters_t(lines(hyperparameters_start:hyperparameters_end)) + + call assert(adjustl(lines(separator_line)%string()) == separator, & + "training_configuration_s(double_precision_from_file): separator", lines(file_start)) + + training_configuration%network_configuration_= network_configuration_t(lines(net_config_start:net_config_end)) + + call assert(adjustl(lines(file_end)%string()) == footer, & + "training_configuration_s(double_precision_from_file): footer", lines(file_end)) #else + + associate(lines => training_configuration%double_precision_file_t%double_precision_lines()) + + call assert(adjustl(lines(file_start)%string()) == header, & + "training_configuration_s(double_precision_from_file): header", lines(file_start)) + + training_configuration%hyperparameters_ = hyperparameters_t(lines(hyperparameters_start:hyperparameters_end)) + + call assert(adjustl(lines(separator_line)%string()) == separator, & + "training_configuration_s(double_precision_from_file): separator", lines(file_start)) + + training_configuration%network_configuration_= network_configuration_t(lines(net_config_start:net_config_end)) + + call assert(adjustl(lines(file_end)%string()) == footer, & + "training_configuration_s(double_precision_from_file): footer", lines(file_end)) + end associate + #endif end procedure @@ -52,32 +123,62 @@ json_lines = self%lines() end procedure + module procedure double_precision_to_json + json_lines = self%lines() + end procedure + module procedure default_real_equals lhs_eq_rhs = & lhs%hyperparameters_ == rhs%hyperparameters_ .and. & lhs%network_configuration_ == rhs%network_configuration_ end procedure + module procedure double_precision_equals + lhs_eq_rhs = & + lhs%hyperparameters_ == rhs%hyperparameters_ .and. & + lhs%network_configuration_ == rhs%network_configuration_ + end procedure + module procedure default_real_mini_batches num_mini_batches = self%hyperparameters_%mini_batches() end procedure + module procedure double_precision_mini_batches + num_mini_batches = self%hyperparameters_%mini_batches() + end procedure + module procedure default_real_optimizer_name identifier = self%hyperparameters_%optimizer_name() end procedure + module procedure double_precision_optimizer_name + identifier = self%hyperparameters_%optimizer_name() + end procedure + module procedure default_real_learning_rate rate = self%hyperparameters_%learning_rate() end procedure + module procedure double_precision_learning_rate + rate = self%hyperparameters_%learning_rate() + end procedure + module procedure default_real_nodes_per_layer nodes = self%network_configuration_%nodes_per_layer() end procedure + module procedure double_precision_nodes_per_layer + nodes = self%network_configuration_%nodes_per_layer() + end procedure + module procedure default_real_skip_connections using_skip = self%network_configuration_%skip_connections() end procedure + module procedure double_precision_skip_connections + using_skip = self%network_configuration_%skip_connections() + end procedure + module procedure default_real_differentiable_activation_strategy #if defined __INTEL_COMPILER || _CRAYFTN type(string_t) :: activation_name @@ -98,6 +199,31 @@ error stop 'activation_strategy_factory_s(factory): unrecognized activation name "' // activation_name%string() // '"' end select #if defined __INTEL_COMPILER || _CRAYFTN +#else + end associate +#endif + end procedure + + module procedure double_precision_differentiable_activation_strategy +#if defined __INTEL_COMPILER || _CRAYFTN + type(string_t) :: activation_name + activation_name = self%network_configuration_%activation_name() +#else + associate(activation_name => self%network_configuration_%activation_name()) +#endif + select case(activation_name%string()) + case ("gelu") + strategy = gelu_t() + case ("relu") + strategy = relu_t() + case ("sigmoid") + strategy = sigmoid_t() + case ("swish") + strategy = swish_t() + case default + error stop 'activation_strategy_factory_s(factory): unrecognized activation name "' // activation_name%string() // '"' + end select +#if defined __INTEL_COMPILER || _CRAYFTN #else end associate #endif From e292caee892666e4276a786aa9c3f5ac1363ef58 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Sun, 1 Sep 2024 15:47:42 -0700 Subject: [PATCH 034/105] chore: rm global "rkind" real kind parameter The recent introduction of parameterized derived types supports default real and double precision kinds in a more flexible way that makes it less useful to have one kind parameter setting the real-value precision globally. This commit therefore removes all occurences of the "rkind" parameter from the repository. --- cloud-microphysics/app/tensor-statistics.f90 | 4 +- .../app/train-cloud-microphysics.f90 | 10 ++-- cloud-microphysics/src/histogram_s.f90 | 16 ++---- cloud-microphysics/src/phase_space_bin_m.f90 | 7 +-- example/read-query-infer.f90 | 1 - example/write-read-infer.F90 | 11 ++-- src/inference_engine/kind_parameters_m.f90 | 2 - src/inference_engine/step_s.f90 | 1 - src/inference_engine_m.f90 | 1 - test/asymmetric_engine_test_m.F90 | 5 +- test/inference_engine_test_m.F90 | 17 +++--- test/trainable_engine_test_m.F90 | 55 +++++++++---------- 12 files changed, 58 insertions(+), 72 deletions(-) diff --git a/cloud-microphysics/app/tensor-statistics.f90 b/cloud-microphysics/app/tensor-statistics.f90 index cb72e7492..3a216205a 100644 --- a/cloud-microphysics/app/tensor-statistics.f90 +++ b/cloud-microphysics/app/tensor-statistics.f90 @@ -9,7 +9,7 @@ program tensor_statistics ! External dependencies: use julienne_m, only : command_line_t, file_t, string_t use assert_m, only : assert, intrinsic_array_t - use inference_engine_m, only : rkind, ubounds_t + use inference_engine_m, only : ubounds_t use ieee_arithmetic, only : ieee_is_nan use iso_fortran_env, only : int64, real64 @@ -195,7 +195,7 @@ subroutine compute_histograms(base_name, raw) allocate(dqr_dt, mold = qr_out) allocate(dqs_dt, mold = qs_out) - associate(dt => real(time_out - time_in, rkind)) + associate(dt => real(time_out - time_in)) do concurrent(t = 1:t_end) dpt_dt(:,:,:,t) = (potential_temperature_out(:,:,:,t) - potential_temperature_in(:,:,:,t))/dt(t) dqv_dt(:,:,:,t) = (qv_out(:,:,:,t)- qv_in(:,:,:,t))/dt(t) diff --git a/cloud-microphysics/app/train-cloud-microphysics.f90 b/cloud-microphysics/app/train-cloud-microphysics.f90 index 50a923b6e..998be2966 100644 --- a/cloud-microphysics/app/train-cloud-microphysics.f90 +++ b/cloud-microphysics/app/train-cloud-microphysics.f90 @@ -13,7 +13,7 @@ program train_on_flat_distribution use julienne_m, only : string_t, file_t, command_line_t, bin_t use assert_m, only : assert, intrinsic_array_t use inference_engine_m, only : & - inference_engine_t, mini_batch_t, input_output_pair_t, tensor_t, trainable_engine_t, rkind, tensor_map_t, & + inference_engine_t, mini_batch_t, input_output_pair_t, tensor_t, trainable_engine_t, tensor_map_t, & training_configuration_t, shuffle !! Internal dependencies; @@ -230,7 +230,7 @@ subroutine read_train_write(training_configuration, base_name, plot_unit, previo allocate(dqr_dt, mold = qr_out) allocate(dqs_dt, mold = qs_out) - associate(dt => real(time_out - time_in, rkind)) + associate(dt => real(time_out - time_in)) do concurrent(t = 1:t_end) dpt_dt(:,:,:,t) = (potential_temperature_out(:,:,:,t) - potential_temperature_in(:,:,:,t))/dt(t) dqv_dt(:,:,:,t) = (qv_out(:,:,:,t)- qv_in(:,:,:,t))/dt(t) @@ -253,7 +253,7 @@ subroutine read_train_write(training_configuration, base_name, plot_unit, previo type(bin_t), allocatable :: bins(:) type(input_output_pair_t), allocatable :: input_output_pairs(:) type(tensor_t), allocatable, dimension(:) :: inputs, outputs - real(rkind), allocatable :: cost(:) + real, allocatable :: cost(:) integer i, lon, lat, level, time, network_unit, io_status, epoch integer(int64) start_training, finish_training @@ -421,8 +421,8 @@ subroutine read_train_write(training_configuration, base_name, plot_unit, previo end subroutine read_train_write pure function normalize(x, x_min, x_max) result(x_normalized) - real(rkind), intent(in) :: x(:,:,:,:), x_min, x_max - real(rkind), allocatable :: x_normalized(:,:,:,:) + real, intent(in) :: x(:,:,:,:), x_min, x_max + real, allocatable :: x_normalized(:,:,:,:) call assert(x_min/=x_max, "train_cloud_microphysics(normaliz): x_min/=x_max") x_normalized = (x - x_min)/(x_max - x_min) end function diff --git a/cloud-microphysics/src/histogram_s.f90 b/cloud-microphysics/src/histogram_s.f90 index f88b51efc..a8936118d 100644 --- a/cloud-microphysics/src/histogram_s.f90 +++ b/cloud-microphysics/src/histogram_s.f90 @@ -2,12 +2,9 @@ ! Terms of use are as specified in LICENSE.txt submodule(histogram_m) histogram_s use assert_m, only : assert, intrinsic_array_t - use kind_parameters_m, only : rkind use julienne_m, only : string_t, operator(.cat.) implicit none - real(rkind), parameter :: zero = 0._rkind, one = 1._rkind, two=2._rkind, half = one/two - contains module procedure variable_name @@ -96,9 +93,8 @@ end procedure pure function normalize(x, x_min, x_max) result(x_normalized) - implicit none - real(rkind), intent(in) :: x(:,:,:,:), x_min, x_max - real(rkind), allocatable :: x_normalized(:,:,:,:) + real, intent(in) :: x(:,:,:,:), x_min, x_max + real, allocatable :: x_normalized(:,:,:,:) call assert(x_min/=x_max, "histogram_m(normalize): x_min/=x_max", intrinsic_array_t([x_min, x_max])) x_normalized = (x - x_min)/(x_max - x_min) end function @@ -108,7 +104,7 @@ pure function normalize(x, x_min, x_max) result(x_normalized) integer i, j, k, n integer, allocatable :: bin_count(:) integer, parameter :: performance_threshold = 80 - real, parameter :: capture_maxval = 1.0001_rkind ! ensure maxval(v_max) falls within the highest bin + real, parameter :: capture_maxval = 1.0001 ! ensure maxval(v_max) falls within the highest bin real, allocatable :: v_mapped(:,:,:,:) histogram%variable_name_ = variable_name @@ -125,11 +121,11 @@ pure function normalize(x, x_min, x_max) result(x_normalized) else v_mapped = normalize(v, v_min, v_max) end if - associate(v_mapped_min => merge(v_min, zero, raw), v_mapped_max => capture_maxval*merge(v_max, one, raw)) + associate(v_mapped_min => merge(v_min, 0., raw), v_mapped_max => capture_maxval*merge(v_max, 1., raw)) associate(dv => (v_mapped_max - v_mapped_min)/real(num_bins)) associate(v_bin_min => [(v_mapped_min + (i-1)*dv, i=1,num_bins)]) associate(v_bin_max => [v_bin_min(2:), v_mapped_max]) - histogram%bin_value_ = half*[v_bin_min + v_bin_max] ! switching to average yields problems likely related to roundoff + histogram%bin_value_ = 0.5*[v_bin_min + v_bin_max] ! switching to average yields problems likely related to roundoff if (num_bins < performance_threshold) then do concurrent(i = 1:num_bins) bin_count(i) = count(v_mapped >= v_bin_min(i) .and. v_mapped < v_bin_max(i)) @@ -147,7 +143,7 @@ pure function normalize(x, x_min, x_max) result(x_normalized) end do end do end do - histogram%bin_frequency_ = real(bin_count,rkind) / real(cardinality,rkind) + histogram%bin_frequency_ = real(bin_count) / real(cardinality) end if end associate end associate diff --git a/cloud-microphysics/src/phase_space_bin_m.f90 b/cloud-microphysics/src/phase_space_bin_m.f90 index 7ad1f0a61..f9ca94a24 100644 --- a/cloud-microphysics/src/phase_space_bin_m.f90 +++ b/cloud-microphysics/src/phase_space_bin_m.f90 @@ -1,5 +1,4 @@ module phase_space_bin_m - use kind_parameters_m, only : rkind use tensor_map_m, only : tensor_map_t use tensor_m, only : tensor_t implicit none @@ -15,7 +14,7 @@ module phase_space_bin_m pure module function bin(tensor, minima, maxima, num_bins) result(phase_space_bin) implicit none type(tensor_t), intent(in) :: tensor - real(rkind), intent(in) :: minima(:), maxima(:) + real, intent(in) :: minima(:), maxima(:) integer, intent(in) :: num_bins type(phase_space_bin_t) phase_space_bin end function @@ -26,9 +25,9 @@ pure module function bin(tensor, minima, maxima, num_bins) result(phase_space_bi module procedure bin - real(rkind), parameter :: half = 0.5_rkind + real, parameter :: half = 0.5 - associate(bin_widths => (maxima - minima)/real(num_bins,rkind)) + associate(bin_widths => (maxima - minima)/real(num_bins)) associate(tensor_values => min(tensor%values(), maxima - half*bin_widths)) phase_space_bin%loc = (tensor_values - minima)/bin_widths + 1 end associate diff --git a/example/read-query-infer.f90 b/example/read-query-infer.f90 index 0fe66a003..45a593914 100644 --- a/example/read-query-infer.f90 +++ b/example/read-query-infer.f90 @@ -6,7 +6,6 @@ program read_query_infer !! and use the network to perform inference. use inference_engine_m, only : inference_engine_t, relu_t, tensor_t use julienne_m, only : string_t, command_line_t, file_t - use kind_parameters_m, only : rkind implicit none type(command_line_t) command_line diff --git a/example/write-read-infer.F90 b/example/write-read-infer.F90 index e6a7a907e..f6880cd3a 100644 --- a/example/write-read-infer.F90 +++ b/example/write-read-infer.F90 @@ -9,7 +9,6 @@ program write_read_infer !! function. use inference_engine_m, only : inference_engine_t, relu_t, tensor_t use julienne_m, only : string_t, command_line_t, file_t - use kind_parameters_m, only : rkind implicit none type(string_t) file_name @@ -31,19 +30,19 @@ function identity_network() result(inference_engine) integer, parameter :: nodes_per_layer(*) = [2, 2, 2] integer, parameter :: max_n = maxval(nodes_per_layer), layers = size(nodes_per_layer) #ifdef _CRAYFTN - real(rkind), allocatable :: weights(:,:,:) - weights = reshape([real(rkind):: [1,0, 0,1], [1,0, 0,1]], [max_n, max_n, layers-1]) + real, allocatable :: weights(:,:,:) + weights = reshape([real :: [1,0, 0,1], [1,0, 0,1]], [max_n, max_n, layers-1]) inference_engine = inference_engine_t( & metadata = [string_t("Identity"), string_t("Damian Rouson"), string_t("2023-09-18"), string_t("relu"), string_t("false")], & weights = weights, & - biases = reshape([real(rkind):: [0,0], [0,0]], [max_n, layers-1]), & + biases = reshape([real:: [0,0], [0,0]], [max_n, layers-1]), & nodes = nodes_per_layer & ) #else inference_engine = inference_engine_t( & metadata = [string_t("Identity"), string_t("Damian Rouson"), string_t("2023-09-18"), string_t("relu"), string_t("false")], & - weights = reshape([real(rkind):: [1,0, 0,1], [1,0, 0,1]], [max_n, max_n, layers-1]), & - biases = reshape([real(rkind):: [0,0], [0,0]], [max_n, layers-1]), & + weights = reshape([real :: [1,0, 0,1], [1,0, 0,1]], [max_n, max_n, layers-1]), & + biases = reshape([real :: [0,0], [0,0]], [max_n, layers-1]), & nodes = nodes_per_layer & ) #endif diff --git a/src/inference_engine/kind_parameters_m.f90 b/src/inference_engine/kind_parameters_m.f90 index 973319d4d..f312a66b6 100644 --- a/src/inference_engine/kind_parameters_m.f90 +++ b/src/inference_engine/kind_parameters_m.f90 @@ -3,11 +3,9 @@ module kind_parameters_m implicit none private - public :: rkind public :: default_real public :: double_precision - integer, parameter :: rkind = kind(1.0) integer, parameter :: default_real = kind(1.) integer, parameter :: double_precision = kind(1D0) end module kind_parameters_m diff --git a/src/inference_engine/step_s.f90 b/src/inference_engine/step_s.f90 index 380dbf617..e98d8d756 100644 --- a/src/inference_engine/step_s.f90 +++ b/src/inference_engine/step_s.f90 @@ -1,7 +1,6 @@ ! Copyright (c), The Regents of the University of California ! Terms of use are as specified in LICENSE.txt submodule(step_m) step_s - use kind_parameters_m, only : rkind implicit none contains diff --git a/src/inference_engine_m.f90 b/src/inference_engine_m.f90 index efd0562bc..bc6ea2271 100644 --- a/src/inference_engine_m.f90 +++ b/src/inference_engine_m.f90 @@ -7,7 +7,6 @@ module inference_engine_m use hyperparameters_m, only : hyperparameters_t use input_output_pair_m, only : input_output_pair_t, shuffle, write_to_stdout use inference_engine_m_, only : inference_engine_t, infer - use kind_parameters_m, only : rkind use metadata_m, only : metadata_t use mini_batch_m, only : mini_batch_t use network_configuration_m, only : network_configuration_t diff --git a/test/asymmetric_engine_test_m.F90 b/test/asymmetric_engine_test_m.F90 index ee0d40e32..522bff898 100644 --- a/test/asymmetric_engine_test_m.F90 +++ b/test/asymmetric_engine_test_m.F90 @@ -10,7 +10,6 @@ module asymmetric_engine_test_m ! Internal dependencies use inference_engine_m, only : inference_engine_t, tensor_t - use kind_parameters_m, only : rkind implicit none @@ -63,7 +62,7 @@ function results() result(test_results) function xor_and_2nd_input_network() result(inference_engine) type(inference_engine_t) inference_engine - real(rkind), allocatable :: biases(:,:), weights(:,:,:) + real, allocatable :: biases(:,:), weights(:,:,:) type(string_t), allocatable :: metadata(:) integer, parameter :: n(0:*) = [2,4,4,1] integer, parameter :: layers = size(n), n_max = maxval(n) @@ -120,7 +119,7 @@ function xor_and_2nd_input_truth_table() result(test_passes) asymmetric = xor_and_2nd_input_network() block - real(rkind), parameter :: tolerance = 1.E-08_rkind, false = 0._rkind, true = 1._rkind + real, parameter :: tolerance = 1.E-08, false = 0., true = 1. type(tensor_t) true_true, true_false, false_true, false_false true_true = asymmetric%infer(tensor_t([true,true])) diff --git a/test/inference_engine_test_m.F90 b/test/inference_engine_test_m.F90 index a2ecb10b1..a2c1bfe1d 100644 --- a/test/inference_engine_test_m.F90 +++ b/test/inference_engine_test_m.F90 @@ -5,7 +5,6 @@ module inference_engine_test_m ! External dependencies use assert_m, only : assert - use kind_parameters_m, only : rkind use julienne_m, only : test_t, test_result_t, test_description_t, test_description_substring, string_t, file_t #ifdef __GFORTRAN__ use julienne_m, only : test_function_i @@ -77,7 +76,7 @@ function single_hidden_layer_xor_network() result(inference_engine) inference_engine = inference_engine_t( & metadata = [string_t("XOR"), string_t("Damian Rouson"), string_t("2023-07-02"), string_t("step"), string_t("false")], & - weights = reshape([real(rkind):: [1,1,0, 0,1,1, 0,0,0], [1,0,0, -2,0,0, 1,0,0]], [max_n, max_n, layers-1]), & + weights = reshape([real:: [1,1,0, 0,1,1, 0,0,0], [1,0,0, -2,0,0, 1,0,0]], [max_n, max_n, layers-1]), & biases = reshape([[0.,-1.99,0.], [0., 0., 0.]], [max_n, layers-1]), & nodes = nodes_per_layer & ) @@ -90,7 +89,7 @@ function multi_layer_xor_network() result(inference_engine) inference_engine = inference_engine_t( & metadata = [string_t("XOR"), string_t("Damian Rouson"), string_t("2023-07-02"), string_t("step"), string_t("false")], & - weights = reshape([real(rkind):: [1,1,0, 0,1,1, 1,0,0, 1,0,0, 0,1,0, 0,0,1], [1,0,0, -2,0,0, 1,0,0]], & + weights = reshape([real:: [1,1,0, 0,1,1, 1,0,0, 1,0,0, 0,1,0, 0,0,1], [1,0,0, -2,0,0, 1,0,0]], & [max_n, max_n, layers-1]), & biases = reshape([[0.,-1.99,0.], [0., 0., 0.], [0., 0., 0.]], [max_n, layers-1]), & nodes = nodes_per_layer & @@ -108,7 +107,7 @@ function decrement_split_combine_increment() result(inference_engine) integer, parameter :: n(*) = [inputs, hidden(1), hidden(2), outputs] ! nodes per layer integer, parameter :: n_max = maxval(n), layers=size(n) ! max layer width, number of layers integer, parameter :: w_shape(*) = [n_max, n_max, layers-1], b_shape(*) = [n_max, n_max] - real(rkind), parameter :: & + real, parameter :: & w(*,*,*) = reshape( [1.,0.,0., 0.,1.,0., 0.,0.,0., 1.,0.,0., 0.,.5,.5, 0.,0.,0., 1.,0.,0., 0.,1.,0., 0.,1.,1.], w_shape), & b(*,*) = reshape( [0.,-1.,0., 0.,0.,0., 0.,1.,0.], b_shape) @@ -130,7 +129,7 @@ function varying_width() result(inference_engine) integer, parameter :: n_max = maxval(n), layers=size(n) ! max layer width, number of layers integer, parameter :: w_shape(*) = [n_max, n_max, layers-1], b_shape(*) = [n_max, n_max] integer i - real(rkind), allocatable :: w(:,:,:), b(:,:) + real, allocatable :: w(:,:,:), b(:,:) w = reshape( [(i, i=1,product(w_shape))], w_shape) b = reshape( [(maxval(w) + i, i=1,product(b_shape))], b_shape) @@ -148,7 +147,7 @@ function distinct_parameters() result(inference_engine) integer, parameter :: n_max = maxval(n), layers=size(n) ! max layer width, number of layers integer, parameter :: w_shape(*) = [n_max, n_max, layers-1], b_shape(*) = [n_max, n_max] integer i - real(rkind), allocatable :: w(:,:,:), b(:,:) + real, allocatable :: w(:,:,:), b(:,:) w = reshape( [(i, i=1,product(w_shape))], w_shape) b = reshape( [(maxval(w) + i, i=1,product(b_shape))], b_shape) @@ -184,7 +183,7 @@ function infer_with_varying_width_net() result(test_passes) logical test_passes type(inference_engine_t) inference_engine type(tensor_t) inputs, outputs - real(rkind), parameter :: tolerance = 1.E-08_rkind + real, parameter :: tolerance = 1.E-08 inference_engine = decrement_split_combine_increment() inputs = tensor_t([1.1, 2.7]) @@ -200,7 +199,7 @@ function elemental_infer_with_1_hidden_layer_xor_net() result(test_passes) block type(tensor_t), allocatable :: truth_table(:) - real(rkind), parameter :: tolerance = 1.E-08_rkind, false = 0._rkind, true = 1._rkind + real, parameter :: tolerance = 1.E-08, false = 0., true = 1. integer i associate(array_of_inputs => [tensor_t([true,true]), tensor_t([true,false]), tensor_t([false,true]), tensor_t([false,false])]) @@ -221,7 +220,7 @@ function elemental_infer_with_2_hidden_layer_xor_net() result(test_passes) block type(tensor_t), allocatable :: truth_table(:) - real(rkind), parameter :: tolerance = 1.E-08_rkind, false = 0._rkind, true = 1._rkind + real, parameter :: tolerance = 1.E-08, false = 0., true = 1. integer i associate(array_of_inputs => [tensor_t([true,true]), tensor_t([true,false]), tensor_t([false,true]), tensor_t([false,false])]) diff --git a/test/trainable_engine_test_m.F90 b/test/trainable_engine_test_m.F90 index 6fc622686..23a7fd694 100644 --- a/test/trainable_engine_test_m.F90 +++ b/test/trainable_engine_test_m.F90 @@ -13,7 +13,6 @@ module trainable_engine_test_m ! Internal dependencies use inference_engine_m, only : trainable_engine_t, tensor_t, sigmoid_t, input_output_pair_t, mini_batch_t, relu_t, shuffle - use kind_parameters_m, only : rkind implicit none private @@ -45,7 +44,7 @@ module trainable_engine_test_m procedure, nopass :: results end type - real(rkind), parameter :: false = 0._rkind, true = 1._rkind + real, parameter :: false = 0., true = 1. abstract interface @@ -179,7 +178,7 @@ function two_zeroed_hidden_layers() result(trainable_engine) integer, parameter :: inputs = 2, outputs = 1, hidden = 3 ! number of neurons in input, output, and hidden layers integer, parameter :: neurons(*) = [inputs, hidden, hidden, outputs] ! neurons per layer integer, parameter :: max_neurons = maxval(neurons), layers=size(neurons) ! max layer width, number of layers - real(rkind) w(max_neurons, max_neurons, layers-1), b(max_neurons, max_neurons) + real w(max_neurons, max_neurons, layers-1), b(max_neurons, max_neurons) w = 0. b = 0. @@ -195,7 +194,7 @@ function two_random_hidden_layers() result(trainable_engine) integer, parameter :: inputs = 2, outputs = 1, hidden = 3 ! number of neurons in input, output, and hidden layers integer, parameter :: neurons(*) = [inputs, hidden, hidden, outputs] ! neurons per layer integer, parameter :: max_neurons = maxval(neurons), layers=size(neurons) ! max layer width, number of layers - real(rkind) w(max_neurons, max_neurons, layers-1), b(max_neurons, max_neurons) + real w(max_neurons, max_neurons, layers-1), b(max_neurons, max_neurons) call random_number(b) call random_number(w) @@ -214,8 +213,8 @@ function and_gate_with_skewed_training_data() result(test_passes) type(tensor_t), allocatable, dimension(:,:) :: training_inputs, training_outputs type(tensor_t), allocatable, dimension(:) :: tmp, tmp2, test_inputs, expected_test_outputs, actual_outputs type(trainable_engine_t) trainable_engine - real(rkind), parameter :: tolerance = 1.E-02_rkind - real(rkind), allocatable :: harvest(:,:,:) + real, parameter :: tolerance = 1.E-02 + real, allocatable :: harvest(:,:,:) integer, parameter :: num_inputs=2, mini_batch_size = 1, num_iterations=20000 integer batch, iter, i @@ -246,7 +245,7 @@ function and_gate_with_skewed_training_data() result(test_passes) elemental function and(inputs_object) result(expected_outputs_object) type(tensor_t), intent(in) :: inputs_object type(tensor_t) expected_outputs_object - expected_outputs_object = tensor_t([merge(true, false, sum(inputs_object%values()) > 1.99_rkind)]) + expected_outputs_object = tensor_t([merge(true, false, sum(inputs_object%values()) > 1.99)]) end function end function @@ -258,8 +257,8 @@ function not_and_gate_with_skewed_training_data() result(test_passes) type(tensor_t), allocatable :: training_outputs(:,:), expected_test_outputs(:), tmp2(:) type(trainable_engine_t) trainable_engine type(tensor_t), allocatable :: actual_outputs(:) - real(rkind), parameter :: tolerance = 1.E-02_rkind - real(rkind), allocatable :: harvest(:,:,:) + real, parameter :: tolerance = 1.E-02 + real, allocatable :: harvest(:,:,:) integer, parameter :: num_inputs=2, mini_batch_size = 1, num_iterations=30000 integer batch, iter, i @@ -301,8 +300,8 @@ function or_gate_with_random_weights() result(test_passes) type(tensor_t), allocatable :: training_inputs(:,:), test_inputs(:), actual_outputs(:) type(tensor_t), allocatable :: training_outputs(:,:), expected_test_outputs(:) type(trainable_engine_t) trainable_engine - real(rkind), parameter :: tolerance = 1.E-02_rkind - real(rkind), allocatable :: harvest(:,:,:) + real, parameter :: tolerance = 1.E-02 + real, allocatable :: harvest(:,:,:) integer, parameter :: num_inputs=2, mini_batch_size = 1, num_iterations=50000 integer batch, iter, i @@ -346,8 +345,8 @@ function xor_gate_with_random_weights() result(test_passes) type(tensor_t), allocatable, dimension(:,:) :: training_inputs, training_outputs type(tensor_t), allocatable, dimension(:) :: actual_outputs, test_inputs, expected_test_outputs type(trainable_engine_t) trainable_engine - real(rkind), parameter :: tolerance = 1.E-02_rkind - real(rkind), allocatable :: harvest(:,:,:) + real, parameter :: tolerance = 1.E-02 + real, allocatable :: harvest(:,:,:) #ifdef __flang__ !! Reducing num_iterations yields a less robust test, but moving away from local minima by !! increasing num_iterations causes this test to crash when compiled with the flang or ifx compilers. @@ -403,19 +402,19 @@ pure function local_xor(inputs) result(expected_outputs) function perturbed_identity_network(perturbation_magnitude) result(trainable_engine) type(trainable_engine_t) trainable_engine - real(rkind), intent(in) :: perturbation_magnitude + real, intent(in) :: perturbation_magnitude integer, parameter :: nodes_per_layer(*) = [2, 2, 2, 2] integer, parameter :: max_n = maxval(nodes_per_layer), layers = size(nodes_per_layer) #ifndef _CRAYFTN - real(rkind), parameter :: identity(*,*,*) = & - reshape([real(rkind):: [1,0], [0,1] ,[1,0], [0,1], [1,0], [0,1]], [max_n, max_n, layers-1]) + real, parameter :: identity(*,*,*) = & + reshape([real:: [1,0], [0,1] ,[1,0], [0,1], [1,0], [0,1]], [max_n, max_n, layers-1]) #else - real(rkind), allocatable :: identity(:,:,:) + real, allocatable :: identity(:,:,:) #endif - real(rkind) harvest(size(identity,1), size(identity,2), size(identity,3)) + real harvest(size(identity,1), size(identity,2), size(identity,3)) #ifdef _CRAYFTN - identity = reshape([real(rkind):: [1,0], [0,1] ,[1,0], [0,1], [1,0], [0,1]], [max_n, max_n, layers-1]) + identity = reshape([real:: [1,0], [0,1] ,[1,0], [0,1], [1,0], [0,1]], [max_n, max_n, layers-1]) #endif call random_number(harvest) @@ -424,7 +423,7 @@ function perturbed_identity_network(perturbation_magnitude) result(trainable_eng trainable_engine = trainable_engine_t( & nodes = nodes_per_layer, & weights = identity + harvest , & - biases = reshape([real(rkind):: [0,0], [0,0], [0,0]], [max_n, layers-1]), & + biases = reshape([real:: [0,0], [0,0], [0,0]], [max_n, layers-1]), & differentiable_activation_strategy = relu_t(), & metadata = [string_t("Identity"), string_t("Damian Rouson"), string_t("2023-09-18"), string_t("relu"), string_t("false")] & ) @@ -437,7 +436,7 @@ function preserves_identity_mapping() result(test_passes) type(tensor_t), allocatable :: inputs(:) type(trainable_engine_t) trainable_engine type(bin_t), allocatable :: bins(:) - real(rkind), allocatable :: cost(:) + real, allocatable :: cost(:) integer, parameter :: num_pairs = 100, num_epochs = 100, n_bins = 3 integer i, bin, epoch @@ -451,10 +450,10 @@ function preserves_identity_mapping() result(test_passes) #ifdef _CRAYFTN allocate(inputs(num_pairs)) do i = 1, num_pairs - inputs(i) = tensor_t(real([i,2*i], rkind)/num_pairs) + inputs(i) = tensor_t(real([i,2*i])/num_pairs) end do #else - inputs = [(tensor_t(real([i,2*i], rkind)/num_pairs), i = 1, num_pairs)] + inputs = [(tensor_t(real([i,2*i])/num_pairs), i = 1, num_pairs)] #endif associate(outputs => inputs) input_output_pairs = input_output_pair_t(inputs, outputs) @@ -467,7 +466,7 @@ function preserves_identity_mapping() result(test_passes) end do block - real(rkind), parameter :: tolerance = 1.E-06 + real, parameter :: tolerance = 1.E-06 #if defined _CRAYFTN || __GFORTRAN__ type(tensor_t), allocatable :: network_outputs(:) network_outputs = trainable_engine%infer(inputs) @@ -496,7 +495,7 @@ function perturbed_identity_converges() result(test_passes) type(tensor_t), allocatable :: inputs(:) type(trainable_engine_t) trainable_engine type(bin_t), allocatable :: bins(:) - real(rkind), allocatable :: cost(:) + real, allocatable :: cost(:) integer, parameter :: num_pairs = 6 integer, parameter :: num_epochs = 180 integer, parameter :: num_bins = 5 @@ -512,10 +511,10 @@ function perturbed_identity_converges() result(test_passes) #ifdef _CRAYFTN allocate(inputs(num_pairs)) do i = 1, num_pairs - inputs(i) = tensor_t(real([i,2*i], rkind)/(2*num_pairs)) + inputs(i) = tensor_t(real([i,2*i])/(2*num_pairs)) end do #else - inputs = [(tensor_t(real([i,2*i], rkind)/(2*num_pairs)), i = 1, num_pairs)] + inputs = [(tensor_t(real([i,2*i])/(2*num_pairs)), i = 1, num_pairs)] #endif associate(outputs => inputs) input_output_pairs = input_output_pair_t(inputs, outputs) @@ -529,7 +528,7 @@ function perturbed_identity_converges() result(test_passes) end do block - real(rkind), parameter :: tolerance = 1.E-06 + real, parameter :: tolerance = 1.E-06 #if defined _CRAYFTN || __GFORTRAN__ type(tensor_t), allocatable :: network_outputs(:) network_outputs = trainable_engine%infer(inputs) From 1e386420843507c87da91d2685d2fc147a19053d Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Sun, 1 Sep 2024 19:09:43 -0700 Subject: [PATCH 035/105] chore(fpm): update to julienne 1.2.1 dependency --- fpm.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fpm.toml b/fpm.toml index 033569f9d..7e22c0bb6 100644 --- a/fpm.toml +++ b/fpm.toml @@ -8,4 +8,4 @@ maintainer = "rouson@lbl.gov" assert = {git = "https://github.com/sourceryinstitute/assert", tag = "1.7.0"} [dev-dependencies] -julienne = {git = "https://github.com/berkeleylab/julienne", tag = "1.2.0"} +julienne = {git = "https://github.com/berkeleylab/julienne", tag = "1.2.1"} From 211f9cbc18f37019c1a87e89852ca330ba6e1db5 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Sun, 1 Sep 2024 20:02:23 -0700 Subject: [PATCH 036/105] feat(metadata):dble_prec_string_t json constructor This commit adds a metadata_t constructor that takes a double_precision_string_t array of lines containing JSON syntax. --- src/inference_engine/metadata_m.f90 | 7 +++++++ src/inference_engine/metadata_s.f90 | 28 ++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/src/inference_engine/metadata_m.f90 b/src/inference_engine/metadata_m.f90 index 09713c83a..09683fc57 100644 --- a/src/inference_engine/metadata_m.f90 +++ b/src/inference_engine/metadata_m.f90 @@ -1,5 +1,6 @@ module metadata_m use julienne_string_m, only : string_t + use double_precision_string_m, only : double_precision_string_t implicit none private @@ -23,6 +24,12 @@ pure module function from_json(lines) result(metadata) type(metadata_t) metadata end function + pure module function double_precision_from_json(lines) result(metadata) + implicit none + type(double_precision_string_t), intent(in) :: lines(:) + type(metadata_t) metadata + end function + pure module function from_components(modelName, modelAuthor, compilationDate, activationFunction, usingSkipConnections) & result(metadata) implicit none diff --git a/src/inference_engine/metadata_s.f90 b/src/inference_engine/metadata_s.f90 index f50b18bce..92597e2c6 100644 --- a/src/inference_engine/metadata_s.f90 +++ b/src/inference_engine/metadata_s.f90 @@ -43,6 +43,34 @@ call assert(any(trim(adjustl(lines(size(lines))%string())) == ["},","} "]), "metadata_s(from_json): metadata object end found") end procedure + module procedure double_precision_from_json + integer l + + call assert(lines(1)%get_json_key() == "metadata", "metadata_s(double_precision_from_json): metadata found") + + do l = 2, size(lines)-1 + associate(key => lines(l)%get_json_key()) + select case (key%string()) + case("modelName") + metadata%modelName_ = lines(l)%get_json_value(key, mold=string_t("")) + case("modelAuthor") + metadata%modelAuthor_ = lines(l)%get_json_value(key, mold=string_t("")) + case("compilationDate") + metadata%compilationDate_ = lines(l)%get_json_value(key, mold=string_t("")) + case("activationFunction") + metadata%activationFunction_ = lines(l)%get_json_value(key, mold=string_t("")) + case("usingSkipConnections") + metadata%usingSkipConnections_ = lines(l)%get_json_value(key, mold=string_t("")) + case default + error stop "metadata_s(double_precision_from_json): missing key " // key%string() + end select + end associate + end do + + call assert(any(trim(adjustl(lines(size(lines))%string())) == ["},","} "]), & + "metadata_s(double_precision_from_json): metadata object end found") + end procedure + module procedure to_json character(len=*), parameter :: indent = repeat(" ",ncopies=4) From 0e26cb0320812d972aae0dc99d960af0d12b0b5b Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Sun, 1 Sep 2024 21:44:33 -0700 Subject: [PATCH 037/105] feat(tensor_map): add double_precision_from_json --- src/inference_engine/tensor_map_m.f90 | 7 +++++++ src/inference_engine/tensor_map_s.f90 | 19 +++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/src/inference_engine/tensor_map_m.f90 b/src/inference_engine/tensor_map_m.f90 index 792327f00..b11457030 100644 --- a/src/inference_engine/tensor_map_m.f90 +++ b/src/inference_engine/tensor_map_m.f90 @@ -4,6 +4,7 @@ module tensor_map_m use tensor_m, only : tensor_t use julienne_m, only : string_t use kind_parameters_m, only : default_real, double_precision + use double_precision_string_m, only : double_precision_string_t implicit none private @@ -47,6 +48,12 @@ module function from_json(lines) result(tensor_map) type(tensor_map_t) tensor_map end function + module function double_precision_from_json(lines) result(tensor_map) + implicit none + type(double_precision_string_t), intent(in) :: lines(:) + type(tensor_map_t(double_precision)) tensor_map + end function + end interface interface diff --git a/src/inference_engine/tensor_map_s.f90 b/src/inference_engine/tensor_map_s.f90 index 92ea17daa..c0e504d81 100644 --- a/src/inference_engine/tensor_map_s.f90 +++ b/src/inference_engine/tensor_map_s.f90 @@ -41,6 +41,25 @@ call assert(tensor_map_key_found, "tensor_map_s(from_json): 'tensor_map' key found") end procedure + module procedure double_precision_from_json + logical tensor_map_key_found + integer l + + tensor_map_key_found = .false. + + do l=1,size(lines) + if (lines(l)%get_json_key() == "inputs_map" .or. lines(l)%get_json_key() == "outputs_map") then + tensor_map_key_found = .true. + tensor_map%layer_ = lines(l+1)%get_json_value(key=string_t("layer"), mold=string_t("")) + tensor_map%intercept_ = lines(l+2)%get_json_value(key=string_t("intercept"), mold=[0D0]) + tensor_map%slope_ = lines(l+3)%get_json_value(key=string_t("slope"), mold=[0D0]) + return + end if + end do + + call assert(tensor_map_key_found, "tensor_map_s(from_json): 'tensor_map' key found") + end procedure + module procedure default_real_equals real, parameter :: tolerance = 1.E-08 From 7a1db6605fd99ef70cef994bad0761144f872eb3 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Sun, 1 Sep 2024 22:06:02 -0700 Subject: [PATCH 038/105] chore(example): rm public non-type-bound infer --- example/concurrent-inferences.f90 | 10 +--------- src/inference_engine_m.f90 | 2 +- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/example/concurrent-inferences.f90 b/example/concurrent-inferences.f90 index 996d4c97d..7a1426096 100644 --- a/example/concurrent-inferences.f90 +++ b/example/concurrent-inferences.f90 @@ -3,7 +3,7 @@ program concurrent_inferences !! This program demonstrates how to read a neural network from a JSON file !! and use the network to perform concurrent inferences. - use inference_engine_m, only : inference_engine_t, tensor_t, infer + use inference_engine_m, only : inference_engine_t, tensor_t use julienne_m, only : string_t, command_line_t, file_t use assert_m, only : assert use iso_fortran_env, only : int64, real64 @@ -69,14 +69,6 @@ program concurrent_inferences call system_clock(t_finish) print *,"Concurrent inference time: ", real(t_finish - t_start, real64)/real(clock_rate, real64) - print *,"Performing concurrent inference with a non-type-bound inference procedure" - call system_clock(t_start) - do concurrent(i=1:lat, j=1:lon, k=1:lev) - outputs(i,j,k) = infer(inference_engine, inputs(i,j,k)) - end do - call system_clock(t_finish) - print *,"Concurrent inference time with non-type-bound procedure: ", real(t_finish - t_start, real64)/real(clock_rate, real64) - end block end block diff --git a/src/inference_engine_m.f90 b/src/inference_engine_m.f90 index bc6ea2271..ca7a8c1eb 100644 --- a/src/inference_engine_m.f90 +++ b/src/inference_engine_m.f90 @@ -6,7 +6,7 @@ module inference_engine_m use differentiable_activation_strategy_m, only : differentiable_activation_strategy_t use hyperparameters_m, only : hyperparameters_t use input_output_pair_m, only : input_output_pair_t, shuffle, write_to_stdout - use inference_engine_m_, only : inference_engine_t, infer + use inference_engine_m_, only : inference_engine_t use metadata_m, only : metadata_t use mini_batch_m, only : mini_batch_t use network_configuration_m, only : network_configuration_t From d11a3776d18a95263aad722d5bc669ca97ef35f2 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Sun, 1 Sep 2024 22:09:42 -0700 Subject: [PATCH 039/105] chore(inference_engine): generic bindings|priv tbp --- src/inference_engine/inference_engine_m_.f90 | 71 +++++++++++--------- src/inference_engine/inference_engine_s.F90 | 36 +++++----- 2 files changed, 59 insertions(+), 48 deletions(-) diff --git a/src/inference_engine/inference_engine_m_.f90 b/src/inference_engine/inference_engine_m_.f90 index f7413dd12..9076dc572 100644 --- a/src/inference_engine/inference_engine_m_.f90 +++ b/src/inference_engine/inference_engine_m_.f90 @@ -14,7 +14,6 @@ module inference_engine_m_ private public :: inference_engine_t public :: exchange_t - public :: infer type inference_engine_t(k) !! Encapsulate the minimal information needed to perform inference @@ -25,20 +24,32 @@ module inference_engine_m_ integer, allocatable, private :: nodes_(:) class(activation_strategy_t), allocatable, private :: activation_strategy_ ! Strategy Pattern facilitates elemental activation contains - procedure :: infer - procedure :: to_json - procedure :: map_to_input_range - procedure :: map_from_output_range - procedure :: num_hidden_layers - procedure :: num_inputs - procedure :: num_outputs - procedure :: nodes_per_layer - procedure :: assert_conformable_with - procedure :: skip - procedure, private :: approximately_equal - generic :: operator(==) => approximately_equal - procedure :: activation_function_name - procedure :: to_exchange + generic :: operator(==) => default_real_approximately_equal + generic :: infer => default_real_infer + generic :: to_json => default_real_to_json + generic :: map_to_input_range => default_real_map_to_input_range + generic :: map_from_output_range => default_real_map_from_output_range + generic :: num_hidden_layers => default_real_num_hidden_layers + generic :: num_inputs => default_real_num_inputs + generic :: num_outputs => default_real_num_outputs + generic :: nodes_per_layer => default_real_nodes_per_layer + generic :: assert_conformable_with => default_real_assert_conformable_with + generic :: skip => default_real_skip + generic :: activation_function_name => default_real_activation_function_name + generic :: to_exchange => default_real_to_exchange + procedure, private :: default_real_approximately_equal + procedure, private :: default_real_infer + procedure, private :: default_real_to_json + procedure, private :: default_real_map_to_input_range + procedure, private :: default_real_map_from_output_range + procedure, private :: default_real_num_hidden_layers + procedure, private :: default_real_num_inputs + procedure, private :: default_real_num_outputs + procedure, private :: default_real_nodes_per_layer + procedure, private :: default_real_assert_conformable_with + procedure, private :: default_real_skip + procedure, private :: default_real_activation_function_name + procedure, private :: default_real_to_exchange end type type exchange_t(k) @@ -52,7 +63,7 @@ module inference_engine_m_ interface inference_engine_t - impure module function construct_from_padded_arrays(metadata, weights, biases, nodes, input_map, output_map) & + impure module function default_real_construct_from_padded_arrays(metadata, weights, biases, nodes, input_map, output_map) & result(inference_engine) implicit none type(string_t), intent(in) :: metadata(:) @@ -62,7 +73,7 @@ impure module function construct_from_padded_arrays(metadata, weights, biases, n type(inference_engine_t) inference_engine end function - impure elemental module function from_json(file_) result(inference_engine) + impure elemental module function default_real_from_json(file_) result(inference_engine) implicit none type(file_t), intent(in) :: file_ type(inference_engine_t) inference_engine @@ -72,14 +83,14 @@ impure elemental module function from_json(file_) result(inference_engine) interface - elemental module function approximately_equal(lhs, rhs) result(lhs_eq_rhs) + elemental module function default_real_approximately_equal(lhs, rhs) result(lhs_eq_rhs) !! The result is true if lhs and rhs are the same to within a tolerance implicit none class(inference_engine_t), intent(in) :: lhs, rhs logical lhs_eq_rhs end function - elemental module function map_to_input_range(self, tensor) result(normalized_tensor) + elemental module function default_real_map_to_input_range(self, tensor) result(normalized_tensor) !! The result contains the input tensor values normalized to fall on the range used during training implicit none class(inference_engine_t), intent(in) :: self @@ -87,7 +98,7 @@ elemental module function map_to_input_range(self, tensor) result(normalized_ten type(tensor_t) normalized_tensor end function - elemental module function map_from_output_range(self, normalized_tensor) result(tensor) + elemental module function default_real_map_from_output_range(self, normalized_tensor) result(tensor) !! The result contains the output tensor values unnormalized via the inverse of the mapping used in training implicit none class(inference_engine_t), intent(in) :: self @@ -95,62 +106,62 @@ elemental module function map_from_output_range(self, normalized_tensor) result( type(tensor_t) tensor end function - impure module function to_exchange(self) result(exchange) + impure module function default_real_to_exchange(self) result(exchange) implicit none class(inference_engine_t), intent(in) :: self type(exchange_t) exchange end function - impure elemental module function to_json(self) result(json_file) + impure elemental module function default_real_to_json(self) result(json_file) implicit none class(inference_engine_t), intent(in) :: self type(file_t) json_file end function - elemental module subroutine assert_conformable_with(self, inference_engine) + elemental module subroutine default_real_assert_conformable_with(self, inference_engine) implicit none class(inference_engine_t), intent(in) :: self type(inference_engine_t), intent(in) :: inference_engine end subroutine - elemental module function infer(self, inputs) result(outputs) + elemental module function default_real_infer(self, inputs) result(outputs) implicit none class(inference_engine_t), intent(in) :: self type(tensor_t), intent(in) :: inputs type(tensor_t) outputs end function - elemental module function num_outputs(self) result(output_count) + elemental module function default_real_num_outputs(self) result(output_count) implicit none class(inference_engine_t), intent(in) :: self integer output_count end function - elemental module function num_hidden_layers(self) result(hidden_layer_count) + elemental module function default_real_num_hidden_layers(self) result(hidden_layer_count) implicit none class(inference_engine_t), intent(in) :: self integer hidden_layer_count end function - elemental module function num_inputs(self) result(input_count) + elemental module function default_real_num_inputs(self) result(input_count) implicit none class(inference_engine_t), intent(in) :: self integer input_count end function - pure module function nodes_per_layer(self) result(node_count) + pure module function default_real_nodes_per_layer(self) result(node_count) implicit none class(inference_engine_t), intent(in) :: self integer, allocatable :: node_count(:) end function - elemental module function activation_function_name(self) result(activation_name) + elemental module function default_real_activation_function_name(self) result(activation_name) implicit none class(inference_engine_t), intent(in) :: self type(string_t) activation_name end function - pure module function skip(self) result(use_skip_connections) + pure module function default_real_skip(self) result(use_skip_connections) implicit none class(inference_engine_t), intent(in) :: self logical use_skip_connections diff --git a/src/inference_engine/inference_engine_s.F90 b/src/inference_engine/inference_engine_s.F90 index 9ffa67580..00f2fd542 100644 --- a/src/inference_engine/inference_engine_s.F90 +++ b/src/inference_engine/inference_engine_s.F90 @@ -19,15 +19,15 @@ contains - module procedure map_to_input_range + module procedure default_real_map_to_input_range normalized_tensor = self%input_map_%map_to_training_range(tensor) end procedure - module procedure map_from_output_range + module procedure default_real_map_from_output_range tensor = self%output_map_%map_from_training_range(normalized_tensor) end procedure - module procedure to_exchange + module procedure default_real_to_exchange exchange%input_map_ = self%input_map_ exchange%output_map_ = self%output_map_ associate(strings => self%metadata_%strings()) @@ -39,7 +39,7 @@ exchange%activation_strategy_ = self%activation_strategy_ end procedure - module procedure infer + module procedure default_real_infer real, allocatable :: a(:,:) integer, parameter :: input_layer = 0 @@ -133,7 +133,7 @@ impure function activation_factory_method(activation_name) result(activation) end select end function - module procedure construct_from_padded_arrays + module procedure default_real_construct_from_padded_arrays inference_engine%metadata_ = metadata_t(metadata(1),metadata(2),metadata(3),metadata(4),metadata(5)) inference_engine%weights_ = weights @@ -169,9 +169,9 @@ impure function activation_factory_method(activation_name) result(activation) call assert_consistency(inference_engine) - end procedure construct_from_padded_arrays + end procedure default_real_construct_from_padded_arrays - module procedure to_json + module procedure default_real_to_json #ifdef _CRAYFTN type(tensor_map_t) proto_map @@ -285,9 +285,9 @@ impure function activation_factory_method(activation_name) result(activation) end block end associate end associate - end procedure to_json + end procedure default_real_to_json - module procedure from_json + module procedure default_real_from_json character(len=:), allocatable :: justified_line integer l, num_file_lines @@ -386,9 +386,9 @@ impure function activation_factory_method(activation_name) result(activation) call assert_consistency(inference_engine) - end procedure from_json + end procedure default_real_from_json - module procedure assert_conformable_with + module procedure default_real_assert_conformable_with call assert_consistency(self) call assert_consistency(inference_engine) @@ -405,7 +405,7 @@ impure function activation_factory_method(activation_name) result(activation) end procedure - module procedure approximately_equal + module procedure default_real_approximately_equal logical nodes_eq @@ -446,12 +446,12 @@ impure function activation_factory_method(activation_name) result(activation) end procedure - module procedure num_outputs + module procedure default_real_num_outputs call assert_consistency(self) output_count = self%nodes_(ubound(self%nodes_,1)) end procedure - module procedure num_hidden_layers + module procedure default_real_num_hidden_layers integer, parameter :: input_layer = 1, output_layer = 1 call assert_consistency(self) associate(num_layers => size(self%nodes_)) @@ -459,23 +459,23 @@ impure function activation_factory_method(activation_name) result(activation) end associate end procedure - module procedure num_inputs + module procedure default_real_num_inputs call assert_consistency(self) input_count = self%nodes_(lbound(self%nodes_,1)) end procedure - module procedure nodes_per_layer + module procedure default_real_nodes_per_layer call assert_consistency(self) node_count = self%nodes_ end procedure - module procedure skip + module procedure default_real_skip associate(strings => self%metadata_%strings()) use_skip_connections = merge(.true., .false., strings(5) == "true") end associate end procedure - module procedure activation_function_name + module procedure default_real_activation_function_name associate(strings => self%metadata_%strings()) activation_name = strings(4) end associate From a6ccaee3a364ce6eb82d52357c2f96cc1280bb9f Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Sun, 1 Sep 2024 23:23:17 -0700 Subject: [PATCH 040/105] feat(inference_engine_t):double-precision bindings --- src/inference_engine/inference_engine_m_.f90 | 140 +++++++-- src/inference_engine/inference_engine_s.F90 | 310 ++++++++++++++++++- 2 files changed, 419 insertions(+), 31 deletions(-) diff --git a/src/inference_engine/inference_engine_m_.f90 b/src/inference_engine/inference_engine_m_.f90 index 9076dc572..294ee2b2c 100644 --- a/src/inference_engine/inference_engine_m_.f90 +++ b/src/inference_engine/inference_engine_m_.f90 @@ -5,7 +5,7 @@ module inference_engine_m_ use activation_strategy_m, only : activation_strategy_t use julienne_file_m, only : file_t use julienne_string_m, only : string_t - use kind_parameters_m, only : default_real + use kind_parameters_m, only : default_real, double_precision use metadata_m, only : metadata_t use tensor_m, only : tensor_t use tensor_map_m, only : tensor_map_t @@ -24,32 +24,32 @@ module inference_engine_m_ integer, allocatable, private :: nodes_(:) class(activation_strategy_t), allocatable, private :: activation_strategy_ ! Strategy Pattern facilitates elemental activation contains - generic :: operator(==) => default_real_approximately_equal - generic :: infer => default_real_infer - generic :: to_json => default_real_to_json - generic :: map_to_input_range => default_real_map_to_input_range - generic :: map_from_output_range => default_real_map_from_output_range - generic :: num_hidden_layers => default_real_num_hidden_layers - generic :: num_inputs => default_real_num_inputs - generic :: num_outputs => default_real_num_outputs - generic :: nodes_per_layer => default_real_nodes_per_layer - generic :: assert_conformable_with => default_real_assert_conformable_with - generic :: skip => default_real_skip - generic :: activation_function_name => default_real_activation_function_name - generic :: to_exchange => default_real_to_exchange - procedure, private :: default_real_approximately_equal - procedure, private :: default_real_infer - procedure, private :: default_real_to_json - procedure, private :: default_real_map_to_input_range - procedure, private :: default_real_map_from_output_range - procedure, private :: default_real_num_hidden_layers - procedure, private :: default_real_num_inputs - procedure, private :: default_real_num_outputs - procedure, private :: default_real_nodes_per_layer - procedure, private :: default_real_assert_conformable_with - procedure, private :: default_real_skip - procedure, private :: default_real_activation_function_name - procedure, private :: default_real_to_exchange + generic :: operator(==) => default_real_approximately_equal, double_precision_approximately_equal + generic :: infer => default_real_infer, double_precision_infer + generic :: to_json => default_real_to_json, double_precision_to_json + generic :: map_to_input_range => default_real_map_to_input_range, double_precision_map_to_input_range + generic :: map_from_output_range => default_real_map_from_output_range, double_precision_map_from_output_range + generic :: num_hidden_layers => default_real_num_hidden_layers, double_precision_num_hidden_layers + generic :: num_inputs => default_real_num_inputs, double_precision_num_inputs + generic :: num_outputs => default_real_num_outputs, double_precision_num_outputs + generic :: nodes_per_layer => default_real_nodes_per_layer, double_precision_nodes_per_layer + generic :: assert_conformable_with => default_real_assert_conformable_with, double_precision_assert_conformable_with + generic :: skip => default_real_skip, double_precision_skip + generic :: activation_function_name => default_real_activation_name, double_precision_activation_name + generic :: to_exchange => default_real_to_exchange, double_precision_to_exchange + procedure, private :: default_real_approximately_equal, double_precision_approximately_equal + procedure, private :: default_real_infer, double_precision_infer + procedure, private :: default_real_to_json, double_precision_to_json + procedure, private :: default_real_map_to_input_range, double_precision_map_to_input_range + procedure, private :: default_real_map_from_output_range, double_precision_map_from_output_range + procedure, private :: default_real_num_hidden_layers, double_precision_num_hidden_layers + procedure, private :: default_real_num_inputs, double_precision_num_inputs + procedure, private :: default_real_num_outputs, double_precision_num_outputs + procedure, private :: default_real_nodes_per_layer, double_precision_nodes_per_layer + procedure, private :: default_real_assert_conformable_with, double_precision_assert_conformable_with + procedure, private :: default_real_skip, double_precision_skip + procedure, private :: default_real_activation_name, double_precision_activation_name + procedure, private :: default_real_to_exchange, double_precision_to_exchange end type type exchange_t(k) @@ -90,6 +90,13 @@ elemental module function default_real_approximately_equal(lhs, rhs) result(lhs_ logical lhs_eq_rhs end function + elemental module function double_precision_approximately_equal(lhs, rhs) result(lhs_eq_rhs) + !! The result is true if lhs and rhs are the same to within a tolerance + implicit none + class(inference_engine_t(double_precision)), intent(in) :: lhs, rhs + logical lhs_eq_rhs + end function + elemental module function default_real_map_to_input_range(self, tensor) result(normalized_tensor) !! The result contains the input tensor values normalized to fall on the range used during training implicit none @@ -98,6 +105,14 @@ elemental module function default_real_map_to_input_range(self, tensor) result(n type(tensor_t) normalized_tensor end function + elemental module function double_precision_map_to_input_range(self, tensor) result(normalized_tensor) + !! The result contains the input tensor values normalized to fall on the range used during training + implicit none + class(inference_engine_t(double_precision)), intent(in) :: self + type(tensor_t(double_precision)), intent(in) :: tensor + type(tensor_t(double_precision)) normalized_tensor + end function + elemental module function default_real_map_from_output_range(self, normalized_tensor) result(tensor) !! The result contains the output tensor values unnormalized via the inverse of the mapping used in training implicit none @@ -106,24 +121,50 @@ elemental module function default_real_map_from_output_range(self, normalized_te type(tensor_t) tensor end function + elemental module function double_precision_map_from_output_range(self, normalized_tensor) result(tensor) + !! The result contains the output tensor values unnormalized via the inverse of the mapping used in training + implicit none + class(inference_engine_t(double_precision)), intent(in) :: self + type(tensor_t(double_precision)), intent(in) :: normalized_tensor + type(tensor_t(double_precision)) tensor + end function + impure module function default_real_to_exchange(self) result(exchange) implicit none class(inference_engine_t), intent(in) :: self type(exchange_t) exchange end function + impure module function double_precision_to_exchange(self) result(exchange) + implicit none + class(inference_engine_t(double_precision)), intent(in) :: self + type(exchange_t(double_precision)) exchange + end function + impure elemental module function default_real_to_json(self) result(json_file) implicit none class(inference_engine_t), intent(in) :: self type(file_t) json_file end function + impure elemental module function double_precision_to_json(self) result(json_file) + implicit none + class(inference_engine_t(double_precision)), intent(in) :: self + type(file_t) json_file + end function + elemental module subroutine default_real_assert_conformable_with(self, inference_engine) implicit none class(inference_engine_t), intent(in) :: self type(inference_engine_t), intent(in) :: inference_engine end subroutine + elemental module subroutine double_precision_assert_conformable_with(self, inference_engine) + implicit none + class(inference_engine_t(double_precision)), intent(in) :: self + type(inference_engine_t(double_precision)), intent(in) :: inference_engine + end subroutine + elemental module function default_real_infer(self, inputs) result(outputs) implicit none class(inference_engine_t), intent(in) :: self @@ -131,42 +172,85 @@ elemental module function default_real_infer(self, inputs) result(outputs) type(tensor_t) outputs end function + elemental module function double_precision_infer(self, inputs) result(outputs) + implicit none + class(inference_engine_t(double_precision)), intent(in) :: self + type(tensor_t(double_precision)), intent(in) :: inputs + type(tensor_t(double_precision)) outputs + end function + elemental module function default_real_num_outputs(self) result(output_count) implicit none class(inference_engine_t), intent(in) :: self integer output_count end function + elemental module function double_precision_num_outputs(self) result(output_count) + implicit none + class(inference_engine_t(double_precision)), intent(in) :: self + integer output_count + end function + elemental module function default_real_num_hidden_layers(self) result(hidden_layer_count) implicit none class(inference_engine_t), intent(in) :: self integer hidden_layer_count end function + elemental module function double_precision_num_hidden_layers(self) result(hidden_layer_count) + implicit none + class(inference_engine_t(double_precision)), intent(in) :: self + integer hidden_layer_count + end function + elemental module function default_real_num_inputs(self) result(input_count) implicit none class(inference_engine_t), intent(in) :: self integer input_count end function + elemental module function double_precision_num_inputs(self) result(input_count) + implicit none + class(inference_engine_t(double_precision)), intent(in) :: self + integer input_count + end function + pure module function default_real_nodes_per_layer(self) result(node_count) implicit none class(inference_engine_t), intent(in) :: self integer, allocatable :: node_count(:) end function - elemental module function default_real_activation_function_name(self) result(activation_name) + pure module function double_precision_nodes_per_layer(self) result(node_count) + implicit none + class(inference_engine_t(double_precision)), intent(in) :: self + integer, allocatable :: node_count(:) + end function + + elemental module function default_real_activation_name(self) result(activation_name) implicit none class(inference_engine_t), intent(in) :: self type(string_t) activation_name end function + elemental module function double_precision_activation_name(self) result(activation_name) + implicit none + class(inference_engine_t(double_precision)), intent(in) :: self + type(string_t) activation_name + end function + pure module function default_real_skip(self) result(use_skip_connections) implicit none class(inference_engine_t), intent(in) :: self logical use_skip_connections end function + pure module function double_precision_skip(self) result(use_skip_connections) + implicit none + class(inference_engine_t(double_precision)), intent(in) :: self + logical use_skip_connections + end function + end interface end module inference_engine_m_ diff --git a/src/inference_engine/inference_engine_s.F90 b/src/inference_engine/inference_engine_s.F90 index 00f2fd542..d20e556a1 100644 --- a/src/inference_engine/inference_engine_s.F90 +++ b/src/inference_engine/inference_engine_s.F90 @@ -12,7 +12,8 @@ implicit none interface assert_consistency - procedure inference_engine_consistency + procedure default_real_consistency + procedure double_precision_consistency end interface character(len=*), parameter :: acceptable_engine_tag = "0.13.0" ! git tag capable of reading the current json file format @@ -23,10 +24,18 @@ normalized_tensor = self%input_map_%map_to_training_range(tensor) end procedure + module procedure double_precision_map_to_input_range + normalized_tensor = self%input_map_%map_to_training_range(tensor) + end procedure + module procedure default_real_map_from_output_range tensor = self%output_map_%map_from_training_range(normalized_tensor) end procedure + module procedure double_precision_map_from_output_range + tensor = self%output_map_%map_from_training_range(normalized_tensor) + end procedure + module procedure default_real_to_exchange exchange%input_map_ = self%input_map_ exchange%output_map_ = self%output_map_ @@ -39,6 +48,18 @@ exchange%activation_strategy_ = self%activation_strategy_ end procedure + module procedure double_precision_to_exchange + exchange%input_map_ = self%input_map_ + exchange%output_map_ = self%output_map_ + associate(strings => self%metadata_%strings()) + exchange%metadata_ = metadata_t(strings(1),strings(2),strings(3),strings(4),strings(5)) + end associate + exchange%weights_ = self%weights_ + exchange%biases_ = self%biases_ + exchange%nodes_ = self%nodes_ + exchange%activation_strategy_ = self%activation_strategy_ + end procedure + module procedure default_real_infer real, allocatable :: a(:,:) @@ -88,7 +109,56 @@ end procedure - pure subroutine inference_engine_consistency(self) + module procedure double_precision_infer + + double precision, allocatable :: a(:,:) + integer, parameter :: input_layer = 0 + integer l + + call assert_consistency(self) + + associate(w => self%weights_, b => self%biases_, n => self%nodes_, output_layer => ubound(self%nodes_,1)) + + allocate(a(maxval(n), input_layer:output_layer)) + +#ifndef _CRAYFTN + associate(normalized_inputs => self%input_map_%map_to_training_range(inputs)) + a(1:n(input_layer),input_layer) = normalized_inputs%values() + end associate +#else + block + type(tensor_t) normalized_inputs + normalized_inputs = self%input_map_%map_to_training_range(inputs) + a(1:n(input_layer),input_layer) = normalized_inputs%values() + end block +#endif + + feed_forward: & + do l = input_layer+1, output_layer + associate(z => matmul(w(1:n(l),1:n(l-1),l), a(1:n(l-1),l-1)) + b(1:n(l),l)) + a(1:n(l),l) = self%activation_strategy_%activation(z) + end associate + end do feed_forward + +#ifdef _CRAYFTN + block + type(tensor_t) :: normalized_outputs + normalized_outputs = tensor_t(a(1:n(output_layer), output_layer)) +#else + associate(normalized_outputs => tensor_t(a(1:n(output_layer), output_layer))) +#endif + outputs = self%output_map_%map_from_training_range(normalized_outputs) +#ifdef _CRAYFTN + end block +#else + end associate +#endif + + end associate + + end procedure + + pure subroutine default_real_consistency(self) type(inference_engine_t), intent(in) :: self @@ -113,6 +183,31 @@ pure subroutine inference_engine_consistency(self) end subroutine + pure subroutine double_precision_consistency(self) + + type(inference_engine_t(double_precision)), intent(in) :: self + + integer, parameter :: input_layer=0 + + associate( & + all_allocated=>[allocated(self%weights_),allocated(self%biases_),allocated(self%nodes_),allocated(self%activation_strategy_)]& + ) + call assert(all(all_allocated),"inference_engine_s(inference_engine_consistency): fully_allocated", & + intrinsic_array_t(all_allocated)) + end associate + + associate(max_width=>maxval(self%nodes_), component_dims=>[size(self%biases_,1), size(self%weights_,1), size(self%weights_,2)]) + call assert(all(component_dims == max_width), "inference_engine_s(inference_engine_consistency): conformable arrays", & + intrinsic_array_t([max_width,component_dims])) + end associate + + associate(input_subscript => lbound(self%nodes_,1)) + call assert(input_subscript == input_layer, "inference_engine_s(inference_engine_consistency): n base subsscript", & + input_subscript) + end associate + + end subroutine + impure function activation_factory_method(activation_name) result(activation) character(len=*), intent(in) :: activation_name class(activation_strategy_t), allocatable :: activation @@ -287,6 +382,122 @@ impure function activation_factory_method(activation_name) result(activation) end associate end procedure default_real_to_json + module procedure double_precision_to_json + +#ifdef _CRAYFTN + type(tensor_map_t) proto_map + type(metadata_t) proto_meta + type(neuron_t) proto_neuron + proto_map = tensor_map_t("",[0.],[1.]) + proto_meta = metadata_t(string_t(""),string_t(""),string_t(""),string_t(""),string_t("")) + proto_neuron = neuron_t([0.],1.) +#endif + + call assert_consistency(self) + + associate( & + num_hidden_layers => self%num_hidden_layers() & + ,num_outputs => self%num_outputs() & + ,num_inputs => self%num_inputs() & + ,first_hidden => lbound(self%nodes_,1) + 1 & + ,last_hidden => ubound(self%nodes_,1) - 1 & +#ifndef _CRAYFTN + ,proto_map => tensor_map_t("",[0.],[1.]) & + ,proto_meta => metadata_t(string_t(""),string_t(""),string_t(""),string_t(""),string_t("")) & + ,proto_neuron => neuron_t([0.],0.) & +#endif + ) + associate( & + metadata_lines => size(proto_meta%to_json()), & + tensor_map_lines => size(proto_map%to_json()), & + neuron_lines => size(proto_neuron%to_json()) & + ) + block + type(string_t), allocatable :: lines(:) + integer layer, n, line + integer, parameter :: & + brace = 1, bracket_hidden_layers_array = 1, bracket_layer = 1, bracket_output_layer = 1, file_version_lines = 1 + + associate( json_lines => & + brace + & ! { + file_version_lines + & ! "acceptable_engine_tag": ... + metadata_lines + & ! "metadata": ... + tensor_map_lines + & ! "inputs_tensor_map": ... + tensor_map_lines + & ! "outputs_tensor_map": ... + bracket_hidden_layers_array + & ! "hidden_layers": [ + bracket_layer*num_hidden_layers + & ! [ + neuron_lines*sum(self%nodes_(first_hidden:last_hidden))+ & ! neuron ... + bracket_layer*num_hidden_layers + & ! ] ... + bracket_hidden_layers_array + & ! ], + bracket_output_layer + & ! "output_layer": [ + neuron_lines*num_outputs + & ! neurons + bracket_output_layer + & ! ] + brace & ! } + ) + allocate(lines(json_lines)) + lines(brace) = string_t('{') + lines(brace+1:brace+file_version_lines)= string_t(' "acceptable_engine_tag": "')//acceptable_engine_tag//'",' + associate(meta_start => brace + file_version_lines + 1) + associate(meta_end => meta_start + metadata_lines - 1) + lines(meta_start:meta_end) = self%metadata_%to_json() + lines(meta_end) = lines(meta_end) // "," + associate(input_map_start => meta_end + 1, input_map_end => meta_end + tensor_map_lines) + lines(input_map_start:input_map_end) = self%input_map_%to_json() + lines(input_map_end) = lines(input_map_end) // "," + associate(output_map_start => input_map_end + 1, output_map_end => input_map_end + tensor_map_lines) + lines(output_map_start:output_map_end) = self%output_map_%to_json() + lines(output_map_end) = lines(output_map_end) // "," + lines(output_map_end + 1) = string_t(' "hidden_layers": [') + line= output_map_end + 1 + end associate + end associate + end associate + end associate + do layer = first_hidden, last_hidden + line = line + 1 + lines(line) = string_t(' [') + do n = 1, self%nodes_(layer) + associate( & + neuron => neuron_t(weights=self%weights_(n,1:self%nodes_(layer-1),layer), bias=self%biases_(n,layer)), & + neuron_start => line + 1, & + neuron_end => line + neuron_lines & + ) + lines(neuron_start:neuron_end) = neuron%to_json() + lines(neuron_end) = lines(neuron_end) // trim(merge(" ", ",", n==self%nodes_(layer))) + end associate + line = line + neuron_lines + end do + line = line + 1 + lines(line) = string_t(' ]') // trim(merge(" ", ",", layer==last_hidden)) + end do + line = line + 1 + lines(line) = string_t(' ],') + line = line + 1 + lines(line) = string_t(' "output_layer": [') + layer = last_hidden + 1 + do n = 1, self%nodes_(layer) + associate( & + neuron => neuron_t(weights=self%weights_(n,1:self%nodes_(layer-1),layer), bias=self%biases_(n,layer)), & + neuron_start=>line+1, & + neuron_end=>line+neuron_lines & + ) + lines(neuron_start:neuron_end) = neuron%to_json() + lines(neuron_end) = lines(neuron_end) // trim(merge(" ", ",", n==self%nodes_(layer))) + end associate + line = line + neuron_lines + end do + line = line + 1 + lines(line) = string_t(' ]') + line = line + 1 + lines(line) = string_t('}') + call assert(line == json_lines, "inference_engine_t%to_json: all lines defined", intrinsic_array_t([json_lines, line])) + end associate + json_file = file_t(lines) + end block + end associate + end associate + end procedure double_precision_to_json + module procedure default_real_from_json character(len=:), allocatable :: justified_line @@ -405,6 +616,23 @@ impure function activation_factory_method(activation_name) result(activation) end procedure + module procedure double_precision_assert_conformable_with + + call assert_consistency(self) + call assert_consistency(inference_engine) + + associate(equal_shapes => [ & + shape(self%weights_) == shape(inference_engine%weights_), & + shape(self%biases_) == shape(inference_engine%biases_), & + shape(self%nodes_) == shape(inference_engine%nodes_) & + ]) + call assert(all(equal_shapes), "assert_conformable_with: all(equal_shapes)", intrinsic_array_t(equal_shapes)) + end associate + + call assert(same_type_as(self%activation_strategy_, inference_engine%activation_strategy_), "assert_conformable_with: types)") + + end procedure + module procedure default_real_approximately_equal logical nodes_eq @@ -446,11 +674,57 @@ impure function activation_factory_method(activation_name) result(activation) end procedure + module procedure double_precision_approximately_equal + + logical nodes_eq + + nodes_eq = all(lhs%nodes_ == rhs%nodes_) + + call assert_consistency(lhs) + call assert_consistency(rhs) + call lhs%assert_conformable_with(rhs) + + block + integer l + logical layer_eq(ubound(lhs%nodes_,1)) + real, parameter :: tolerance = 1.D-12 + + associate(n => lhs%nodes_) +#ifndef __INTEL_COMPILER + do concurrent(l = 1:ubound(n,1)) + layer_eq(l) = all(abs(lhs%weights_(1:n(l),1:n(l-1),l) - rhs%weights_(1:n(l),1:n(l-1),l)) < tolerance) .and. & + all(abs(lhs%biases_(1:n(l),l) - rhs%biases_(1:n(l),l)) < tolerance) + end do +#else + block + integer j, k + do l = 1, ubound(n,1) + do j = 1, n(l) + do k = 1, n(l-1) + layer_eq(l) = all(abs(lhs%weights_(j,k,l) - rhs%weights_(j,k,l)) < tolerance) .and. & + all(abs(lhs%biases_(j,l) - rhs%biases_(j,l)) < tolerance) + end do + end do + end do + end block +#endif + end associate + + lhs_eq_rhs = nodes_eq .and. all(layer_eq) + end block + + end procedure + module procedure default_real_num_outputs call assert_consistency(self) output_count = self%nodes_(ubound(self%nodes_,1)) end procedure + module procedure double_precision_num_outputs + call assert_consistency(self) + output_count = self%nodes_(ubound(self%nodes_,1)) + end procedure + module procedure default_real_num_hidden_layers integer, parameter :: input_layer = 1, output_layer = 1 call assert_consistency(self) @@ -459,23 +733,53 @@ impure function activation_factory_method(activation_name) result(activation) end associate end procedure + module procedure double_precision_num_hidden_layers + integer, parameter :: input_layer = 1, output_layer = 1 + call assert_consistency(self) + associate(num_layers => size(self%nodes_)) + hidden_layer_count = num_layers - (input_layer + output_layer) + end associate + end procedure + module procedure default_real_num_inputs call assert_consistency(self) input_count = self%nodes_(lbound(self%nodes_,1)) end procedure + module procedure double_precision_num_inputs + call assert_consistency(self) + input_count = self%nodes_(lbound(self%nodes_,1)) + end procedure + module procedure default_real_nodes_per_layer call assert_consistency(self) node_count = self%nodes_ end procedure + module procedure double_precision_nodes_per_layer + call assert_consistency(self) + node_count = self%nodes_ + end procedure + module procedure default_real_skip associate(strings => self%metadata_%strings()) use_skip_connections = merge(.true., .false., strings(5) == "true") end associate end procedure - module procedure default_real_activation_function_name + module procedure double_precision_skip + associate(strings => self%metadata_%strings()) + use_skip_connections = merge(.true., .false., strings(5) == "true") + end associate + end procedure + + module procedure default_real_activation_name + associate(strings => self%metadata_%strings()) + activation_name = strings(4) + end associate + end procedure + + module procedure double_precision_activation_name associate(strings => self%metadata_%strings()) activation_name = strings(4) end associate From cd3acd6d25f3352a6cf5f4fac16ec6f75ee94dc2 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Mon, 2 Sep 2024 12:20:39 -0700 Subject: [PATCH 041/105] test(inference_engine): double-precision inference This is the first commit with a passing test of double-precision inference using parameterized derived types. --- src/inference_engine/inference_engine_m_.f90 | 12 ++++- src/inference_engine/inference_engine_s.F90 | 56 +++++++++++++++++--- src/inference_engine/metadata_m.f90 | 11 +++- src/inference_engine/metadata_s.f90 | 4 ++ test/inference_engine_test_m.F90 | 44 +++++++++++++-- 5 files changed, 113 insertions(+), 14 deletions(-) diff --git a/src/inference_engine/inference_engine_m_.f90 b/src/inference_engine/inference_engine_m_.f90 index 294ee2b2c..46985483a 100644 --- a/src/inference_engine/inference_engine_m_.f90 +++ b/src/inference_engine/inference_engine_m_.f90 @@ -63,7 +63,7 @@ module inference_engine_m_ interface inference_engine_t - impure module function default_real_construct_from_padded_arrays(metadata, weights, biases, nodes, input_map, output_map) & + impure module function default_real_construct_from_components(metadata, weights, biases, nodes, input_map, output_map) & result(inference_engine) implicit none type(string_t), intent(in) :: metadata(:) @@ -73,6 +73,16 @@ impure module function default_real_construct_from_padded_arrays(metadata, weigh type(inference_engine_t) inference_engine end function + impure module function double_precision_construct_from_components(metadata, weights, biases, nodes, input_map, output_map) & + result(inference_engine) + implicit none + type(metadata_t), intent(in) :: metadata + double precision, intent(in) :: weights(:,:,:), biases(:,:) + integer, intent(in) :: nodes(0:) + type(tensor_map_t(double_precision)), intent(in), optional :: input_map, output_map + type(inference_engine_t(double_precision)) inference_engine + end function + impure elemental module function default_real_from_json(file_) result(inference_engine) implicit none type(file_t), intent(in) :: file_ diff --git a/src/inference_engine/inference_engine_s.F90 b/src/inference_engine/inference_engine_s.F90 index d20e556a1..4ac1b5871 100644 --- a/src/inference_engine/inference_engine_s.F90 +++ b/src/inference_engine/inference_engine_s.F90 @@ -228,7 +228,7 @@ impure function activation_factory_method(activation_name) result(activation) end select end function - module procedure default_real_construct_from_padded_arrays + module procedure default_real_construct_from_components inference_engine%metadata_ = metadata_t(metadata(1),metadata(2),metadata(3),metadata(4),metadata(5)) inference_engine%weights_ = weights @@ -264,7 +264,47 @@ impure function activation_factory_method(activation_name) result(activation) call assert_consistency(inference_engine) - end procedure default_real_construct_from_padded_arrays + end procedure default_real_construct_from_components + + module procedure double_precision_construct_from_components + + inference_engine%metadata_ = metadata + inference_engine%weights_ = weights + inference_engine%biases_ = biases + inference_engine%nodes_ = nodes + + block + integer i + + if (present(input_map)) then + inference_engine%input_map_ = input_map + else + associate(num_inputs => nodes(lbound(nodes,1))) + associate(default_intercept => [(0D0, i=1,num_inputs)], default_slope => [(1D0, i=1,num_inputs)]) + inference_engine%input_map_ = tensor_map_t("inputs", default_intercept, default_slope) + end associate + end associate + end if + + if (present(output_map)) then + inference_engine%output_map_ = output_map + else + associate(num_outputs => nodes(ubound(nodes,1))) + associate(default_intercept => [(0D0, i=1,num_outputs)], default_slope => [(1D0, i=1,num_outputs)]) + inference_engine%output_map_ = tensor_map_t("outputs", default_intercept, default_slope) + end associate + end associate + end if + end block + + if (allocated(inference_engine%activation_strategy_)) deallocate(inference_engine%activation_strategy_) + associate(function_name => metadata%activation_name()) + allocate(inference_engine%activation_strategy_, source = activation_factory_method(function_name%string())) + end associate + + call assert_consistency(inference_engine) + + end procedure double_precision_construct_from_components module procedure default_real_to_json @@ -388,9 +428,9 @@ impure function activation_factory_method(activation_name) result(activation) type(tensor_map_t) proto_map type(metadata_t) proto_meta type(neuron_t) proto_neuron - proto_map = tensor_map_t("",[0.],[1.]) + proto_map = tensor_map_t("",[0D0],[1D0]) proto_meta = metadata_t(string_t(""),string_t(""),string_t(""),string_t(""),string_t("")) - proto_neuron = neuron_t([0.],1.) + proto_neuron = neuron_t([0D0],1D0) #endif call assert_consistency(self) @@ -402,9 +442,9 @@ impure function activation_factory_method(activation_name) result(activation) ,first_hidden => lbound(self%nodes_,1) + 1 & ,last_hidden => ubound(self%nodes_,1) - 1 & #ifndef _CRAYFTN - ,proto_map => tensor_map_t("",[0.],[1.]) & + ,proto_map => tensor_map_t("",[0D0],[0D0]) & ,proto_meta => metadata_t(string_t(""),string_t(""),string_t(""),string_t(""),string_t("")) & - ,proto_neuron => neuron_t([0.],0.) & + ,proto_neuron => neuron_t([0D0],0D0) & #endif ) associate( & @@ -590,7 +630,9 @@ impure function activation_factory_method(activation_name) result(activation) associate(metadata_strings => metadata%strings()) inference_engine = hidden_layers%inference_engine(metadata_strings, output_layer, input_map, output_map) if (allocated(inference_engine%activation_strategy_)) deallocate(inference_engine%activation_strategy_) - allocate(inference_engine%activation_strategy_, source = activation_factory_method(metadata_strings(4)%string())) + associate(function_name => metadata%activation_name()) + allocate(inference_engine%activation_strategy_, source = activation_factory_method(function_name%string())) + end associate end associate end associate end associate read_metadata diff --git a/src/inference_engine/metadata_m.f90 b/src/inference_engine/metadata_m.f90 index 09683fc57..6dcf5c909 100644 --- a/src/inference_engine/metadata_m.f90 +++ b/src/inference_engine/metadata_m.f90 @@ -10,10 +10,11 @@ module metadata_m private type(string_t) modelName_, modelAuthor_, compilationDate_, activationFunction_, usingSkipConnections_ contains + generic :: operator(==) => equals procedure :: strings procedure :: to_json - procedure :: equals - generic :: operator(==) => equals + procedure :: activation_name + procedure, private :: equals end type interface metadata_t @@ -47,6 +48,12 @@ pure module function strings(self) result(components) type(string_t), allocatable :: components(:) end function + pure module function activation_name(self) result(function_name) + implicit none + class(metadata_t), intent(in) :: self + type(string_t) function_name + end function + pure module function to_json(self) result(lines) implicit none class(metadata_t), intent(in) :: self diff --git a/src/inference_engine/metadata_s.f90 b/src/inference_engine/metadata_s.f90 index 92597e2c6..c2702bcd4 100644 --- a/src/inference_engine/metadata_s.f90 +++ b/src/inference_engine/metadata_s.f90 @@ -8,6 +8,10 @@ components = [self%modelName_, self%modelAuthor_, self%compilationDate_, self%activationFunction_, self%usingSkipConnections_] end procedure + module procedure activation_name + function_name = self%activationFunction_ + end procedure + module procedure from_components metadata%modelName_ = modelName metadata%modelAuthor_ = modelAuthor diff --git a/test/inference_engine_test_m.F90 b/test/inference_engine_test_m.F90 index a2c1bfe1d..128598ae4 100644 --- a/test/inference_engine_test_m.F90 +++ b/test/inference_engine_test_m.F90 @@ -5,13 +5,14 @@ module inference_engine_test_m ! External dependencies use assert_m, only : assert + use kind_parameters_m, only : double_precision use julienne_m, only : test_t, test_result_t, test_description_t, test_description_substring, string_t, file_t #ifdef __GFORTRAN__ use julienne_m, only : test_function_i #endif ! Internal dependencies - use inference_engine_m, only : inference_engine_t, tensor_t + use inference_engine_m, only : inference_engine_t, tensor_t, metadata_t implicit none @@ -42,22 +43,26 @@ function results() result(test_results) ,test_description_t("converting a network with 2 hidden layers to and from JSON format", multi_hidden_layer_net_to_from_json)& ,test_description_t("converting a network with varying-width hidden layers to/from JSON", varying_width_net_to_from_json) & ,test_description_t("performing inference with a network with hidden layers of varying width", infer_with_varying_width_net) & + ,test_description_t("double-precision inference", double_precision_inference) & ] #else - procedure(test_function_i), pointer :: & - elemental_infer_1_ptr, elemental_infer_2_ptr, multi_hidden_ptr, vary_width_ptr, vary_width_infer_ptr + procedure(test_function_i), pointer :: elemental_infer_1_ptr, elemental_infer_2_ptr, multi_hidden_ptr, vary_width_ptr, & + vary_width_infer_ptr, double_precision_inference_ptr + elemental_infer_1_ptr => elemental_infer_with_1_hidden_layer_xor_net elemental_infer_2_ptr => elemental_infer_with_2_hidden_layer_xor_net multi_hidden_ptr => multi_hidden_layer_net_to_from_json vary_width_ptr => varying_width_net_to_from_json vary_width_infer_ptr => infer_with_varying_width_net + double_precision_inference_ptr => double_precision_inference test_descriptions = [ & test_description_t("performing elemental inference with 1 hidden layer", elemental_infer_1_ptr) & ,test_description_t("performing elemental inference with 2 hidden layers", elemental_infer_2_ptr) & ,test_description_t("converting a network with 2 hidden layers to and from JSON format", multi_hidden_ptr) & ,test_description_t("converting a network with varying-width hidden layers to/from JSON", vary_width_ptr) & - ,test_description_t("performing inference with varyring-width hidden layers", vary_width_infer_ptr) & + ,test_description_t("performing inference with varying-width hidden layers", vary_width_infer_ptr) & + ,test_description_t("double-precision inference", double_precision_inference_ptr) & ] #endif associate( & @@ -122,6 +127,25 @@ function decrement_split_combine_increment() result(inference_engine) ) end function + function double_precision_network() result(inference_engine) + !! The result is a double-precision version of the this network defined in the decrement_split_combine_increment function + type(inference_engine_t(double_precision)) inference_engine + integer, parameter :: inputs = 2, hidden(*) = [2,3], outputs = 2 ! number of neurons in input, output, and hidden layers + integer, parameter :: n(*) = [inputs, hidden(1), hidden(2), outputs] ! nodes per layer + integer, parameter :: n_max = maxval(n), layers=size(n) ! max layer width, number of layers + integer, parameter :: w_shape(*) = [n_max, n_max, layers-1], b_shape(*) = [n_max, n_max] + double precision, parameter :: & + w(*,*,*) = reshape( & + [double precision :: 1.,0.,0., 0.,1.,0., 0.,0.,0., 1.,0.,0., 0.,.5,.5, 0.,0.,0., 1.,0.,0., 0.,1.,0., 0.,1.,1.], w_shape & + ), b(*,*) = reshape( [double precision :: 0.,-1.,0., 0.,0.,0., 0.,1.,0.], b_shape) + + inference_engine = inference_engine_t( & + metadata = metadata_t( & + string_t("Double-Precision"), string_t("Damian Rouson"), string_t("2024-09-02"), string_t("relu"), string_t("false") & + ), weights = w, biases = b, nodes = n & + ) + end function + function varying_width() result(inference_engine) type(inference_engine_t) inference_engine integer, parameter :: inputs = 2, hidden(*) = [2,3], outputs = 2 ! number of neurons in input, output, and hidden layers @@ -191,6 +215,18 @@ function infer_with_varying_width_net() result(test_passes) test_passes = all(abs(inputs%values() - outputs%values()) < tolerance) end function + function double_precision_inference() result(test_passes) + logical test_passes + type(inference_engine_t(double_precision)) inference_engine + type(tensor_t(double_precision)) inputs, outputs + real, parameter :: tolerance = 1.D-08 + + inference_engine = double_precision_network() + inputs = tensor_t([1.1D0, 2.7D0]) + outputs = inference_engine%infer(inputs) + test_passes = all(abs(inputs%values() - outputs%values()) < tolerance) + end function + function elemental_infer_with_1_hidden_layer_xor_net() result(test_passes) logical test_passes type(inference_engine_t) inference_engine From bf1c126410ee74170686dd5e676002e424c4c788 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Mon, 2 Sep 2024 12:52:26 -0700 Subject: [PATCH 042/105] test(inference_engine): whitespace edits --- test/inference_engine_test_m.F90 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/inference_engine_test_m.F90 b/test/inference_engine_test_m.F90 index 128598ae4..2d7d3071c 100644 --- a/test/inference_engine_test_m.F90 +++ b/test/inference_engine_test_m.F90 @@ -112,7 +112,7 @@ function decrement_split_combine_increment() result(inference_engine) integer, parameter :: n(*) = [inputs, hidden(1), hidden(2), outputs] ! nodes per layer integer, parameter :: n_max = maxval(n), layers=size(n) ! max layer width, number of layers integer, parameter :: w_shape(*) = [n_max, n_max, layers-1], b_shape(*) = [n_max, n_max] - real, parameter :: & + real, parameter :: & w(*,*,*) = reshape( [1.,0.,0., 0.,1.,0., 0.,0.,0., 1.,0.,0., 0.,.5,.5, 0.,0.,0., 1.,0.,0., 0.,1.,0., 0.,1.,1.], w_shape), & b(*,*) = reshape( [0.,-1.,0., 0.,0.,0., 0.,1.,0.], b_shape) @@ -134,11 +134,11 @@ function double_precision_network() result(inference_engine) integer, parameter :: n(*) = [inputs, hidden(1), hidden(2), outputs] ! nodes per layer integer, parameter :: n_max = maxval(n), layers=size(n) ! max layer width, number of layers integer, parameter :: w_shape(*) = [n_max, n_max, layers-1], b_shape(*) = [n_max, n_max] - double precision, parameter :: & + double precision, parameter :: & w(*,*,*) = reshape( & [double precision :: 1.,0.,0., 0.,1.,0., 0.,0.,0., 1.,0.,0., 0.,.5,.5, 0.,0.,0., 1.,0.,0., 0.,1.,0., 0.,1.,1.], w_shape & ), b(*,*) = reshape( [double precision :: 0.,-1.,0., 0.,0.,0., 0.,1.,0.], b_shape) - + inference_engine = inference_engine_t( & metadata = metadata_t( & string_t("Double-Precision"), string_t("Damian Rouson"), string_t("2024-09-02"), string_t("relu"), string_t("false") & From 3abb069a58501a0a72bd6e89bcff9f8c04cac557 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Mon, 2 Sep 2024 17:31:42 -0700 Subject: [PATCH 043/105] fix(inference_engine_m): make entities public This commit exposes the double_precision kind parameter and the double_precision_string_t in the inference_engine_m module. --- src/inference_engine_m.f90 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/inference_engine_m.f90 b/src/inference_engine_m.f90 index ca7a8c1eb..5f961b85b 100644 --- a/src/inference_engine_m.f90 +++ b/src/inference_engine_m.f90 @@ -4,13 +4,15 @@ module inference_engine_m !! Specify the user-facing modules, derived types, and type parameters use activation_strategy_m, only : activation_strategy_t use differentiable_activation_strategy_m, only : differentiable_activation_strategy_t + use double_precision_string_m, only : double_precision_string_t + use gelu_m, only : gelu_t use hyperparameters_m, only : hyperparameters_t use input_output_pair_m, only : input_output_pair_t, shuffle, write_to_stdout use inference_engine_m_, only : inference_engine_t + use kind_parameters_m, only : default_real, double_precision use metadata_m, only : metadata_t use mini_batch_m, only : mini_batch_t use network_configuration_m, only : network_configuration_t - use gelu_m, only : gelu_t use relu_m, only : relu_t use sigmoid_m, only : sigmoid_t use step_m, only : step_t From 35585740a65b6f3d8d9c4d91cc34cc363ab3ce0b Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Mon, 2 Sep 2024 21:24:13 -0700 Subject: [PATCH 044/105] build(fpm): update dependency to julienne 1.2.2 --- fpm.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fpm.toml b/fpm.toml index 7e22c0bb6..cd88caba8 100644 --- a/fpm.toml +++ b/fpm.toml @@ -8,4 +8,4 @@ maintainer = "rouson@lbl.gov" assert = {git = "https://github.com/sourceryinstitute/assert", tag = "1.7.0"} [dev-dependencies] -julienne = {git = "https://github.com/berkeleylab/julienne", tag = "1.2.1"} +julienne = {git = "https://github.com/berkeleylab/julienne", tag = "1.2.2"} From f4c0c6ffd2dbeb68fbfecaf7d0a89559256c22e7 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Mon, 2 Sep 2024 21:25:34 -0700 Subject: [PATCH 045/105] doc(example):double-precision concurrent inference --- example/concurrent-inferences.f90 | 83 ++++++++---- .../double_precision_file_m.f90 | 20 ++- .../double_precision_file_s.f90 | 8 ++ src/inference_engine/inference_engine_m_.f90 | 10 +- src/inference_engine/inference_engine_s.F90 | 125 ++++++++++++++++-- src/inference_engine/layer_m.f90 | 22 ++- src/inference_engine/layer_s.f90 | 74 ++++++++++- src/inference_engine_m.f90 | 1 + 8 files changed, 296 insertions(+), 47 deletions(-) diff --git a/example/concurrent-inferences.f90 b/example/concurrent-inferences.f90 index 7a1426096..5036e67b7 100644 --- a/example/concurrent-inferences.f90 +++ b/example/concurrent-inferences.f90 @@ -3,7 +3,7 @@ program concurrent_inferences !! This program demonstrates how to read a neural network from a JSON file !! and use the network to perform concurrent inferences. - use inference_engine_m, only : inference_engine_t, tensor_t + use inference_engine_m, only : inference_engine_t, tensor_t, double_precision, double_precision_string_t, double_precision_file_t use julienne_m, only : string_t, command_line_t, file_t use assert_m, only : assert use iso_fortran_env, only : int64, real64 @@ -19,35 +19,38 @@ program concurrent_inferences 'Usage: fpm run --example concurrent-inferences --profile release --flag "-fopenmp" -- --network ""' end if - block - type(inference_engine_t) inference_engine - type(tensor_t), allocatable :: inputs(:,:,:), outputs(:,:,:) - real, allocatable :: input_components(:,:,:,:) + block integer, parameter :: lat=263, lon=317, lev=15 ! latitudes, longitudes, levels (elevations) integer i, j, k - print *, "Constructing a new inference_engine_t object from the file " // network_file_name%string() - inference_engine = inference_engine_t(file_t(network_file_name)) + single_precision_inference: & + block + integer(int64) t_start, t_finish, clock_rate - print *,"Defining an array of tensor_t input objects with random normalized components" - allocate(inputs(lat,lon,lev)) - allocate(input_components(lat,lon,lev,inference_engine%num_inputs())) - call random_number(input_components) + type(inference_engine_t) inference_engine + type(tensor_t), allocatable :: inputs(:,:,:), outputs(:,:,:) + real, allocatable :: input_components(:,:,:,:) - do concurrent(i=1:lat, j=1:lon, k=1:lev) - inputs(i,j,k) = tensor_t(input_components(i,j,k,:)) - end do + print *, "Constructing a new inference_engine_t object from the file " // network_file_name%string() + inference_engine = inference_engine_t(file_t(network_file_name)) - block - integer(int64) t_start, t_finish, clock_rate + print *,"Defining an array of tensor_t input objects with random normalized components" + allocate(outputs(lat,lon,lev)) + allocate( inputs(lat,lon,lev)) + allocate(input_components(lat,lon,lev,inference_engine%num_inputs())) + call random_number(input_components) - print *,"Performing elemental inferences" + do concurrent(i=1:lat, j=1:lon, k=1:lev) + inputs(i,j,k) = tensor_t(input_components(i,j,k,:)) + end do + + print *,"Performing concurrent inference" call system_clock(t_start, clock_rate) - outputs = inference_engine%infer(inputs) ! implicit allocation of outputs array + do concurrent(i=1:lat, j=1:lon, k=1:lev) + outputs(i,j,k) = inference_engine%infer(inputs(i,j,k)) + end do call system_clock(t_finish) - print *,"Elemental inference time: ", real(t_finish - t_start, real64)/real(clock_rate, real64) - - call assert(all(shape(outputs) == shape(inputs)), "all(shape(outputs) == shape(inputs))") + print *,"Concurrent inference time: ", real(t_finish - t_start, real64)/real(clock_rate, real64) print *,"Performing loop-based inference" call system_clock(t_start) @@ -61,15 +64,45 @@ program concurrent_inferences call system_clock(t_finish) print *,"Looping inference time: ", real(t_finish - t_start, real64)/real(clock_rate, real64) - print *,"Performing concurrent inference" - call system_clock(t_start) + print *,"Performing elemental inferences" + call system_clock(t_start, clock_rate) + outputs = inference_engine%infer(inputs) ! implicit (re)allocation of outputs array only if shape(inputs) /= shape(outputs) + call system_clock(t_finish) + print *,"Elemental inference time: ", real(t_finish - t_start, real64)/real(clock_rate, real64) + + end block single_precision_inference + + double_precision_inference: & + block + integer(int64) t_start, t_finish, clock_rate + + type(inference_engine_t(double_precision)) inference_engine + type(tensor_t(double_precision)), allocatable :: inputs(:,:,:), outputs(:,:,:) + double precision, allocatable :: input_components(:,:,:,:) + + print *, "Constructing a new inference_engine_t object from the file " // network_file_name%string() + inference_engine = inference_engine_t(double_precision_file_t(network_file_name)) + + print *,"Defining an array of tensor_t input objects with random normalized components" + allocate(outputs(lat,lon,lev)) + allocate( inputs(lat,lon,lev)) + allocate(input_components(lat,lon,lev,inference_engine%num_inputs())) + call random_number(input_components) + + do concurrent(i=1:lat, j=1:lon, k=1:lev) + inputs(i,j,k) = tensor_t(input_components(i,j,k,:)) + end do + + print *,"Performing double-precision concurrent inference" + call system_clock(t_start, clock_rate) do concurrent(i=1:lat, j=1:lon, k=1:lev) outputs(i,j,k) = inference_engine%infer(inputs(i,j,k)) end do call system_clock(t_finish) - print *,"Concurrent inference time: ", real(t_finish - t_start, real64)/real(clock_rate, real64) + print *,"Double-precision concurrent inference time: ", real(t_finish - t_start, real64)/real(clock_rate, real64) + + end block double_precision_inference - end block end block end program diff --git a/src/inference_engine/double_precision_file_m.f90 b/src/inference_engine/double_precision_file_m.f90 index 90b50cd98..6264116ef 100644 --- a/src/inference_engine/double_precision_file_m.f90 +++ b/src/inference_engine/double_precision_file_m.f90 @@ -1,5 +1,5 @@ module double_precision_file_m - use julienne_m, only : file_t + use julienne_m, only : file_t, string_t use double_precision_string_m, only : double_precision_string_t implicit none @@ -9,9 +9,25 @@ module double_precision_file_m procedure double_precision_lines end type + interface double_precision_file_t + + impure elemental module function construct_from_string(file_name) result(double_precision_file) + implicit none + type(string_t), intent(in) :: file_name + type(double_precision_file_t) double_precision_file + end function + + impure elemental module function construct_from_character(file_name) result(double_precision_file) + implicit none + character(len=*), intent(in) :: file_name + type(double_precision_file_t) double_precision_file + end function + + end interface + interface - module function double_precision_lines(self) result(lines) + pure module function double_precision_lines(self) result(lines) implicit none class(double_precision_file_t), intent(in) :: self type(double_precision_string_t), allocatable :: lines(:) diff --git a/src/inference_engine/double_precision_file_s.f90 b/src/inference_engine/double_precision_file_s.f90 index 9d8eec923..961bfde48 100644 --- a/src/inference_engine/double_precision_file_s.f90 +++ b/src/inference_engine/double_precision_file_s.f90 @@ -3,6 +3,14 @@ contains + module procedure construct_from_string + double_precision_file%file_t = file_t(file_name) + end procedure + + module procedure construct_from_character + double_precision_file%file_t = file_t(file_name) + end procedure + module procedure double_precision_lines lines = double_precision_string_t(self%file_t%lines()) end procedure diff --git a/src/inference_engine/inference_engine_m_.f90 b/src/inference_engine/inference_engine_m_.f90 index 46985483a..136a143aa 100644 --- a/src/inference_engine/inference_engine_m_.f90 +++ b/src/inference_engine/inference_engine_m_.f90 @@ -3,9 +3,9 @@ module inference_engine_m_ !! Define an abstraction that supports inference operationsn on a neural network use activation_strategy_m, only : activation_strategy_t - use julienne_file_m, only : file_t - use julienne_string_m, only : string_t + use double_precision_file_m, only : double_precision_file_t use kind_parameters_m, only : default_real, double_precision + use julienne_m, only : file_t, string_t use metadata_m, only : metadata_t use tensor_m, only : tensor_t use tensor_map_m, only : tensor_map_t @@ -89,6 +89,12 @@ impure elemental module function default_real_from_json(file_) result(inference_ type(inference_engine_t) inference_engine end function + impure elemental module function double_precision_from_json(file) result(inference_engine) + implicit none + type(double_precision_file_t), intent(in) :: file + type(inference_engine_t(double_precision)) inference_engine + end function + end interface interface diff --git a/src/inference_engine/inference_engine_s.F90 b/src/inference_engine/inference_engine_s.F90 index 4ac1b5871..4bd3d6cd5 100644 --- a/src/inference_engine/inference_engine_s.F90 +++ b/src/inference_engine/inference_engine_s.F90 @@ -2,13 +2,15 @@ ! Terms of use are as specified in LICENSE.txt submodule(inference_engine_m_) inference_engine_s use assert_m, only : assert, intrinsic_array_t - use step_m, only : step_t - use swish_m, only : swish_t - use sigmoid_m, only : sigmoid_t + use double_precision_string_m, only : double_precision_string_t use gelu_m, only : gelu_t - use relu_m, only : relu_t + use kind_parameters_m, only : double_precision use layer_m, only : layer_t use neuron_m, only : neuron_t + use relu_m, only : relu_t + use step_m, only : step_t + use swish_m, only : swish_t + use sigmoid_m, only : sigmoid_t implicit none interface assert_consistency @@ -547,7 +549,7 @@ impure function activation_factory_method(activation_name) result(activation) type(layer_t) hidden_layers, output_layer lines = file_%lines() - call assert(adjustl(lines(1)%string())=="{", "inference_engine_s(from_json): expected outermost object '{'") + call assert(adjustl(lines(1)%string())=="{", "inference_engine_s(default_real_from_json): expected outermost object '{'") check_git_tag: & block @@ -556,7 +558,7 @@ impure function activation_factory_method(activation_name) result(activation) tag = lines(2)%get_json_value("acceptable_engine_tag", mold="") call assert( & tag == acceptable_engine_tag & - ,"inference_engine_s(from_json): acceptable_engine_tag" & + ,"inference_engine_s(default_real_from_json): acceptable_engine_tag" & ,tag //"(expected " //acceptable_engine_tag // ")" & ) end block check_git_tag @@ -573,7 +575,7 @@ impure function activation_factory_method(activation_name) result(activation) if (justified_line == '"inputs_map": {') exit end do find_inputs_map - call assert(justified_line =='"inputs_map": {', 'from_json: expecting "inputs_map": {', justified_line) + call assert(justified_line =='"inputs_map": {', 'default_real_from_json: expecting "inputs_map": {', justified_line) input_map = tensor_map_t(lines(l:l+num_map_lines-1)) find_outputs_map: & @@ -582,7 +584,7 @@ impure function activation_factory_method(activation_name) result(activation) if (justified_line == '"outputs_map": {') exit end do find_outputs_map - call assert(justified_line =='"outputs_map": {', 'from_json: expecting "outputs_map": {', justified_line) + call assert(justified_line =='"outputs_map": {', 'default_real_from_json: expecting "outputs_map": {', justified_line) output_map = tensor_map_t(lines(l:l+num_map_lines-1)) end associate @@ -593,7 +595,7 @@ impure function activation_factory_method(activation_name) result(activation) justified_line = adjustl(lines(l)%string()) if (justified_line == '"hidden_layers": [') exit end do find_hidden_layers - call assert(justified_line=='"hidden_layers": [', 'from_json: expecting "hidden_layers": [', justified_line) + call assert(justified_line=='"hidden_layers": [', 'default_real_from_json: expecting "hidden_layers": [', justified_line) read_hidden_layers: & block @@ -609,7 +611,7 @@ impure function activation_factory_method(activation_name) result(activation) output_layer_line = lines(output_layer_line_number)%string() - call assert(adjustl(output_layer_line)=='"output_layer": [', 'from_json: expecting "output_layer": [', & + call assert(adjustl(output_layer_line)=='"output_layer": [', 'default_real_from_json: expecting "output_layer": [', & lines(output_layer_line_number)%string()) output_layer = layer_t(lines, start=output_layer_line_number) @@ -622,7 +624,7 @@ impure function activation_factory_method(activation_name) result(activation) justified_line = adjustl(lines(l)%string()) if (justified_line == '"metadata": {') exit end do find_metadata - call assert(justified_line=='"metadata": {', 'from_json: expecting "metadata": {', justified_line) + call assert(justified_line=='"metadata": {', 'default_real_from_json: expecting "metadata": {', justified_line) read_metadata: & associate(proto_meta => metadata_t(string_t(""),string_t(""),string_t(""),string_t(""),string_t(""))) @@ -641,6 +643,107 @@ impure function activation_factory_method(activation_name) result(activation) end procedure default_real_from_json + module procedure double_precision_from_json + + character(len=:), allocatable :: justified_line + integer l, num_file_lines + type(double_precision_string_t), allocatable :: lines(:) + type(tensor_map_t(double_precision)) input_map, output_map + type(layer_t(double_precision)) hidden_layers, output_layer + + lines = file%double_precision_lines() + call assert(adjustl(lines(1)%string())=="{", "inference_engine_s(double_precision_from_json): expected outermost object '{'") + + check_git_tag: & + block + character(len=:), allocatable :: tag + + tag = lines(2)%get_json_value("acceptable_engine_tag", mold="") + call assert( & + tag == acceptable_engine_tag & + ,"inference_engine_s(double_precision_from_json): acceptable_engine_tag" & + ,tag //"(expected " //acceptable_engine_tag // ")" & + ) + end block check_git_tag + + num_file_lines = size(lines) + + read_tensor_maps: & + associate(proto_map => tensor_map_t("",[0D0],[1D0])) + associate(num_map_lines => size(proto_map%to_json())) + + find_inputs_map: & + do l = 1, num_file_lines + justified_line = adjustl(lines(l)%string()) + if (justified_line == '"inputs_map": {') exit + end do find_inputs_map + + call assert(justified_line =='"inputs_map": {', 'double_precision_from_json: expecting "inputs_map": {', justified_line) + input_map = tensor_map_t(lines(l:l+num_map_lines-1)) + + find_outputs_map: & + do l = 1, num_file_lines + justified_line = adjustl(lines(l)%string()) + if (justified_line == '"outputs_map": {') exit + end do find_outputs_map + + call assert(justified_line =='"outputs_map": {', 'double_precision_from_json: expecting "outputs_map": {', justified_line) + output_map = tensor_map_t(lines(l:l+num_map_lines-1)) + + end associate + end associate read_tensor_maps + + find_hidden_layers: & + do l = 1, num_file_lines + justified_line = adjustl(lines(l)%string()) + if (justified_line == '"hidden_layers": [') exit + end do find_hidden_layers + call assert(justified_line=='"hidden_layers": [', 'double_precision_from_json: expecting "hidden_layers": [', justified_line) + + read_hidden_layers: & + block + integer, parameter :: bracket_lines_per_layer=2 + character(len=:), allocatable :: output_layer_line + + hidden_layers = layer_t(lines, start=l+1) + + read_layers_of_neurons: & + associate(proto_neuron => neuron_t(weights=[0D0], bias=0D0)) + associate(output_layer_line_number => l + 1 + size(proto_neuron%to_json())*sum(hidden_layers%count_neurons()) & + + bracket_lines_per_layer*hidden_layers%count_layers() + 1) + + output_layer_line = lines(output_layer_line_number)%string() + + call assert(adjustl(output_layer_line)=='"output_layer": [', 'double_precision_from_json: expecting "output_layer": [', & + lines(output_layer_line_number)%string()) + + output_layer = layer_t(lines, start=output_layer_line_number) + end associate + end associate read_layers_of_neurons + end block read_hidden_layers + + find_metadata: & + do l = 1, num_file_lines + justified_line = adjustl(lines(l)%string()) + if (justified_line == '"metadata": {') exit + end do find_metadata + call assert(justified_line=='"metadata": {', 'double_precision_from_json: expecting "metadata": {', justified_line) + + read_metadata: & + associate(proto_meta => metadata_t(string_t(""),string_t(""),string_t(""),string_t(""),string_t(""))) + associate(metadata => metadata_t(lines(l : l + size(proto_meta%to_json()) - 1))) + inference_engine = hidden_layers%inference_engine(metadata, output_layer, input_map, output_map) + if (allocated(inference_engine%activation_strategy_)) deallocate(inference_engine%activation_strategy_) + associate(function_name => metadata%activation_name()) + allocate(inference_engine%activation_strategy_, source = activation_factory_method(function_name%string())) + end associate + end associate + end associate read_metadata + + call assert_consistency(inference_engine) + + end procedure double_precision_from_json + module procedure default_real_assert_conformable_with call assert_consistency(self) diff --git a/src/inference_engine/layer_m.f90 b/src/inference_engine/layer_m.f90 index 47065eef1..cc418e8d6 100644 --- a/src/inference_engine/layer_m.f90 +++ b/src/inference_engine/layer_m.f90 @@ -1,12 +1,13 @@ ! Copyright (c), The Regents of the University of California ! Terms of use are as specified in LICENSE.txt module layer_m - use neuron_m, only : neuron_t + use double_precision_string_m, only : double_precision_string_t + use kind_parameters_m, only : default_real, double_precision use julienne_string_m, only : string_t use inference_engine_m_, only : inference_engine_t - use tensor_map_m, only : tensor_map_t - use kind_parameters_m, only : default_real, double_precision - use double_precision_string_m, only : double_precision_string_t + use metadata_m, only : metadata_t + use neuron_m, only : neuron_t + use tensor_map_m, only : tensor_map_t implicit none private @@ -18,8 +19,8 @@ module layer_m type(neuron_t(k)), private :: neuron !! linked list of this layer's neurons type(layer_t(k)), allocatable, private :: next !! next layer contains - generic :: inference_engine => default_real_inference_engine - procedure, private :: default_real_inference_engine + generic :: inference_engine => default_real_inference_engine, double_precision_inference_engine + procedure, private :: default_real_inference_engine, double_precision_inference_engine generic :: count_layers => default_real_count_layers, double_precision_count_layers procedure, private :: default_real_count_layers, double_precision_count_layers generic :: count_neurons => default_real_count_neurons, double_precision_count_neurons @@ -65,6 +66,15 @@ module function default_real_inference_engine(hidden_layers, metadata, output_la type(inference_engine_t) inference_engine_ end function + module function double_precision_inference_engine(hidden_layers, metadata, output_layer, input_map, output_map) result(inference_engine_) + implicit none + class(layer_t(double_precision)), intent(in), target :: hidden_layers + type(layer_t(double_precision)), intent(in), target :: output_layer + type(metadata_t), intent(in) :: metadata + type(tensor_map_t(double_precision)), intent(in) :: input_map, output_map + type(inference_engine_t(double_precision)) inference_engine_ + end function + module function default_real_count_layers(layer) result(num_layers) implicit none class(layer_t), intent(in), target :: layer diff --git a/src/inference_engine/layer_s.f90 b/src/inference_engine/layer_s.f90 index 1fdb858f9..231514e2f 100644 --- a/src/inference_engine/layer_s.f90 +++ b/src/inference_engine/layer_s.f90 @@ -138,7 +138,79 @@ end associate end associate - end procedure + end procedure default_real_inference_engine + + module procedure double_precision_inference_engine + + associate( & + num_inputs => hidden_layers%count_inputs(), & + num_outputs => output_layer%count_neurons(), & + neurons_per_hidden_layer => hidden_layers%count_neurons(), & + num_hidden_layers => hidden_layers%count_layers(), & + num_output_layers => output_layer%count_layers() & + ) + call assert(num_output_layers==1, "inference_engine_s(double_precision_inference_engine): 1 output layer", num_output_layers) + + associate(nodes => [num_inputs, neurons_per_hidden_layer, num_outputs]) + associate(n_max => maxval(nodes)) + block + double precision, allocatable :: weights(:,:,:), biases(:,:) + type(layer_t(double_precision)), pointer :: layer_ptr + type(neuron_t(double_precision)), pointer :: neuron_ptr + integer j, l + + allocate(weights(n_max, n_max, num_hidden_layers + num_output_layers)) + allocate(biases(n_max, num_hidden_layers + num_output_layers)) + + layer_ptr => hidden_layers + l = 0 + loop_over_hidden_Layers: & + do + l = l + 1 + neuron_ptr => layer_ptr%neuron + j = 0 + loop_over_hidden_neurons: & + do + j = j + 1 + associate(w => neuron_ptr%weights()) + weights(j,1:size(w,1),l) = w + end associate + biases(j,l) = neuron_ptr%bias() + + if (.not. neuron_ptr%next_allocated()) exit + neuron_ptr => neuron_ptr%next_pointer() + + end do loop_over_hidden_neurons + + if (.not. allocated(layer_ptr%next)) exit + layer_ptr => layer_ptr%next_pointer() + + end do loop_over_hidden_Layers + + layer_ptr => output_layer + l = l + 1 + neuron_ptr => layer_ptr%neuron + j = 0 + loop_over_output_neurons: & + do + j = j + 1 + associate(w => neuron_ptr%weights()) + weights(j,1:size(w,1),l) = w + end associate + biases(j,l) = neuron_ptr%bias() + + if (.not. neuron_ptr%next_allocated()) exit + neuron_ptr => neuron_ptr%next_pointer() + + end do loop_over_output_neurons + + inference_engine_ = inference_engine_t(metadata, weights, biases, nodes, input_map, output_map) + end block + end associate + end associate + end associate + + end procedure double_precision_inference_engine module procedure default_real_count_layers diff --git a/src/inference_engine_m.f90 b/src/inference_engine_m.f90 index 5f961b85b..06ec6d98f 100644 --- a/src/inference_engine_m.f90 +++ b/src/inference_engine_m.f90 @@ -4,6 +4,7 @@ module inference_engine_m !! Specify the user-facing modules, derived types, and type parameters use activation_strategy_m, only : activation_strategy_t use differentiable_activation_strategy_m, only : differentiable_activation_strategy_t + use double_precision_file_m, only : double_precision_file_t use double_precision_string_m, only : double_precision_string_t use gelu_m, only : gelu_t use hyperparameters_m, only : hyperparameters_t From b8468bce231f290496442ccf55290ae5e69e04fb Mon Sep 17 00:00:00 2001 From: Katherine Rasmussen Date: Tue, 3 Sep 2024 14:23:54 -0700 Subject: [PATCH 046/105] Add new Github CI actions yaml file to build with flang --- .github/workflows/build-with-flang.yml | 32 ++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 .github/workflows/build-with-flang.yml diff --git a/.github/workflows/build-with-flang.yml b/.github/workflows/build-with-flang.yml new file mode 100644 index 000000000..7bd2849dc --- /dev/null +++ b/.github/workflows/build-with-flang.yml @@ -0,0 +1,32 @@ +name: Build with LLVM-Flang + +on: [push, pull_request] + + +jobs: + Build: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-24.04] + fail-fast: true + container: gmao/llvm-flang:latest + env: + FC: flang-new + CC: clang + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - uses: fortran-lang/setup-fpm@v4 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and Test + if: contains(matrix.os, 'ubuntu') + run: | + fpm --version + export FPM_FC=$FC + export FPM_CC=$CC + fpm test --flag "-mmlir -allow-assumed-rank -O3" From 71860db1330b3b9071b36c6e7efdd7edbf7d8fda Mon Sep 17 00:00:00 2001 From: Katherine Rasmussen Date: Tue, 3 Sep 2024 14:27:26 -0700 Subject: [PATCH 047/105] Replace CI yaml file that builds with gfortran with new CI yaml file that builds with Flang --- .github/workflows/CI.yml | 35 +++++++------------------- .github/workflows/build-with-flang.yml | 32 ----------------------- 2 files changed, 9 insertions(+), 58 deletions(-) delete mode 100644 .github/workflows/build-with-flang.yml diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index cabff7e84..ac2e16395 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -1,4 +1,4 @@ -name: CI +name: Build with LLVM Flang on: [push, pull_request] @@ -8,11 +8,12 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [macOS-12, ubuntu-24.04] + os: [ubuntu-24.04] fail-fast: true + container: gmao/llvm-flang:latest env: - FC: gfortran - GCC_V: 14 + FC: flang-new + CC: clang steps: - name: Checkout code @@ -22,28 +23,10 @@ jobs: with: github-token: ${{ secrets.GITHUB_TOKEN }} - - name: Install Dependencies MacOS - if: contains(matrix.os, 'mac') - run: | - brew install gcc@${GCC_V} - sudo ln -s $(which gfortran-${GCC_V}) $(dirname $(which gfortran-${GCC_V}))/gfortran - - - name: Install on Ubuntu - if: contains(matrix.os, 'ubuntu') - run: | - sudo apt update - sudo apt install -y build-essential gfortran-14 gcc-14 g++-14 - - - name: Build and Test MacOS - if: contains(matrix.os, 'mac') - run: | - export PATH="${HOME}/.local/bin:$PATH" - ./setup.sh - - - name: Build and Test Ubuntu + - name: Build and Test with LLVM Flang if: contains(matrix.os, 'ubuntu') run: | fpm --version - export FPM_FC=gfortran-14 - export FPM_CC=gcc-14 - fpm test + export FPM_FC=$FC + export FPM_CC=$CC + fpm test --flag "-mmlir -allow-assumed-rank -O3" diff --git a/.github/workflows/build-with-flang.yml b/.github/workflows/build-with-flang.yml deleted file mode 100644 index 7bd2849dc..000000000 --- a/.github/workflows/build-with-flang.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: Build with LLVM-Flang - -on: [push, pull_request] - - -jobs: - Build: - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ubuntu-24.04] - fail-fast: true - container: gmao/llvm-flang:latest - env: - FC: flang-new - CC: clang - - steps: - - name: Checkout code - uses: actions/checkout@v2 - - - uses: fortran-lang/setup-fpm@v4 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - - - name: Build and Test - if: contains(matrix.os, 'ubuntu') - run: | - fpm --version - export FPM_FC=$FC - export FPM_CC=$CC - fpm test --flag "-mmlir -allow-assumed-rank -O3" From d134548d02b53a8db4bb7baf43f22edd3173ebfe Mon Sep 17 00:00:00 2001 From: Katherine Rasmussen Date: Tue, 3 Sep 2024 15:00:54 -0700 Subject: [PATCH 048/105] Update .github/workflows/CI.yml Co-authored-by: Damian Rouson --- .github/workflows/CI.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index ac2e16395..27dbc968b 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -27,6 +27,7 @@ jobs: if: contains(matrix.os, 'ubuntu') run: | fpm --version + flang-new --version export FPM_FC=$FC export FPM_CC=$CC fpm test --flag "-mmlir -allow-assumed-rank -O3" From 0221ee08dd78ce95a33d343a5f6740cf9d320dcb Mon Sep 17 00:00:00 2001 From: Katherine Rasmussen Date: Tue, 3 Sep 2024 15:08:01 -0700 Subject: [PATCH 049/105] Add version check to CI --- .github/workflows/CI.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 27dbc968b..021833e93 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -27,7 +27,8 @@ jobs: if: contains(matrix.os, 'ubuntu') run: | fpm --version - flang-new --version + $FC --version + $CC --version export FPM_FC=$FC export FPM_CC=$CC fpm test --flag "-mmlir -allow-assumed-rank -O3" From 867ae6fe3faa1a66957068eb60927f476e6cf804 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Wed, 4 Sep 2024 12:29:17 -0700 Subject: [PATCH 050/105] refac(inference_engine_s): edit Cray workaround --- src/inference_engine/inference_engine_s.F90 | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/inference_engine/inference_engine_s.F90 b/src/inference_engine/inference_engine_s.F90 index 4bd3d6cd5..17005ba3f 100644 --- a/src/inference_engine/inference_engine_s.F90 +++ b/src/inference_engine/inference_engine_s.F90 @@ -93,18 +93,16 @@ end associate end do feed_forward -#ifdef _CRAYFTN +#ifndef _CRAYFTN + associate(normalized_outputs => tensor_t(a(1:n(output_layer), output_layer))) + outputs = self%output_map_%map_from_training_range(normalized_outputs) + end associate +#else block type(tensor_t) :: normalized_outputs normalized_outputs = tensor_t(a(1:n(output_layer), output_layer)) -#else - associate(normalized_outputs => tensor_t(a(1:n(output_layer), output_layer))) -#endif outputs = self%output_map_%map_from_training_range(normalized_outputs) -#ifdef _CRAYFTN end block -#else - end associate #endif end associate From eb4e067766712276681dc81e6f07ca077b48c206 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Wed, 4 Sep 2024 14:09:48 -0700 Subject: [PATCH 051/105] feat(pdt): merge infer_a proc from aerosol branch --- src/inference_engine/inference_engine_m_.f90 | 20 ++++++ src/inference_engine/inference_engine_s.F90 | 66 +++++++++++++++++++- 2 files changed, 84 insertions(+), 2 deletions(-) diff --git a/src/inference_engine/inference_engine_m_.f90 b/src/inference_engine/inference_engine_m_.f90 index 136a143aa..1a763cd9d 100644 --- a/src/inference_engine/inference_engine_m_.f90 +++ b/src/inference_engine/inference_engine_m_.f90 @@ -52,6 +52,12 @@ module inference_engine_m_ procedure, private :: default_real_to_exchange, double_precision_to_exchange end type + type, extends(inference_engine_t) :: unnormalized_engine_t + contains + generic :: infer => default_real_infer_unnormalized, double_precision_infer_unnormalized + procedure, private :: default_real_infer_unnormalized, double_precision_infer_unnormalized + end type + type exchange_t(k) integer, kind :: k = default_real type(tensor_map_t(k)) input_map_, output_map_ @@ -188,6 +194,20 @@ elemental module function default_real_infer(self, inputs) result(outputs) type(tensor_t) outputs end function + elemental module function default_real_infer_unnormalized(self, inputs) result(outputs) + implicit none + class(unnormalized_engine_t), intent(in) :: self + type(tensor_t), intent(in) :: inputs + type(tensor_t) outputs + end function + + elemental module function double_precision_infer_unnormalized(self, inputs) result(outputs) + implicit none + class(unnormalized_engine_t(double_precision)), intent(in) :: self + type(tensor_t(double_precision)), intent(in) :: inputs + type(tensor_t(double_precision)) outputs + end function + elemental module function double_precision_infer(self, inputs) result(outputs) implicit none class(inference_engine_t(double_precision)), intent(in) :: self diff --git a/src/inference_engine/inference_engine_s.F90 b/src/inference_engine/inference_engine_s.F90 index 17005ba3f..f94327dcc 100644 --- a/src/inference_engine/inference_engine_s.F90 +++ b/src/inference_engine/inference_engine_s.F90 @@ -62,6 +62,68 @@ exchange%activation_strategy_ = self%activation_strategy_ end procedure + module procedure default_real_infer_unnormalized + + real, allocatable :: a(:,:) + integer, parameter :: input_layer = 0 + integer l + + call assert_consistency(self) + + associate(w => self%weights_, b => self%biases_, n => self%nodes_, output_layer => ubound(self%nodes_,1)) + + allocate(a(maxval(n), input_layer:output_layer)) + + a(1:n(input_layer),input_layer) = inputs%values() + + feed_forward: & + do l = input_layer+1, output_layer + associate(z => matmul(w(1:n(l),1:n(l-1),l), a(1:n(l-1),l-1)) + b(1:n(l),l)) + if (l .lt. output_layer) then + a(1:n(l),l) = self%activation_strategy_%activation(z) + else + a(1:n(l),l) = z(1:n(l)) + end if + end associate + end do feed_forward + + outputs = tensor_t(a(1:n(output_layer), output_layer)) + + end associate + + end procedure + + module procedure double_precision_infer_unnormalized + + double precision, allocatable :: a(:,:) + integer, parameter :: input_layer = 0 + integer l + + call assert_consistency(self) + + associate(w => self%weights_, b => self%biases_, n => self%nodes_, output_layer => ubound(self%nodes_,1)) + + allocate(a(maxval(n), input_layer:output_layer)) + + a(1:n(input_layer),input_layer) = inputs%values() + + feed_forward: & + do l = input_layer+1, output_layer + associate(z => matmul(w(1:n(l),1:n(l-1),l), a(1:n(l-1),l-1)) + b(1:n(l),l)) + if (l .lt. output_layer) then + a(1:n(l),l) = self%activation_strategy_%activation(z) + else + a(1:n(l),l) = z(1:n(l)) + end if + end associate + end do feed_forward + + outputs = tensor_t(a(1:n(output_layer), output_layer)) + + end associate + + end procedure + module procedure default_real_infer real, allocatable :: a(:,:) @@ -160,7 +222,7 @@ pure subroutine default_real_consistency(self) - type(inference_engine_t), intent(in) :: self + class(inference_engine_t), intent(in) :: self integer, parameter :: input_layer=0 @@ -185,7 +247,7 @@ pure subroutine default_real_consistency(self) pure subroutine double_precision_consistency(self) - type(inference_engine_t(double_precision)), intent(in) :: self + class(inference_engine_t(double_precision)), intent(in) :: self integer, parameter :: input_layer=0 From c900539baecac1e891c3cf429f409773e64d41fc Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Thu, 5 Sep 2024 15:58:43 -0700 Subject: [PATCH 052/105] refac(inference_engine): unnormalized -> unmapped --- src/inference_engine/inference_engine_m_.f90 | 18 +++++++++--------- src/inference_engine/inference_engine_s.F90 | 4 ++-- src/inference_engine_m.f90 | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/inference_engine/inference_engine_m_.f90 b/src/inference_engine/inference_engine_m_.f90 index 1a763cd9d..df9d6dee9 100644 --- a/src/inference_engine/inference_engine_m_.f90 +++ b/src/inference_engine/inference_engine_m_.f90 @@ -52,10 +52,10 @@ module inference_engine_m_ procedure, private :: default_real_to_exchange, double_precision_to_exchange end type - type, extends(inference_engine_t) :: unnormalized_engine_t + type, extends(inference_engine_t) :: unmapped_engine_t contains - generic :: infer => default_real_infer_unnormalized, double_precision_infer_unnormalized - procedure, private :: default_real_infer_unnormalized, double_precision_infer_unnormalized + generic :: infer => default_real_infer_unmapped, double_precision_infer_unmapped + procedure, private :: default_real_infer_unmapped, double_precision_infer_unmapped end type type exchange_t(k) @@ -136,7 +136,7 @@ elemental module function double_precision_map_to_input_range(self, tensor) resu end function elemental module function default_real_map_from_output_range(self, normalized_tensor) result(tensor) - !! The result contains the output tensor values unnormalized via the inverse of the mapping used in training + !! The result contains the output tensor values unmapped via the inverse of the mapping used in training implicit none class(inference_engine_t), intent(in) :: self type(tensor_t), intent(in) :: normalized_tensor @@ -144,7 +144,7 @@ elemental module function default_real_map_from_output_range(self, normalized_te end function elemental module function double_precision_map_from_output_range(self, normalized_tensor) result(tensor) - !! The result contains the output tensor values unnormalized via the inverse of the mapping used in training + !! The result contains the output tensor values unmapped via the inverse of the mapping used in training implicit none class(inference_engine_t(double_precision)), intent(in) :: self type(tensor_t(double_precision)), intent(in) :: normalized_tensor @@ -194,16 +194,16 @@ elemental module function default_real_infer(self, inputs) result(outputs) type(tensor_t) outputs end function - elemental module function default_real_infer_unnormalized(self, inputs) result(outputs) + elemental module function default_real_infer_unmapped(self, inputs) result(outputs) implicit none - class(unnormalized_engine_t), intent(in) :: self + class(unmapped_engine_t), intent(in) :: self type(tensor_t), intent(in) :: inputs type(tensor_t) outputs end function - elemental module function double_precision_infer_unnormalized(self, inputs) result(outputs) + elemental module function double_precision_infer_unmapped(self, inputs) result(outputs) implicit none - class(unnormalized_engine_t(double_precision)), intent(in) :: self + class(unmapped_engine_t(double_precision)), intent(in) :: self type(tensor_t(double_precision)), intent(in) :: inputs type(tensor_t(double_precision)) outputs end function diff --git a/src/inference_engine/inference_engine_s.F90 b/src/inference_engine/inference_engine_s.F90 index f94327dcc..e19c3701c 100644 --- a/src/inference_engine/inference_engine_s.F90 +++ b/src/inference_engine/inference_engine_s.F90 @@ -62,7 +62,7 @@ exchange%activation_strategy_ = self%activation_strategy_ end procedure - module procedure default_real_infer_unnormalized + module procedure default_real_infer_unmapped real, allocatable :: a(:,:) integer, parameter :: input_layer = 0 @@ -93,7 +93,7 @@ end procedure - module procedure double_precision_infer_unnormalized + module procedure double_precision_infer_unmapped double precision, allocatable :: a(:,:) integer, parameter :: input_layer = 0 diff --git a/src/inference_engine_m.f90 b/src/inference_engine_m.f90 index 06ec6d98f..2b9c2a040 100644 --- a/src/inference_engine_m.f90 +++ b/src/inference_engine_m.f90 @@ -9,7 +9,7 @@ module inference_engine_m use gelu_m, only : gelu_t use hyperparameters_m, only : hyperparameters_t use input_output_pair_m, only : input_output_pair_t, shuffle, write_to_stdout - use inference_engine_m_, only : inference_engine_t + use inference_engine_m_, only : inference_engine_t, unmapped_engine_t use kind_parameters_m, only : default_real, double_precision use metadata_m, only : metadata_t use mini_batch_m, only : mini_batch_t From d06f14f7c05bd89fa401062ed68d44f208072b74 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Thu, 5 Sep 2024 16:34:42 -0700 Subject: [PATCH 053/105] fix(unmapped_engine_t): make public --- src/inference_engine/inference_engine_m_.f90 | 1 + 1 file changed, 1 insertion(+) diff --git a/src/inference_engine/inference_engine_m_.f90 b/src/inference_engine/inference_engine_m_.f90 index df9d6dee9..ead1eed60 100644 --- a/src/inference_engine/inference_engine_m_.f90 +++ b/src/inference_engine/inference_engine_m_.f90 @@ -13,6 +13,7 @@ module inference_engine_m_ private public :: inference_engine_t + public :: unmapped_engine_t public :: exchange_t type inference_engine_t(k) From 80b9e23f0f1c55f44b132b25c3113b03dc65811e Mon Sep 17 00:00:00 2001 From: Katherine Rasmussen Date: Thu, 5 Sep 2024 16:27:36 -0700 Subject: [PATCH 054/105] Support building cloud-microphysics with LLVM Flang --- cloud-microphysics/fpm.toml | 2 +- cloud-microphysics/setup.sh | 9 ++++++++- cloud-microphysics/src/phase_space_bin_m.f90 | 6 +++++- cloud-microphysics/test/main.f90 | 7 +++++++ 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/cloud-microphysics/fpm.toml b/cloud-microphysics/fpm.toml index 4e76ace76..da9e0206b 100644 --- a/cloud-microphysics/fpm.toml +++ b/cloud-microphysics/fpm.toml @@ -5,6 +5,6 @@ maintainer = "(Please see inference-engine/fpm.toml.)" [dependencies] assert = {git = "https://github.com/sourceryinstitute/assert", tag = "1.7.0"} -julienne = {git = "https://github.com/berkeleylab/julienne", tag = "1.1.1"} +julienne = {git = "https://github.com/berkeleylab/julienne", tag = "1.2.2"} 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 index 724282fab..a2d130d82 100755 --- a/cloud-microphysics/setup.sh +++ b/cloud-microphysics/setup.sh @@ -63,10 +63,17 @@ 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-14"} FPM_CC=${CC:-"gcc-14"} +if [ $FPM_FC = "gfortran-14" ]; then + FPM_FLAG="-fcoarray=single -O3 -fallow-argument-mismatch -ffree-line-length-none -L$NETCDF_LIB_PATH -L$HDF5_LIB_PATH" +elif [ $FPM_FC = "flang-new" ]; then + FPM_FLAG="-mmlir -allow-assumed-rank -O3 -L$NETCDF_LIB_PATH -L$HDF5_LIB_PATH" +else + FPM_FLAG="" +fi + mkdir -p build CI=${CI:-"false"} # GitHub Actions workflows set CI=true diff --git a/cloud-microphysics/src/phase_space_bin_m.f90 b/cloud-microphysics/src/phase_space_bin_m.f90 index f9ca94a24..d48655599 100644 --- a/cloud-microphysics/src/phase_space_bin_m.f90 +++ b/cloud-microphysics/src/phase_space_bin_m.f90 @@ -20,6 +20,10 @@ pure module function bin(tensor, minima, maxima, num_bins) result(phase_space_bi end function end interface +end module phase_space_bin_m + +submodule (phase_space_bin_m) phase_space_bin_s + implicit none contains @@ -35,4 +39,4 @@ pure module function bin(tensor, minima, maxima, num_bins) result(phase_space_bi end procedure -end module phase_space_bin_m +end submodule diff --git a/cloud-microphysics/test/main.f90 b/cloud-microphysics/test/main.f90 index c0c5685e6..778d90443 100644 --- a/cloud-microphysics/test/main.f90 +++ b/cloud-microphysics/test/main.f90 @@ -1,5 +1,10 @@ ! Copyright (c), The Regents of the University of California ! Terms of use are as specified in LICENSE.txt + +#if defined(__flang__) +# define SINGLE_IMAGE_ONLY +#endif + program main use netCDF_file_test_m, only : netCDF_file_test_t implicit none @@ -19,7 +24,9 @@ program main print *,"Test suite execution time: ",t_finish - t_start print * print '(*(a,:,g0))',"_________ In total, ",passes," of ",tests, " tests pass. _________" +#ifndef SINGLE_IMAGE_ONLY sync all +#endif print * if (passes/=tests) error stop "-------- One or more tests failed. See the above report. ---------" end program From 27be2039ec78f8df028822cb1cf51293e0472466 Mon Sep 17 00:00:00 2001 From: Katherine Rasmussen Date: Thu, 5 Sep 2024 18:07:01 -0700 Subject: [PATCH 055/105] Improve setup script changes in cloud microphysics --- cloud-microphysics/setup.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cloud-microphysics/setup.sh b/cloud-microphysics/setup.sh index a2d130d82..49686bf1c 100755 --- a/cloud-microphysics/setup.sh +++ b/cloud-microphysics/setup.sh @@ -66,9 +66,10 @@ FPM_LD_FLAG=" -L$NETCDF_LIB_PATH -L$HDF5_LIB_PATH -L$NETCDFF_LIB_PATH" FPM_FC=${FC:-"gfortran-14"} FPM_CC=${CC:-"gcc-14"} -if [ $FPM_FC = "gfortran-14" ]; then +FPM_FC_NAME_ONLY=${FPM_FC##*/} +if [ $FPM_FC_NAME_ONLY} = "gfortran-14" ]; then FPM_FLAG="-fcoarray=single -O3 -fallow-argument-mismatch -ffree-line-length-none -L$NETCDF_LIB_PATH -L$HDF5_LIB_PATH" -elif [ $FPM_FC = "flang-new" ]; then +elif [ $FPM_FC_NAME_ONLY = "flang-new" ]; then FPM_FLAG="-mmlir -allow-assumed-rank -O3 -L$NETCDF_LIB_PATH -L$HDF5_LIB_PATH" else FPM_FLAG="" From 1534ee2c1add4651faeffd3677d94b6bdf5ed82c Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Thu, 12 Sep 2024 16:34:19 -0700 Subject: [PATCH 056/105] chore: git mv cloud-microphysics/ demo/ --- {cloud-microphysics => demo}/app/plot-normalized-histograms.gnu | 0 {cloud-microphysics => demo}/app/plot-raw-histograms.gnu | 0 {cloud-microphysics => demo}/app/tensor-statistics.f90 | 0 {cloud-microphysics => demo}/app/train-cloud-microphysics.f90 | 0 {cloud-microphysics => demo}/fpm.toml | 0 {cloud-microphysics => demo}/setup.sh | 0 {cloud-microphysics => demo}/src/NetCDF_file_m.f90 | 0 {cloud-microphysics => demo}/src/NetCDF_file_s.f90 | 0 {cloud-microphysics => demo}/src/histogram_m.f90 | 0 {cloud-microphysics => demo}/src/histogram_s.f90 | 0 {cloud-microphysics => demo}/src/phase_space_bin_m.f90 | 0 {cloud-microphysics => demo}/src/run-fpm.sh-header | 0 {cloud-microphysics => demo}/test/main.f90 | 0 {cloud-microphysics => demo}/test/netCDF_file_test_m.f90 | 0 {cloud-microphysics => demo}/train.sh | 0 {cloud-microphysics => demo}/training_configuration.json | 0 16 files changed, 0 insertions(+), 0 deletions(-) rename {cloud-microphysics => demo}/app/plot-normalized-histograms.gnu (100%) rename {cloud-microphysics => demo}/app/plot-raw-histograms.gnu (100%) rename {cloud-microphysics => demo}/app/tensor-statistics.f90 (100%) rename {cloud-microphysics => demo}/app/train-cloud-microphysics.f90 (100%) rename {cloud-microphysics => demo}/fpm.toml (100%) rename {cloud-microphysics => demo}/setup.sh (100%) rename {cloud-microphysics => demo}/src/NetCDF_file_m.f90 (100%) rename {cloud-microphysics => demo}/src/NetCDF_file_s.f90 (100%) rename {cloud-microphysics => demo}/src/histogram_m.f90 (100%) rename {cloud-microphysics => demo}/src/histogram_s.f90 (100%) rename {cloud-microphysics => demo}/src/phase_space_bin_m.f90 (100%) rename {cloud-microphysics => demo}/src/run-fpm.sh-header (100%) rename {cloud-microphysics => demo}/test/main.f90 (100%) rename {cloud-microphysics => demo}/test/netCDF_file_test_m.f90 (100%) rename {cloud-microphysics => demo}/train.sh (100%) rename {cloud-microphysics => demo}/training_configuration.json (100%) diff --git a/cloud-microphysics/app/plot-normalized-histograms.gnu b/demo/app/plot-normalized-histograms.gnu similarity index 100% rename from cloud-microphysics/app/plot-normalized-histograms.gnu rename to demo/app/plot-normalized-histograms.gnu diff --git a/cloud-microphysics/app/plot-raw-histograms.gnu b/demo/app/plot-raw-histograms.gnu similarity index 100% rename from cloud-microphysics/app/plot-raw-histograms.gnu rename to demo/app/plot-raw-histograms.gnu diff --git a/cloud-microphysics/app/tensor-statistics.f90 b/demo/app/tensor-statistics.f90 similarity index 100% rename from cloud-microphysics/app/tensor-statistics.f90 rename to demo/app/tensor-statistics.f90 diff --git a/cloud-microphysics/app/train-cloud-microphysics.f90 b/demo/app/train-cloud-microphysics.f90 similarity index 100% rename from cloud-microphysics/app/train-cloud-microphysics.f90 rename to demo/app/train-cloud-microphysics.f90 diff --git a/cloud-microphysics/fpm.toml b/demo/fpm.toml similarity index 100% rename from cloud-microphysics/fpm.toml rename to demo/fpm.toml diff --git a/cloud-microphysics/setup.sh b/demo/setup.sh similarity index 100% rename from cloud-microphysics/setup.sh rename to demo/setup.sh diff --git a/cloud-microphysics/src/NetCDF_file_m.f90 b/demo/src/NetCDF_file_m.f90 similarity index 100% rename from cloud-microphysics/src/NetCDF_file_m.f90 rename to demo/src/NetCDF_file_m.f90 diff --git a/cloud-microphysics/src/NetCDF_file_s.f90 b/demo/src/NetCDF_file_s.f90 similarity index 100% rename from cloud-microphysics/src/NetCDF_file_s.f90 rename to demo/src/NetCDF_file_s.f90 diff --git a/cloud-microphysics/src/histogram_m.f90 b/demo/src/histogram_m.f90 similarity index 100% rename from cloud-microphysics/src/histogram_m.f90 rename to demo/src/histogram_m.f90 diff --git a/cloud-microphysics/src/histogram_s.f90 b/demo/src/histogram_s.f90 similarity index 100% rename from cloud-microphysics/src/histogram_s.f90 rename to demo/src/histogram_s.f90 diff --git a/cloud-microphysics/src/phase_space_bin_m.f90 b/demo/src/phase_space_bin_m.f90 similarity index 100% rename from cloud-microphysics/src/phase_space_bin_m.f90 rename to demo/src/phase_space_bin_m.f90 diff --git a/cloud-microphysics/src/run-fpm.sh-header b/demo/src/run-fpm.sh-header similarity index 100% rename from cloud-microphysics/src/run-fpm.sh-header rename to demo/src/run-fpm.sh-header diff --git a/cloud-microphysics/test/main.f90 b/demo/test/main.f90 similarity index 100% rename from cloud-microphysics/test/main.f90 rename to demo/test/main.f90 diff --git a/cloud-microphysics/test/netCDF_file_test_m.f90 b/demo/test/netCDF_file_test_m.f90 similarity index 100% rename from cloud-microphysics/test/netCDF_file_test_m.f90 rename to demo/test/netCDF_file_test_m.f90 diff --git a/cloud-microphysics/train.sh b/demo/train.sh similarity index 100% rename from cloud-microphysics/train.sh rename to demo/train.sh diff --git a/cloud-microphysics/training_configuration.json b/demo/training_configuration.json similarity index 100% rename from cloud-microphysics/training_configuration.json rename to demo/training_configuration.json From 45448c198f1a2ef030df0dcf1db820efb407d7aa Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Thu, 12 Sep 2024 16:42:24 -0700 Subject: [PATCH 057/105] chore: copy infer-aersol to pdt branch --- demo/app/infer-aerosol.f90 | 154 +++++++++++++++++++++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 demo/app/infer-aerosol.f90 diff --git a/demo/app/infer-aerosol.f90 b/demo/app/infer-aerosol.f90 new file mode 100644 index 000000000..1fa90d582 --- /dev/null +++ b/demo/app/infer-aerosol.f90 @@ -0,0 +1,154 @@ +! Copyright (c), The Regents of the University of California +! Terms of use are as specified in LICENSE.txt + +program infer_aerosol + + ! Intrinsic modules: + use iso_fortran_env, only : int64, real64 + + ! External dependencies: + use inference_engine_m, only : inference_engine_t, tensor_t, infer + use julienne_m, only : string_t, file_t, command_line_t + use kind_parameters_m, only : rkind + use omp_lib + + ! Internal dependencies: + use NetCDF_file_m, only: NetCDF_file_t + implicit none + + type tensor_statistics_t + real(rkind), allocatable, dimension(:) :: mean, standard_deviation + end type + + character(len=*), parameter :: usage_info = & + new_line('') // & + 'Usage: ./build/run-fpm.sh run infer-aerosol -- --file-path ' // & + 'where angular brackets (<>) denote user-provided input.' // & + new_line('') + + call read_stats_and_perform_inference( file_path( stop_code = usage_info ) ) + + print *,new_line('') // "______infer-aerosol done _______" + +contains + + function file_path(stop_code) result(path) + character(len=:), allocatable :: path + character(len=*), intent(in) :: stop_code + type(command_line_t) command_line + + path = command_line%flag_value("--file-path") // "/" + if (len(path)==0) error stop stop_code + + end function + + subroutine read_stats_and_perform_inference(path) + character(len=*), intent(in) :: path + integer, parameter :: num_inputs = 80, num_outputs = 31 + character(len=*), parameter :: network_file_name = "training_network.json" + character(len=*), parameter :: inputs_tensor_file_name = "training_input.nc" + real(rkind) cube_root + real(rkind), allocatable, dimension(:,:) :: aerosol_data, input_components, output_components + type(tensor_statistics_t) :: input_stats, output_stats + type(inference_engine_t) inference_engine + integer i, j + + input_stats = read_tensor_statistics(path // "meanxp.txt", path // "stdxp.txt", num_inputs) !for pre-processing normalization + output_stats = read_tensor_statistics(path // "meanyp.txt", path // "stdyp.txt", num_outputs) !for post-processing normalization + + associate(inputs_tensor_file => NetCDF_file_t(path // inputs_tensor_file_name)) + print *,"Reading network inputs from ", inputs_tensor_file_name + call inputs_tensor_file%input("input_vars", aerosol_data) + end associate + + ! print*,'shape aerosol array = ',shape(aerosol_data) ! convert to an asssertion + + allocate(input_components(size(aerosol_data,2),size(aerosol_data,1))) + allocate(output_components(size(aerosol_data,2),num_outputs)) + + !$omp parallel do shared(aerosol_data,input_components,input_stats) private(i,j,cube_root) + pre_process: & + do j = 1,size(aerosol_data,2) + do i = 1,size(aerosol_data,1) + cube_root = (abs(aerosol_data(i,j))**(1.d0/3.d0))*sign(1.0,aerosol_data(i,j)) + input_components(j,i) = (cube_root - input_stats%mean(i))/input_stats%standard_deviation(i) + end do + end do pre_process + !$omp end parallel do + + print *, "Reading the neural network from " // network_file_name + inference_engine = inference_engine_t(file_t(path // network_file_name)) + + time_inference: & + block + integer(int64) t_start, t_finish, clock_rate + type(tensor_t), allocatable :: inputs(:), outputs(:) + double precision start_time,end_time + real(rkind), allocatable :: output_slice(:) + integer i, icc + + allocate(inputs(size(input_components,1))) + allocate(outputs(size(input_components,1))) + + !$omp parallel do shared(inputs,input_components) + do i = 1,size(input_components,1) + inputs(i) = tensor_t(input_components(i,:)) + end do + !$omp end parallel do + + print*, "Starting inference." + call system_clock(t_start, clock_rate) + + icc = size(input_components,1) + + !$ start_time = omp_get_wtime() + !$omp parallel do shared(inputs,outputs,icc) + do i = 1,icc + outputs(i) = inference_engine%infer_na(inputs(i)) + end do + !$omp end parallel do + !$ end_time = omp_get_wtime() + + call system_clock(t_finish) + print*, "Finished inference." + print *,"System clock time: ", real(t_finish - t_start, real64)/real(clock_rate, real64) + !$ print*,'OMP Total time = ',end_time - start_time + + allocate(output_slice(num_outputs)) + + !$omp parallel do shared(outputs,output_components,icc,output_stats) private(output_slice,i,j) + post_process: & + do i = 1,icc + output_slice = outputs(i)%values() + do j = 1,num_outputs + output_components(i,j) = (output_stats%standard_deviation(j)*output_slice(j)+output_stats%mean(j))**3 + end do + end do post_process + !$omp end parallel do + + end block time_inference + + end subroutine read_stats_and_perform_inference + + function read_tensor_statistics(mean_file, standard_deviation_file, n) result(tensor_statistics) + type(tensor_statistics_t) tensor_statistics + character(len=*), intent(in) :: mean_file, standard_deviation_file + integer, intent(in) :: n + integer i, mean_unit, standard_deviation_unit + + open(newunit = mean_unit, file = mean_file, status="old") + open(newunit = standard_deviation_unit , file = standard_deviation_file, status="old") + + allocate(tensor_statistics%mean(n)) + allocate(tensor_statistics%standard_deviation(n)) + + do i = 1, n + read(mean_unit, *) tensor_statistics%mean(i) + read(standard_deviation_unit , *) tensor_statistics%standard_deviation(i) + end do + + close(mean_unit) + close(standard_deviation_unit) + end function + +end program infer_aerosol From 4c905b570d2055fc35c52d65fafae8bfcb9163ad Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Sat, 14 Sep 2024 19:36:00 -0700 Subject: [PATCH 058/105] feat(demo/app/infer-aerosol): add double precision --- demo/app/infer-aerosol.f90 | 23 +++++++++++------------ demo/src/NetCDF_file_m.f90 | 11 +++++++++-- demo/src/NetCDF_file_s.f90 | 25 +++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 14 deletions(-) diff --git a/demo/app/infer-aerosol.f90 b/demo/app/infer-aerosol.f90 index 1fa90d582..ef83e340c 100644 --- a/demo/app/infer-aerosol.f90 +++ b/demo/app/infer-aerosol.f90 @@ -7,9 +7,8 @@ program infer_aerosol use iso_fortran_env, only : int64, real64 ! External dependencies: - use inference_engine_m, only : inference_engine_t, tensor_t, infer - use julienne_m, only : string_t, file_t, command_line_t - use kind_parameters_m, only : rkind + use inference_engine_m, only : inference_engine_t, tensor_t, double_precision, double_precision_file_t + use julienne_m, only : string_t, command_line_t use omp_lib ! Internal dependencies: @@ -17,7 +16,7 @@ program infer_aerosol implicit none type tensor_statistics_t - real(rkind), allocatable, dimension(:) :: mean, standard_deviation + double precision, allocatable, dimension(:) :: mean, standard_deviation end type character(len=*), parameter :: usage_info = & @@ -47,10 +46,10 @@ subroutine read_stats_and_perform_inference(path) integer, parameter :: num_inputs = 80, num_outputs = 31 character(len=*), parameter :: network_file_name = "training_network.json" character(len=*), parameter :: inputs_tensor_file_name = "training_input.nc" - real(rkind) cube_root - real(rkind), allocatable, dimension(:,:) :: aerosol_data, input_components, output_components - type(tensor_statistics_t) :: input_stats, output_stats - type(inference_engine_t) inference_engine + double precision cube_root + double precision, allocatable, dimension(:,:) :: aerosol_data, input_components, output_components + type(tensor_statistics_t) input_stats, output_stats + type(inference_engine_t(double_precision)) inference_engine integer i, j input_stats = read_tensor_statistics(path // "meanxp.txt", path // "stdxp.txt", num_inputs) !for pre-processing normalization @@ -77,14 +76,14 @@ subroutine read_stats_and_perform_inference(path) !$omp end parallel do print *, "Reading the neural network from " // network_file_name - inference_engine = inference_engine_t(file_t(path // network_file_name)) + inference_engine = inference_engine_t(double_precision_file_t(path // network_file_name)) time_inference: & block integer(int64) t_start, t_finish, clock_rate - type(tensor_t), allocatable :: inputs(:), outputs(:) + type(tensor_t(double_precision)), allocatable :: inputs(:), outputs(:) double precision start_time,end_time - real(rkind), allocatable :: output_slice(:) + real, allocatable :: output_slice(:) integer i, icc allocate(inputs(size(input_components,1))) @@ -104,7 +103,7 @@ subroutine read_stats_and_perform_inference(path) !$ start_time = omp_get_wtime() !$omp parallel do shared(inputs,outputs,icc) do i = 1,icc - outputs(i) = inference_engine%infer_na(inputs(i)) + outputs(i) = inference_engine%infer(inputs(i)) end do !$omp end parallel do !$ end_time = omp_get_wtime() diff --git a/demo/src/NetCDF_file_m.f90 b/demo/src/NetCDF_file_m.f90 index c898efb17..826552464 100644 --- a/demo/src/NetCDF_file_m.f90 +++ b/demo/src/NetCDF_file_m.f90 @@ -13,8 +13,8 @@ module NetCDF_file_m private character(len=:), allocatable :: file_name_ contains - procedure :: input_2D_integer, input_1D_double, input_4D_real, input_3D_real, input_real_scalar - generic :: input => input_2D_integer, input_1D_double, input_4D_real, input_3D_real, input_real_scalar + generic :: input => input_2D_integer, input_1D_double, input_4D_real, input_3D_real, input_real_scalar, input_2D_double + procedure, private :: input_2D_integer, input_1D_double, input_4D_real, input_3D_real, input_real_scalar, input_2D_double end type interface NetCDF_file_t @@ -50,6 +50,13 @@ module subroutine input_1D_double(self, varname, values) double precision, intent(out), allocatable :: values(:) end subroutine + module subroutine input_2D_double(self, varname, values) + implicit none + class(NetCDF_file_t), intent(in) :: self + character(len=*), intent(in) :: varname + double precision, intent(out), allocatable :: values(:,:) + end subroutine + module subroutine input_4D_real(self, varname, values) implicit none class(NetCDF_file_t), intent(in) :: self diff --git a/demo/src/NetCDF_file_s.f90 b/demo/src/NetCDF_file_s.f90 index 4c0ecba59..b3aa7b9ec 100644 --- a/demo/src/NetCDF_file_s.f90 +++ b/demo/src/NetCDF_file_s.f90 @@ -124,6 +124,31 @@ function get_shape(ncid, varname) result(array_shape) end associate end procedure + module procedure input_2D_double + character(len=32) varid_string + integer ncid, varid + + associate( nf_status => nf90_open(self%file_name_, nf90_nowrite, ncid) ) ! open file with read-only acces + call assert(nf_status == nf90_noerr, "nf90_open(self%file_name_, NF90_NOWRITE, ncid)", & + trim(nf90_strerror(nf_status)) // self%file_name_) + end associate + + associate( nf_status => nf90_inq_varid(ncid, varname, varid)) ! get variable's ID + write(varid_string, *) varid + call assert(nf_status == nf90_noerr, "Net_CDF_file_m(input_2D_double): nf90_inq_varid " // trim(nf90_strerror(nf_status)), & + diagnostic_data = "varname '" // varname // "', varid " // trim(adjustl(varid_string))) + end associate + + associate(array_shape => get_shape(ncid, varname)) + call assert(size(array_shape)==rank(values), "netCDF_file_s(input_2D_double): size(array_shape)==rank(values)", & + intrinsic_array_t([size(array_shape),rank(values)])) + allocate(values(array_shape(1),array_shape(2))) + associate( nf_status => nf90_get_var(ncid, varid, values)) ! read data + call assert(nf_status == nf90_noerr, "nf90_get_var(ncid, varid, array)", trim(nf90_strerror(nf_status))) + end associate + end associate + end procedure + module procedure input_4D_real character(len=32) varid_string From 4595a8ccc041bea3e4de14df5e6a7429bea678f0 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Sat, 14 Sep 2024 20:37:23 -0700 Subject: [PATCH 059/105] refac(NetCDF_file): use generic assumed-rank I/O This commit replaces the rank-specific NetCDF_file_t type-bound input procedures with assumed-rank versions so that the interface is now rank-agnostic and expands the matrix of supported types and ranks to include 1. Integer: ranks 0-4 1. Real: ranks 0-4 1. Double precision: ranks 0-4 --- demo/src/NetCDF_file_m.f90 | 37 ++---- demo/src/NetCDF_file_s.f90 | 233 ++++++++++++++++++++----------------- 2 files changed, 136 insertions(+), 134 deletions(-) diff --git a/demo/src/NetCDF_file_m.f90 b/demo/src/NetCDF_file_m.f90 index 826552464..7bba60ab0 100644 --- a/demo/src/NetCDF_file_m.f90 +++ b/demo/src/NetCDF_file_m.f90 @@ -13,8 +13,8 @@ module NetCDF_file_m private character(len=:), allocatable :: file_name_ contains - generic :: input => input_2D_integer, input_1D_double, input_4D_real, input_3D_real, input_real_scalar, input_2D_double - procedure, private :: input_2D_integer, input_1D_double, input_4D_real, input_3D_real, input_real_scalar, input_2D_double + generic :: input => input_integer, input_double_precision, input_real + procedure, private :: input_integer, input_double_precision, input_real end type interface NetCDF_file_t @@ -29,46 +29,25 @@ pure module function construct(file_name) result(NetCDF_file) interface - module subroutine input_real_scalar(self, varname, scalar) + module subroutine input_real(self, varname, values) implicit none class(NetCDF_file_t), intent(in) :: self character(len=*), intent(in) :: varname - real, intent(out) :: scalar + real, intent(out), allocatable :: values(..) end subroutine - module subroutine input_2D_integer(self, varname, values) + module subroutine input_integer(self, varname, values) implicit none class(NetCDF_file_t), intent(in) :: self character(len=*), intent(in) :: varname - integer, intent(out), allocatable :: values(:,:) + integer, intent(out), allocatable :: values(..) end subroutine - module subroutine input_1D_double(self, varname, values) + module subroutine input_double_precision(self, varname, values) implicit none class(NetCDF_file_t), intent(in) :: self character(len=*), intent(in) :: varname - double precision, intent(out), allocatable :: values(:) - end subroutine - - module subroutine input_2D_double(self, varname, values) - implicit none - class(NetCDF_file_t), intent(in) :: self - character(len=*), intent(in) :: varname - double precision, intent(out), allocatable :: values(:,:) - end subroutine - - module subroutine input_4D_real(self, varname, values) - implicit none - class(NetCDF_file_t), intent(in) :: self - character(len=*), intent(in) :: varname - real, intent(out), allocatable :: values(:,:,:,:) - end subroutine - - module subroutine input_3D_real(self, varname, values) - implicit none - class(NetCDF_file_t), intent(in) :: self - character(len=*), intent(in) :: varname - real, intent(out), allocatable :: values(:,:,:) + double precision, intent(out), allocatable :: values(..) end subroutine end interface diff --git a/demo/src/NetCDF_file_s.f90 b/demo/src/NetCDF_file_s.f90 index b3aa7b9ec..0200cfc89 100644 --- a/demo/src/NetCDF_file_s.f90 +++ b/demo/src/NetCDF_file_s.f90 @@ -49,57 +49,63 @@ function get_shape(ncid, varname) result(array_shape) array_shape = dims(2:var_rank+1) end function - module procedure input_real_scalar - - character(len=32) varid_string - integer ncid, varid - - associate( nf_status => nf90_open(self%file_name_, nf90_nowrite, ncid) ) ! open file with read-only acces - call assert(nf_status == nf90_noerr, & - "Net_CDF_file_m(input_real_scalar): nf90_open " // trim(nf90_strerror(nf_status)), & - diagnostic_data = trim(nf90_strerror(nf_status)) // self%file_name_) - end associate - - associate( nf_status => nf90_inq_varid(ncid, varname, varid)) ! get variable's ID - write(varid_string, *) varid - call assert(nf_status == nf90_noerr, "Net_CDF_file_m(input_real_scalar): nf90_inq_varid " // trim(nf90_strerror(nf_status)), & - diagnostic_data = "varname '" // varname // "', varid " // trim(adjustl(varid_string))) - end associate - - associate( nf_status => nf90_get_var(ncid, varid, scalar)) ! read data - call assert(nf_status == nf90_noerr, "NetCDF_file_s(input_real_scalar): nf90_get_var ", trim(nf90_strerror(nf_status))) - end associate - - end procedure - - module procedure input_2D_integer + module procedure input_integer character(len=32) varid_string integer ncid, varid associate( nf_status => nf90_open(self%file_name_, nf90_nowrite, ncid) ) ! open file with read-only acces call assert(nf_status == nf90_noerr, & - "Net_CDF_file_m(input_2D_integer): nf90_open" // trim(nf90_strerror(nf_status)), & + "Net_CDF_file_m(input_integer): nf90_open" // trim(nf90_strerror(nf_status)), & diagnostic_data = trim(nf90_strerror(nf_status)) // self%file_name_) end associate associate( nf_status => nf90_inq_varid(ncid, varname, varid)) ! get variable's ID write(varid_string, *) varid - call assert(nf_status == nf90_noerr, "Net_CDF_file_m(input_2D_integer): nf90_inq_varid " // trim(nf90_strerror(nf_status)), & + call assert(nf_status == nf90_noerr, "Net_CDF_file_m(input_integer): nf90_inq_varid " // trim(nf90_strerror(nf_status)), & diagnostic_data = "varname '" // varname // "', varid " // trim(adjustl(varid_string))) end associate - associate(array_shape => get_shape(ncid, varname)) - call assert(size(array_shape)==rank(values), "netCDF_file_s(input_2D_integer): size(array_shape)==rank(values)") - allocate(values(array_shape(1), array_shape(2))) - associate( nf_status => nf90_get_var(ncid, varid, values)) ! read data - call assert(nf_status == nf90_noerr, "NetCDF_file_s(input_2D_integer): nf90_get_var", trim(nf90_strerror(nf_status))) - end associate - end associate + select rank(values) + rank(1) + associate(array_shape => get_shape(ncid, varname)) + call assert(size(array_shape)==rank(values), "netCDF_file_s(input_integer): size(array_shape)==rank(values)") + allocate(values(array_shape(1))) + associate( nf_status => nf90_get_var(ncid, varid, values)) ! read data + call assert(nf_status == nf90_noerr, "NetCDF_file_s(input_integer): nf90_get_var", trim(nf90_strerror(nf_status))) + end associate + end associate + rank(2) + associate(array_shape => get_shape(ncid, varname)) + call assert(size(array_shape)==rank(values), "netCDF_file_s(input_integer): size(array_shape)==rank(values)") + allocate(values(array_shape(1), array_shape(2))) + associate( nf_status => nf90_get_var(ncid, varid, values)) ! read data + call assert(nf_status == nf90_noerr, "NetCDF_file_s(input_integer): nf90_get_var", trim(nf90_strerror(nf_status))) + end associate + end associate + rank(3) + associate(array_shape => get_shape(ncid, varname)) + call assert(size(array_shape)==rank(values), "netCDF_file_s(input_integer): size(array_shape)==rank(values)") + allocate(values(array_shape(1), array_shape(2), array_shape(3))) + associate( nf_status => nf90_get_var(ncid, varid, values)) ! read data + call assert(nf_status == nf90_noerr, "NetCDF_file_s(input_integer): nf90_get_var", trim(nf90_strerror(nf_status))) + end associate + end associate + rank(4) + associate(array_shape => get_shape(ncid, varname)) + call assert(size(array_shape)==rank(values), "netCDF_file_s(input_integer): size(array_shape)==rank(values)") + allocate(values(array_shape(1), array_shape(2), array_shape(3), array_shape(4))) + associate( nf_status => nf90_get_var(ncid, varid, values)) ! read data + call assert(nf_status == nf90_noerr, "NetCDF_file_s(input_integer): nf90_get_var", trim(nf90_strerror(nf_status))) + end associate + end associate + rank default + error stop "NetCDF_file_s(input_integer): unsupported rank" + end select end procedure - module procedure input_1D_double + module procedure input_double_precision character(len=32) varid_string integer ncid, varid @@ -110,73 +116,53 @@ function get_shape(ncid, varname) result(array_shape) associate( nf_status => nf90_inq_varid(ncid, varname, varid)) ! get variable's ID write(varid_string, *) varid - call assert(nf_status == nf90_noerr, "Net_CDF_file_m(input_1D_double): nf90_inq_varid " // trim(nf90_strerror(nf_status)), & + call assert(nf_status == nf90_noerr, "Net_CDF_file_m(input_double): nf90_inq_varid " // trim(nf90_strerror(nf_status)), & diagnostic_data = "varname '" // varname // "', varid " // trim(adjustl(varid_string))) end associate - associate(array_shape => get_shape(ncid, varname)) - call assert(size(array_shape)==rank(values), "netCDF_file_s(input_1D_double): size(array_shape)==rank(values)", & - intrinsic_array_t([size(array_shape),rank(values)])) - allocate(values(array_shape(1))) - associate( nf_status => nf90_get_var(ncid, varid, values)) ! read data - call assert(nf_status == nf90_noerr, "nf90_get_var(ncid, varid, array)", trim(nf90_strerror(nf_status))) - end associate - end associate + select rank(values) + rank(1) + associate(array_shape => get_shape(ncid, varname)) + call assert(size(array_shape)==rank(values), "netCDF_file_s(input_double): size(array_shape)==rank(values)", & + intrinsic_array_t([size(array_shape),rank(values)])) + allocate(values(array_shape(1))) + associate( nf_status => nf90_get_var(ncid, varid, values)) ! read data + call assert(nf_status == nf90_noerr, "nf90_get_var(ncid, varid, array)", trim(nf90_strerror(nf_status))) + end associate + end associate + rank(2) + associate(array_shape => get_shape(ncid, varname)) + call assert(size(array_shape)==rank(values), "netCDF_file_s(input_double): size(array_shape)==rank(values)", & + intrinsic_array_t([size(array_shape),rank(values)])) + allocate(values(array_shape(1),array_shape(2))) + associate( nf_status => nf90_get_var(ncid, varid, values)) ! read data + call assert(nf_status == nf90_noerr, "nf90_get_var(ncid, varid, array)", trim(nf90_strerror(nf_status))) + end associate + end associate + rank(3) + associate(array_shape => get_shape(ncid, varname)) + call assert(size(array_shape)==rank(values), "netCDF_file_s(input_double): size(array_shape)==rank(values)", & + intrinsic_array_t([size(array_shape),rank(values)])) + allocate(values(array_shape(1),array_shape(2),array_shape(3))) + associate( nf_status => nf90_get_var(ncid, varid, values)) ! read data + call assert(nf_status == nf90_noerr, "nf90_get_var(ncid, varid, array)", trim(nf90_strerror(nf_status))) + end associate + end associate + rank(4) + associate(array_shape => get_shape(ncid, varname)) + call assert(size(array_shape)==rank(values), "netCDF_file_s(input_double): size(array_shape)==rank(values)", & + intrinsic_array_t([size(array_shape),rank(values)])) + allocate(values(array_shape(1),array_shape(2),array_shape(3),array_shape(4))) + associate( nf_status => nf90_get_var(ncid, varid, values)) ! read data + call assert(nf_status == nf90_noerr, "nf90_get_var(ncid, varid, array)", trim(nf90_strerror(nf_status))) + end associate + end associate + rank default + error stop "NetCDF_file_s(input_double): unsupported rank" + end select end procedure - - module procedure input_2D_double - character(len=32) varid_string - integer ncid, varid - associate( nf_status => nf90_open(self%file_name_, nf90_nowrite, ncid) ) ! open file with read-only acces - call assert(nf_status == nf90_noerr, "nf90_open(self%file_name_, NF90_NOWRITE, ncid)", & - trim(nf90_strerror(nf_status)) // self%file_name_) - end associate - - associate( nf_status => nf90_inq_varid(ncid, varname, varid)) ! get variable's ID - write(varid_string, *) varid - call assert(nf_status == nf90_noerr, "Net_CDF_file_m(input_2D_double): nf90_inq_varid " // trim(nf90_strerror(nf_status)), & - diagnostic_data = "varname '" // varname // "', varid " // trim(adjustl(varid_string))) - end associate - - associate(array_shape => get_shape(ncid, varname)) - call assert(size(array_shape)==rank(values), "netCDF_file_s(input_2D_double): size(array_shape)==rank(values)", & - intrinsic_array_t([size(array_shape),rank(values)])) - allocate(values(array_shape(1),array_shape(2))) - associate( nf_status => nf90_get_var(ncid, varid, values)) ! read data - call assert(nf_status == nf90_noerr, "nf90_get_var(ncid, varid, array)", trim(nf90_strerror(nf_status))) - end associate - end associate - end procedure - - module procedure input_4D_real - - character(len=32) varid_string - integer ncid, varid - - associate( nf_status => nf90_open(self%file_name_, nf90_nowrite, ncid) ) ! open file with read-only acces - call assert(nf_status == nf90_noerr, "nf90_open(self%file_name_, NF90_NOWRITE, ncid)", & - trim(nf90_strerror(nf_status)) // self%file_name_) - end associate - - associate( nf_status => nf90_inq_varid(ncid, varname, varid)) ! get variable's ID - write(varid_string, *) varid - call assert(nf_status == nf90_noerr, "Net_CDF_file_m(input_4D_real): nf90_inq_varid " // trim(nf90_strerror(nf_status)), & - diagnostic_data = "varname '" // varname // "', varid " // trim(adjustl(varid_string))) - end associate - - associate(array_shape => get_shape(ncid, varname)) - call assert(size(array_shape)==rank(values), "netCDF_file_s(input_4D_real): size(array_shape)==rank(values)", & - intrinsic_array_t([size(array_shape),rank(values)])) - allocate(values(array_shape(1), array_shape(2), array_shape(3), array_shape(4))) - associate( nf_status => nf90_get_var(ncid, varid, values)) ! read data - call assert(nf_status == nf90_noerr, "nf90_get_var(ncid, varid, array)", trim(nf90_strerror(nf_status))) - end associate - end associate - - end procedure - - module procedure input_3D_real + module procedure input_real character(len=32) varid_string integer ncid, varid @@ -188,18 +174,55 @@ function get_shape(ncid, varname) result(array_shape) associate( nf_status => nf90_inq_varid(ncid, varname, varid)) ! get variable's ID write(varid_string, *) varid - call assert(nf_status == nf90_noerr, "Net_CDF_file_m(input_3D_real): nf90_inq_varid " // trim(nf90_strerror(nf_status)), & + call assert(nf_status == nf90_noerr, "Net_CDF_file_m(input_real): nf90_inq_varid " // trim(nf90_strerror(nf_status)), & diagnostic_data = "varname '" // varname // "', varid " // trim(adjustl(varid_string))) end associate - associate(array_shape => get_shape(ncid, varname)) - call assert(size(array_shape)==rank(values), "netCDF_file_s(input_3D_real): size(array_shape)==rank(values)", & - intrinsic_array_t([size(array_shape),rank(values)])) - allocate(values(array_shape(1), array_shape(2), array_shape(3))) - associate( nf_status => nf90_get_var(ncid, varid, values)) ! read data - call assert(nf_status == nf90_noerr, "nf90_get_var(ncid, varid, array)", trim(nf90_strerror(nf_status))) - end associate - end associate + select rank(values) + rank(0) + allocate(values) + associate( nf_status => nf90_get_var(ncid, varid, values)) ! read data + call assert(nf_status == nf90_noerr, "nf90_get_var(ncid, varid, values)", trim(nf90_strerror(nf_status))) + end associate + rank(1) + associate(array_shape => get_shape(ncid, varname)) + call assert(size(array_shape)==rank(values), "netCDF_file_s(input_real): size(array_shape)==rank(values)", & + intrinsic_array_t([size(array_shape),rank(values)])) + allocate(values(array_shape(1))) + associate( nf_status => nf90_get_var(ncid, varid, values)) ! read data + call assert(nf_status == nf90_noerr, "nf90_get_var(ncid, varid, array)", trim(nf90_strerror(nf_status))) + end associate + end associate + rank(2) + associate(array_shape => get_shape(ncid, varname)) + call assert(size(array_shape)==rank(values), "netCDF_file_s(input_real): size(array_shape)==rank(values)", & + intrinsic_array_t([size(array_shape),rank(values)])) + allocate(values(array_shape(1), array_shape(2))) + associate( nf_status => nf90_get_var(ncid, varid, values)) ! read data + call assert(nf_status == nf90_noerr, "nf90_get_var(ncid, varid, array)", trim(nf90_strerror(nf_status))) + end associate + end associate + rank(3) + associate(array_shape => get_shape(ncid, varname)) + call assert(size(array_shape)==rank(values), "netCDF_file_s(input_real): size(array_shape)==rank(values)", & + intrinsic_array_t([size(array_shape),rank(values)])) + allocate(values(array_shape(1), array_shape(2), array_shape(3))) + associate( nf_status => nf90_get_var(ncid, varid, values)) ! read data + call assert(nf_status == nf90_noerr, "nf90_get_var(ncid, varid, array)", trim(nf90_strerror(nf_status))) + end associate + end associate + rank(4) + associate(array_shape => get_shape(ncid, varname)) + call assert(size(array_shape)==rank(values), "netCDF_file_s(input_real): size(array_shape)==rank(values)", & + intrinsic_array_t([size(array_shape),rank(values)])) + allocate(values(array_shape(1), array_shape(2), array_shape(3), array_shape(4))) + associate( nf_status => nf90_get_var(ncid, varid, values)) ! read data + call assert(nf_status == nf90_noerr, "nf90_get_var(ncid, varid, array)", trim(nf90_strerror(nf_status))) + end associate + end associate + rank default + error stop "NetCDF_file_s(input_real_array): unsupported rank" + end select end procedure From a092e02a68b01e35d42531cbf147dfc22a1d9b03 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Sat, 14 Sep 2024 21:53:45 -0700 Subject: [PATCH 060/105] fix(infer-aerosol): handle missing req'd arg --- demo/app/infer-aerosol.f90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/demo/app/infer-aerosol.f90 b/demo/app/infer-aerosol.f90 index ef83e340c..1a3d42302 100644 --- a/demo/app/infer-aerosol.f90 +++ b/demo/app/infer-aerosol.f90 @@ -36,9 +36,9 @@ function file_path(stop_code) result(path) character(len=*), intent(in) :: stop_code type(command_line_t) command_line - path = command_line%flag_value("--file-path") // "/" + path = command_line%flag_value("--file-path") if (len(path)==0) error stop stop_code - + path = path // "/" end function subroutine read_stats_and_perform_inference(path) From e7bc20a39c240a5d204b2a89a8b6ab9dfe345608 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Sun, 15 Sep 2024 20:48:13 -0700 Subject: [PATCH 061/105] fix(demo): revise setup.sh for Linux --- demo/setup.sh | 83 +++++++++++++++++++++++++++++---------------------- 1 file changed, 47 insertions(+), 36 deletions(-) diff --git a/demo/setup.sh b/demo/setup.sh index 49686bf1c..a7e19ce14 100755 --- a/demo/setup.sh +++ b/demo/setup.sh @@ -39,40 +39,54 @@ 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" +FPM_FC=${FPM_FC:-"flang-new"} +FPM_CC=${FPM_CC:-"clang"} + +if [ $(uname) = "Darwin" ]; then + if command -v brew ; then + brew install netcdf netcdf-fortran pkg-config coreutils # coreutils supports `realpath` below + NETCDF_LIB_PATH="`brew --prefix netcdf`/lib" + HDF5_LIB_PATH="`brew --prefix hdf5`/lib" + NETCDFF_LIB_PATH="`brew --prefix netcdf-fortran`/lib" + fpm_cc_version=$($FPM_CC --version) + if [[ $fpm_cc_version = Apple* ]]; then + echo "$FPM_CC appears to be an Apple compiler. Please set FPM_CC to the location of LLVM clang." + exit 1 fi + else + cat <<'EOF' + + Command 'brew' not found. On macOS, this script uses Homebrew (https://brew.sh) to + install the prerequisite packages netcdf, netcdf-fortran, pkg-config, and coreutils. + Please install Homebrew and restart this script." +EOF fi +elif [ $(uname) = "Linux" ]; then + echo + # TODO: Unless NETCDF_LIB_PATH, HDF5_LIB_PATH, and NETCDFF_LIB_PATH are set, build + # NetCDF, NetCDF-Fortran and HDF5 set the aforementioned environment variables. fi -brew install netcdf netcdf-fortran pkg-config coreutils # coreutils supports `realpath` below +[ -z ${HDF5_LIB_PATH:-} ] && printf "Please set HDF5_LIB_PATH to the HDF5 library path and restart this script.\n\n"; exit 1 +[ -z ${NETCDF_LIB_PATH:-} ] && printf "Please set NETCDF_LIB_PATH to the NetCDF library path and restart this script.\n\n"; exit 1 +[ -z ${NETCDFF_LIB_PATH:-} ] && printf "Please set NETCDFF_LIB_PATH to the NetCDF-Fortran library path and restart this script.\n\n"; exit 1 -PREFIX=`realpath $PREFIX` +FPM_LD_FLAG=" -L$NETCDF_LIB_PATH -L$HDF5_LIB_PATH -L$NETCDFF_LIB_PATH" -NETCDF_LIB_PATH="`brew --prefix netcdf`/lib" -HDF5_LIB_PATH="`brew --prefix hdf5`/lib" -NETCDFF_LIB_PATH="`brew --prefix netcdf-fortran`/lib" +PREFIX=`realpath $PREFIX` -FPM_LD_FLAG=" -L$NETCDF_LIB_PATH -L$HDF5_LIB_PATH -L$NETCDFF_LIB_PATH" -FPM_FC=${FC:-"gfortran-14"} -FPM_CC=${CC:-"gcc-14"} - -FPM_FC_NAME_ONLY=${FPM_FC##*/} -if [ $FPM_FC_NAME_ONLY} = "gfortran-14" ]; then - FPM_FLAG="-fcoarray=single -O3 -fallow-argument-mismatch -ffree-line-length-none -L$NETCDF_LIB_PATH -L$HDF5_LIB_PATH" -elif [ $FPM_FC_NAME_ONLY = "flang-new" ]; then - FPM_FLAG="-mmlir -allow-assumed-rank -O3 -L$NETCDF_LIB_PATH -L$HDF5_LIB_PATH" +fpm_fc_version=$($FPM_FC --version) +if [[ $fpm_fc_version = flang* ]]; then + FPM_FC_FLAG="-mmlir -allow-assumed-rank -O3 -L$NETCDF_LIB_PATH -L$HDF5_LIB_PATH" + FPM_LD_FLAG="" +elif [[ $fpm_fc_version = GNU* ]]; then + echo + echo "$FPM_FC appears to be gfortran, which is currently unsupported due to compiler bugs for parameterized derived types." + echo + exit 1 + FPM_FC_FLAG="-fcoarray=single -O3 -fallow-argument-mismatch -ffree-line-length-none -L$NETCDF_LIB_PATH -L$HDF5_LIB_PATH" else - FPM_FLAG="" + FPM_FC_FLAG="" fi mkdir -p build @@ -100,10 +114,10 @@ echo "INFERENCE_ENGINE_FPM_FLAG=\"$FPM_FLAG\"" >> $INFERENCE_ENGINE 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 +echo "Version: 0.13.0" >> $INFERENCE_ENGINE_PC if [ $CI = true ]; then echo "---------------" - echo "cat $INFERENCE_ENGINE_PC" + echo "cat \$INFERENCE_ENGINE_PC" cat $INFERENCE_ENGINE_PC echo "---------------" fi @@ -126,16 +140,13 @@ if [ $CI = true ]; then echo "---------------" fi -echo "$RUN_FPM_SH test" -$RUN_FPM_SH test +$RUN_FPM_SH build echo "" -echo "____________________ cloud-microphysics has been set up! _______________________" +echo "____________________ The inference-engine demo apps build succeeded! _______________________" echo "" -echo "Usage:" +echo "Run the following command to see a list of available apps:" echo "" -echo "./build/run-fpm.sh run train-cloud-microphysics -- \ " -echo " --base --epochs \ " -echo " [--start ] [--end ] [--stride ] [--bins Date: Mon, 16 Sep 2024 13:17:28 -0700 Subject: [PATCH 062/105] fix(setup.sh): works on macOS --- demo/setup.sh | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/demo/setup.sh b/demo/setup.sh index a7e19ce14..57eb0d932 100755 --- a/demo/setup.sh +++ b/demo/setup.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash set -e # exit on error @@ -67,9 +67,18 @@ elif [ $(uname) = "Linux" ]; then # NetCDF, NetCDF-Fortran and HDF5 set the aforementioned environment variables. fi -[ -z ${HDF5_LIB_PATH:-} ] && printf "Please set HDF5_LIB_PATH to the HDF5 library path and restart this script.\n\n"; exit 1 -[ -z ${NETCDF_LIB_PATH:-} ] && printf "Please set NETCDF_LIB_PATH to the NetCDF library path and restart this script.\n\n"; exit 1 -[ -z ${NETCDFF_LIB_PATH:-} ] && printf "Please set NETCDFF_LIB_PATH to the NetCDF-Fortran library path and restart this script.\n\n"; exit 1 +if [[ -z ${HDF5_LIB_PATH:-} ]]; then + printf "Please set HDF5_LIB_PATH to the HDF5 library path and restart this script.\n\n" + exit 1 +fi +if [ -z ${NETCDF_LIB_PATH:-} ]; then + printf "Please set NETCDF_LIB_PATH to the NetCDF library path and restart this script.\n\n" + exit 1 +fi +if [ -z ${NETCDFF_LIB_PATH:-} ]; then + printf "Please set NETCDFF_LIB_PATH to the NetCDF-Fortran library path and restart this script.\n\n" + exit 1 +fi FPM_LD_FLAG=" -L$NETCDF_LIB_PATH -L$HDF5_LIB_PATH -L$NETCDFF_LIB_PATH" @@ -77,16 +86,15 @@ PREFIX=`realpath $PREFIX` fpm_fc_version=$($FPM_FC --version) if [[ $fpm_fc_version = flang* ]]; then - FPM_FC_FLAG="-mmlir -allow-assumed-rank -O3 -L$NETCDF_LIB_PATH -L$HDF5_LIB_PATH" - FPM_LD_FLAG="" + FPM_FLAG="-mmlir -allow-assumed-rank -O3 -L$NETCDF_LIB_PATH -L$HDF5_LIB_PATH" elif [[ $fpm_fc_version = GNU* ]]; then echo echo "$FPM_FC appears to be gfortran, which is currently unsupported due to compiler bugs for parameterized derived types." echo exit 1 - FPM_FC_FLAG="-fcoarray=single -O3 -fallow-argument-mismatch -ffree-line-length-none -L$NETCDF_LIB_PATH -L$HDF5_LIB_PATH" + FPM_FLAG="-fcoarray=single -O3 -fallow-argument-mismatch -ffree-line-length-none -L$NETCDF_LIB_PATH -L$HDF5_LIB_PATH" else - FPM_FC_FLAG="" + FPM_FLAG="" fi mkdir -p build From 2d908a600de8236d588aba14c96c9386152ed691 Mon Sep 17 00:00:00 2001 From: Katherine Rasmussen Date: Mon, 16 Sep 2024 14:06:18 -0700 Subject: [PATCH 063/105] Update filenames being read in by infer_aerosol --- demo/app/infer-aerosol.f90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/demo/app/infer-aerosol.f90 b/demo/app/infer-aerosol.f90 index 1a3d42302..319710db0 100644 --- a/demo/app/infer-aerosol.f90 +++ b/demo/app/infer-aerosol.f90 @@ -44,8 +44,8 @@ function file_path(stop_code) result(path) subroutine read_stats_and_perform_inference(path) character(len=*), intent(in) :: path integer, parameter :: num_inputs = 80, num_outputs = 31 - character(len=*), parameter :: network_file_name = "training_network.json" - character(len=*), parameter :: inputs_tensor_file_name = "training_input.nc" + character(len=*), parameter :: network_file_name = "model.json" + character(len=*), parameter :: inputs_tensor_file_name = "aerosol_input.nc" double precision cube_root double precision, allocatable, dimension(:,:) :: aerosol_data, input_components, output_components type(tensor_statistics_t) input_stats, output_stats From 5f4d8f980d0a0de126536feb8df0fa178e46a999 Mon Sep 17 00:00:00 2001 From: Katherine Rasmussen Date: Mon, 16 Sep 2024 15:00:03 -0700 Subject: [PATCH 064/105] Update actions/upload-artifact version for github deploy documentation workflow --- .github/workflows/deploy-docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml index 183d5bfc1..5714ed292 100644 --- a/.github/workflows/deploy-docs.yml +++ b/.github/workflows/deploy-docs.yml @@ -26,7 +26,7 @@ jobs: ford ford.md cp ./README.md ./doc/html - name: Upload Documentation - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: documentation path: doc/html From 47f9ef45579d76a8213ef73b70e84520664dc1ba Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Fri, 20 Sep 2024 14:42:05 -0700 Subject: [PATCH 065/105] feat(demo): add unmapped_engine_t constructor The new constructor is used demo/app/infer-aerosol.f90. --- demo/app/infer-aerosol.f90 | 6 +++--- src/inference_engine/inference_engine_m_.f90 | 11 +++++++++++ src/inference_engine/inference_engine_s.F90 | 4 ++++ 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/demo/app/infer-aerosol.f90 b/demo/app/infer-aerosol.f90 index 319710db0..5cd369b61 100644 --- a/demo/app/infer-aerosol.f90 +++ b/demo/app/infer-aerosol.f90 @@ -7,7 +7,7 @@ program infer_aerosol use iso_fortran_env, only : int64, real64 ! External dependencies: - use inference_engine_m, only : inference_engine_t, tensor_t, double_precision, double_precision_file_t + use inference_engine_m, only : unmapped_engine_t, tensor_t, double_precision, double_precision_file_t use julienne_m, only : string_t, command_line_t use omp_lib @@ -49,7 +49,7 @@ subroutine read_stats_and_perform_inference(path) double precision cube_root double precision, allocatable, dimension(:,:) :: aerosol_data, input_components, output_components type(tensor_statistics_t) input_stats, output_stats - type(inference_engine_t(double_precision)) inference_engine + type(unmapped_engine_t(double_precision)) inference_engine integer i, j input_stats = read_tensor_statistics(path // "meanxp.txt", path // "stdxp.txt", num_inputs) !for pre-processing normalization @@ -76,7 +76,7 @@ subroutine read_stats_and_perform_inference(path) !$omp end parallel do print *, "Reading the neural network from " // network_file_name - inference_engine = inference_engine_t(double_precision_file_t(path // network_file_name)) + inference_engine = unmapped_engine_t(double_precision_file_t(path // network_file_name)) time_inference: & block diff --git a/src/inference_engine/inference_engine_m_.f90 b/src/inference_engine/inference_engine_m_.f90 index ead1eed60..f87f1a3fd 100644 --- a/src/inference_engine/inference_engine_m_.f90 +++ b/src/inference_engine/inference_engine_m_.f90 @@ -104,6 +104,17 @@ impure elemental module function double_precision_from_json(file) result(inferen end interface + interface unmapped_engine_t + + impure elemental module function double_precision_unmapped_from_json(file) result(unmapped_engine) + implicit none + type(double_precision_file_t), intent(in) :: file + type(unmapped_engine_t(double_precision)) unmapped_engine + end function + + end interface + + interface elemental module function default_real_approximately_equal(lhs, rhs) result(lhs_eq_rhs) diff --git a/src/inference_engine/inference_engine_s.F90 b/src/inference_engine/inference_engine_s.F90 index e19c3701c..64fde4e8c 100644 --- a/src/inference_engine/inference_engine_s.F90 +++ b/src/inference_engine/inference_engine_s.F90 @@ -703,6 +703,10 @@ impure function activation_factory_method(activation_name) result(activation) end procedure default_real_from_json + module procedure double_precision_unmapped_from_json + unmapped_engine%inference_engine_t = double_precision_from_json(file) + end procedure + module procedure double_precision_from_json character(len=:), allocatable :: justified_line From 98a68a4cf25a1c45fe66407d666ddd19d4813a7d Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Fri, 20 Sep 2024 15:09:17 -0700 Subject: [PATCH 066/105] fix(infer-aerosol): double-prec. sign() invocation --- demo/app/infer-aerosol.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo/app/infer-aerosol.f90 b/demo/app/infer-aerosol.f90 index 5cd369b61..6810af0cc 100644 --- a/demo/app/infer-aerosol.f90 +++ b/demo/app/infer-aerosol.f90 @@ -69,7 +69,7 @@ subroutine read_stats_and_perform_inference(path) pre_process: & do j = 1,size(aerosol_data,2) do i = 1,size(aerosol_data,1) - cube_root = (abs(aerosol_data(i,j))**(1.d0/3.d0))*sign(1.0,aerosol_data(i,j)) + cube_root = (abs(aerosol_data(i,j))**(1.d0/3.d0))*sign(1.d0,aerosol_data(i,j)) input_components(j,i) = (cube_root - input_stats%mean(i))/input_stats%standard_deviation(i) end do end do pre_process From f3872905bf1da98cafa3e29873740f0e97fd4a76 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Fri, 20 Sep 2024 17:56:53 -0700 Subject: [PATCH 067/105] chore: add missing copyright statements to files --- demo/src/phase_space_bin_m.f90 | 2 ++ example/print-training-configuration.F90 | 2 ++ src/inference_engine/double_precision_file_m.f90 | 2 ++ src/inference_engine/double_precision_file_s.f90 | 2 ++ src/inference_engine/double_precision_string_m.f90 | 2 ++ src/inference_engine/double_precision_string_s.f90 | 2 ++ src/inference_engine/hyperparameters_m.f90 | 2 ++ src/inference_engine/hyperparameters_s.f90 | 2 ++ src/inference_engine/metadata_m.f90 | 2 ++ src/inference_engine/metadata_s.f90 | 2 ++ src/inference_engine/network_configuration_m.f90 | 2 ++ src/inference_engine/network_configuration_s.F90 | 2 ++ src/inference_engine/training_configuration_m.f90 | 2 ++ src/inference_engine/training_configuration_s.F90 | 2 ++ 14 files changed, 28 insertions(+) diff --git a/demo/src/phase_space_bin_m.f90 b/demo/src/phase_space_bin_m.f90 index d48655599..1e164fea1 100644 --- a/demo/src/phase_space_bin_m.f90 +++ b/demo/src/phase_space_bin_m.f90 @@ -1,3 +1,5 @@ +! Copyright (c), The Regents of the University of California +! Terms of use are as specified in LICENSE.txt module phase_space_bin_m use tensor_map_m, only : tensor_map_t use tensor_m, only : tensor_t diff --git a/example/print-training-configuration.F90 b/example/print-training-configuration.F90 index 6643318bd..cf6f4996b 100644 --- a/example/print-training-configuration.F90 +++ b/example/print-training-configuration.F90 @@ -1,3 +1,5 @@ +! Copyright (c), The Regents of the University of California +! Terms of use are as specified in LICENSE.txt program print_training_configuration !! Demonstrate how to construct and print a training_configuration_t object use inference_engine_m, only : training_configuration_t, hyperparameters_t, network_configuration_t diff --git a/src/inference_engine/double_precision_file_m.f90 b/src/inference_engine/double_precision_file_m.f90 index 6264116ef..f62506a42 100644 --- a/src/inference_engine/double_precision_file_m.f90 +++ b/src/inference_engine/double_precision_file_m.f90 @@ -1,3 +1,5 @@ +! Copyright (c), The Regents of the University of California +! Terms of use are as specified in LICENSE.txt module double_precision_file_m use julienne_m, only : file_t, string_t use double_precision_string_m, only : double_precision_string_t diff --git a/src/inference_engine/double_precision_file_s.f90 b/src/inference_engine/double_precision_file_s.f90 index 961bfde48..620e95291 100644 --- a/src/inference_engine/double_precision_file_s.f90 +++ b/src/inference_engine/double_precision_file_s.f90 @@ -1,3 +1,5 @@ +! Copyright (c), The Regents of the University of California +! Terms of use are as specified in LICENSE.txt submodule(double_precision_file_m) double_precision_file_s implicit none diff --git a/src/inference_engine/double_precision_string_m.f90 b/src/inference_engine/double_precision_string_m.f90 index 14f0cdea0..01b2f19d2 100644 --- a/src/inference_engine/double_precision_string_m.f90 +++ b/src/inference_engine/double_precision_string_m.f90 @@ -1,3 +1,5 @@ +! Copyright (c), The Regents of the University of California +! Terms of use are as specified in LICENSE.txt module double_precision_string_m use julienne_m, only : string_t implicit none diff --git a/src/inference_engine/double_precision_string_s.f90 b/src/inference_engine/double_precision_string_s.f90 index 9e4ff72d3..b0cecd45c 100644 --- a/src/inference_engine/double_precision_string_s.f90 +++ b/src/inference_engine/double_precision_string_s.f90 @@ -1,3 +1,5 @@ +! Copyright (c), The Regents of the University of California +! Terms of use are as specified in LICENSE.txt submodule(double_precision_string_m)double_precision_string_s implicit none contains diff --git a/src/inference_engine/hyperparameters_m.f90 b/src/inference_engine/hyperparameters_m.f90 index e6d41a529..7d485be73 100644 --- a/src/inference_engine/hyperparameters_m.f90 +++ b/src/inference_engine/hyperparameters_m.f90 @@ -1,3 +1,5 @@ +! Copyright (c), The Regents of the University of California +! Terms of use are as specified in LICENSE.txt module hyperparameters_m use julienne_string_m, only : string_t use kind_parameters_m, only : default_real, double_precision diff --git a/src/inference_engine/hyperparameters_s.f90 b/src/inference_engine/hyperparameters_s.f90 index 229d1d637..e6df53996 100644 --- a/src/inference_engine/hyperparameters_s.f90 +++ b/src/inference_engine/hyperparameters_s.f90 @@ -1,3 +1,5 @@ +! Copyright (c), The Regents of the University of California +! Terms of use are as specified in LICENSE.txt submodule(hyperparameters_m) hyperparameters_s use assert_m, only : assert implicit none diff --git a/src/inference_engine/metadata_m.f90 b/src/inference_engine/metadata_m.f90 index 6dcf5c909..a5d9df6c3 100644 --- a/src/inference_engine/metadata_m.f90 +++ b/src/inference_engine/metadata_m.f90 @@ -1,3 +1,5 @@ +! Copyright (c), The Regents of the University of California +! Terms of use are as specified in LICENSE.txt module metadata_m use julienne_string_m, only : string_t use double_precision_string_m, only : double_precision_string_t diff --git a/src/inference_engine/metadata_s.f90 b/src/inference_engine/metadata_s.f90 index c2702bcd4..ca521d0ff 100644 --- a/src/inference_engine/metadata_s.f90 +++ b/src/inference_engine/metadata_s.f90 @@ -1,3 +1,5 @@ +! Copyright (c), The Regents of the University of California +! Terms of use are as specified in LICENSE.txt submodule(metadata_m) metadata_s use assert_m, only : assert implicit none diff --git a/src/inference_engine/network_configuration_m.f90 b/src/inference_engine/network_configuration_m.f90 index 5d2f34cc5..2822173bc 100644 --- a/src/inference_engine/network_configuration_m.f90 +++ b/src/inference_engine/network_configuration_m.f90 @@ -1,3 +1,5 @@ +! Copyright (c), The Regents of the University of California +! Terms of use are as specified in LICENSE.txt module network_configuration_m use julienne_string_m, only : string_t use double_precision_string_m, only : double_precision_string_t diff --git a/src/inference_engine/network_configuration_s.F90 b/src/inference_engine/network_configuration_s.F90 index 8100d9fa1..750ca0cc2 100644 --- a/src/inference_engine/network_configuration_s.F90 +++ b/src/inference_engine/network_configuration_s.F90 @@ -1,3 +1,5 @@ +! Copyright (c), The Regents of the University of California +! Terms of use are as specified in LICENSE.txt submodule(network_configuration_m) network_configuration_s use assert_m, only : assert use julienne_formats_m, only : csv diff --git a/src/inference_engine/training_configuration_m.f90 b/src/inference_engine/training_configuration_m.f90 index 2c610b295..3e5179d2f 100644 --- a/src/inference_engine/training_configuration_m.f90 +++ b/src/inference_engine/training_configuration_m.f90 @@ -1,3 +1,5 @@ +! Copyright (c), The Regents of the University of California +! Terms of use are as specified in LICENSE.txt module training_configuration_m use julienne_string_m, only : string_t use julienne_file_m, only : file_t diff --git a/src/inference_engine/training_configuration_s.F90 b/src/inference_engine/training_configuration_s.F90 index 9a9651e76..24b926ca1 100644 --- a/src/inference_engine/training_configuration_s.F90 +++ b/src/inference_engine/training_configuration_s.F90 @@ -1,3 +1,5 @@ +! Copyright (c), The Regents of the University of California +! Terms of use are as specified in LICENSE.txt submodule(training_configuration_m) training_configuration_s use assert_m, only : assert use inference_engine_m, only : gelu_t, relu_t, sigmoid_t, swish_t From d6154f7bd74fe87a7f434e145ceafa91e177f306 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Fri, 20 Sep 2024 18:01:01 -0700 Subject: [PATCH 068/105] doc: update subproject name in demo/fpm.toml --- demo/fpm.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo/fpm.toml b/demo/fpm.toml index da9e0206b..ae7a32a68 100644 --- a/demo/fpm.toml +++ b/demo/fpm.toml @@ -1,4 +1,4 @@ -name = "cloud-microphysics-trainer" +name = "Inference-Engine Demonstration Applications" license = "(Please see inference-engine/LICENSE.txt.)" author = "(Please see inference-engine/fpm.toml.)" maintainer = "(Please see inference-engine/fpm.toml.)" From f4a28c25421e27b11dfd421ef710544ea7bd38e1 Mon Sep 17 00:00:00 2001 From: Katherine Rasmussen Date: Mon, 23 Sep 2024 13:29:22 -0700 Subject: [PATCH 069/105] Remove unallowed whitespace from project name in `demo/fpm.toml` --- demo/fpm.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo/fpm.toml b/demo/fpm.toml index ae7a32a68..eefd579dd 100644 --- a/demo/fpm.toml +++ b/demo/fpm.toml @@ -1,4 +1,4 @@ -name = "Inference-Engine Demonstration Applications" +name = "Inference-Engine-Demonstration-Applications" license = "(Please see inference-engine/LICENSE.txt.)" author = "(Please see inference-engine/fpm.toml.)" maintainer = "(Please see inference-engine/fpm.toml.)" From ffd1c7caa5f2a9f3defb931580e22629554670cc Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Fri, 27 Sep 2024 05:49:18 -0700 Subject: [PATCH 070/105] fix(inference_engine_t): conforming real literal This commit fixes a non-standard-conforming declaration allowed by flang as an extension but reported as an error by nagfor. --- src/inference_engine/hyperparameters_m.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/inference_engine/hyperparameters_m.f90 b/src/inference_engine/hyperparameters_m.f90 index 7d485be73..d7fc5c464 100644 --- a/src/inference_engine/hyperparameters_m.f90 +++ b/src/inference_engine/hyperparameters_m.f90 @@ -11,7 +11,7 @@ module hyperparameters_m type hyperparameters_t(k) integer, kind :: k = default_real integer, private:: mini_batches_ = 10 - real(k), private :: learning_rate_ = 1.5_k + real(k), private :: learning_rate_ = real(1.5,k) character(len=:), allocatable :: optimizer_ contains generic :: to_json => default_real_to_json, double_precision_to_json From 5a06c4d3adf27a9e7ba4f598ffe645448ccdef89 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Wed, 9 Oct 2024 17:30:39 -0700 Subject: [PATCH 071/105] fix(sigmoid): make real literals double precision --- src/inference_engine/sigmoid_s.f90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/inference_engine/sigmoid_s.f90 b/src/inference_engine/sigmoid_s.f90 index ebbb4efbe..11d88dd6d 100644 --- a/src/inference_engine/sigmoid_s.f90 +++ b/src/inference_engine/sigmoid_s.f90 @@ -10,7 +10,7 @@ end procedure module procedure double_precision_activation - y = 1./(1.+exp(-x)) + y = 1.D0/(1.D0+exp(-x)) end procedure module procedure default_real_activation_derivative @@ -18,7 +18,7 @@ end procedure module procedure double_precision_activation_derivative - y = exp(-x)/(1.+exp(-x))**2 + y = exp(-x)/(1.D0+exp(-x))**2 end procedure module procedure function_name From 08f425404333959b794c79201ea7337d50296ec2 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Wed, 9 Oct 2024 20:30:34 -0700 Subject: [PATCH 072/105] fix(gelu): make constants double-precision --- src/inference_engine/gelu_s.f90 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/inference_engine/gelu_s.f90 b/src/inference_engine/gelu_s.f90 index c684a1721..6e926ff34 100644 --- a/src/inference_engine/gelu_s.f90 +++ b/src/inference_engine/gelu_s.f90 @@ -7,9 +7,9 @@ real, parameter :: half = 0.5, two = 2.D0 real, parameter :: sqrt_2_pi = sqrt(2*pi), sqrt_2 = sqrt(2.) - real, parameter :: pi_dp = 3.141592653589793D0 - real, parameter :: half_dp = 0.5D0, two_dp = 2.D0 - real, parameter :: sqrt_2_pi_dp = sqrt(two_dp*pi_dp), sqrt_2_dp = sqrt(2.D0) + double precision, parameter :: pi_dp = 3.141592653589793D0 + double precision, parameter :: half_dp = 0.5D0, two_dp = 2.D0 + double precision, parameter :: sqrt_2_pi_dp = sqrt(two_dp*pi_dp), sqrt_2_dp = sqrt(2.D0) contains From f848d4399ba718b7f4f5dac948a88658d6e84353 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Wed, 9 Oct 2024 14:35:20 -0700 Subject: [PATCH 073/105] feat: make inference non_overridable --- src/inference_engine/inference_engine_m_.f90 | 2 +- src/inference_engine/tensor_m.f90 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/inference_engine/inference_engine_m_.f90 b/src/inference_engine/inference_engine_m_.f90 index f87f1a3fd..b9b2a9c60 100644 --- a/src/inference_engine/inference_engine_m_.f90 +++ b/src/inference_engine/inference_engine_m_.f90 @@ -39,7 +39,7 @@ module inference_engine_m_ generic :: activation_function_name => default_real_activation_name, double_precision_activation_name generic :: to_exchange => default_real_to_exchange, double_precision_to_exchange procedure, private :: default_real_approximately_equal, double_precision_approximately_equal - procedure, private :: default_real_infer, double_precision_infer + procedure, private, non_overridable :: default_real_infer, double_precision_infer procedure, private :: default_real_to_json, double_precision_to_json procedure, private :: default_real_map_to_input_range, double_precision_map_to_input_range procedure, private :: default_real_map_from_output_range, double_precision_map_from_output_range diff --git a/src/inference_engine/tensor_m.f90 b/src/inference_engine/tensor_m.f90 index 859331f1c..425a899bb 100644 --- a/src/inference_engine/tensor_m.f90 +++ b/src/inference_engine/tensor_m.f90 @@ -12,7 +12,7 @@ module tensor_m real(k), allocatable, private :: values_(:) contains generic :: values => default_real_values, double_precision_values - procedure, private :: default_real_values, double_precision_values + procedure, private, non_overridable :: default_real_values, double_precision_values generic :: num_components => default_real_num_components, double_precision_num_components procedure, private :: default_real_num_components, double_precision_num_components end type From 6a1c601e60e842887b75e307d02be0e4a023823d Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Wed, 9 Oct 2024 22:48:43 -0700 Subject: [PATCH 074/105] refac(inference_engine): define activation_t --- src/inference_engine/activation_m.f90 | 73 ++++++++++++++ src/inference_engine/activation_s.f90 | 101 +++++++++++++++++++ src/inference_engine/inference_engine_m_.f90 | 2 + 3 files changed, 176 insertions(+) create mode 100644 src/inference_engine/activation_m.f90 create mode 100644 src/inference_engine/activation_s.f90 diff --git a/src/inference_engine/activation_m.f90 b/src/inference_engine/activation_m.f90 new file mode 100644 index 000000000..c2f51bf42 --- /dev/null +++ b/src/inference_engine/activation_m.f90 @@ -0,0 +1,73 @@ +module activation_m + use iso_c_binding, only : c_int + use julienne_m, only : string_t + implicit none + + private + public :: activation_t + public :: gelu, relu, sigmoid, step, swish + + enum, bind(C) + enumerator :: gelu, relu, sigmoid, step, swish + end enum + + type activation_t + private + integer(c_int) selection_ = sigmoid + contains + generic :: evaluate => default_real_evaluate , double_precision_evaluate + procedure, non_overridable :: default_real_evaluate , double_precision_evaluate + generic :: differentiate => default_real_differentiate, double_precision_differentiate + procedure, non_overridable :: default_real_differentiate, double_precision_differentiate + procedure, non_overridable :: function_name + end type + + interface activation_t + + elemental module function construct(selection) result(activation) + implicit none + integer(c_int), intent(in) :: selection + type(activation_t) activation + end function + + end interface + + interface + + elemental module function default_real_evaluate(self, x) result(y) + implicit none + class(activation_t), intent(in) :: self + real, intent(in) :: x + real y + end function + + elemental module function double_precision_evaluate(self, x) result(y) + implicit none + class(activation_t), intent(in) :: self + double precision, intent(in) :: x + double precision y + end function + + elemental module function default_real_differentiate(self, x) result(dy_dx) + implicit none + class(activation_t), intent(in) :: self + real, intent(in) :: x + real dy_dx + end function + + elemental module function double_precision_differentiate(self, x) result(dy_dx) + implicit none + class(activation_t), intent(in) :: self + double precision, intent(in) :: x + double precision dy_dx + end function + + elemental module function function_name(self) result(string) + implicit none + class(activation_t), intent(in) :: self + type(string_t) string + end function + + end interface + +end module activation_m diff --git a/src/inference_engine/activation_s.f90 b/src/inference_engine/activation_s.f90 new file mode 100644 index 000000000..63b672ef6 --- /dev/null +++ b/src/inference_engine/activation_s.f90 @@ -0,0 +1,101 @@ +submodule(activation_m) activation_s + implicit none + + real , parameter :: pi = 3.141592653589793 + double precision, parameter :: pi_dp = 3.141592653589793D0 + +contains + + module procedure default_real_evaluate + select case(self%selection_) + case(gelu) + y = .5*x*(1. + erf(x/sqrt(2.))) + case(relu) + y = max(0., x) + case(sigmoid) + y = 1./(1.+exp(-x)) + case(step) + y = merge(1., 0., x>0.) + case(swish) + associate(sigmoid_activation => 1./(1.+exp(-x))) + y = x*sigmoid_activation + end associate + case default + error stop "activation_s(default_real_evaluate): unknown activation" + end select + end procedure + + module procedure double_precision_evaluate + select case(self%selection_) + case(gelu) + y = .5D0*x*(1D0 + erf(x/sqrt(2D0))) + case(relu) + y = max(0D0, x) + case(sigmoid) + y = 1D0/(1D0+exp(-x)) + case(step) + y = merge(1D0, 0D0, x>0D0) + case(swish) + associate(sigmoid_activation => 1D0/(1D0+exp(-x))) + y = x*sigmoid_activation + end associate + case default + error stop "activation_s(double_precision_evaluate): unknown activation" + end select + end procedure + + module procedure default_real_differentiate + select case(self%selection_) + case(gelu) + dy_dx = .5*(1. + erf(x/sqrt(2.))) + x*exp(-x**2/2.)/sqrt(2*pi) + case(relu) + dy_dx = merge(1., 0., x>0.) + case(sigmoid) + dy_dx = exp(-x)/(1.+exp(-x))**2 + case(step) + error stop "activation_s(default_real_differentiate): non-differentiable activation" + case(swish) + associate(sigmoid_activation => 1./(1.+exp(-x)), sigmoid_differentiate => exp(-x)/(1.+exp(-x))**2) + dy_dx = sigmoid_activation + x * sigmoid_differentiate + end associate + case default + error stop "activation_s(default_real_differentiate): unknown activation" + end select + end procedure + + module procedure double_precision_differentiate + select case(self%selection_) + case(gelu) + dy_dx = .5D0*(1D0 + erf(x/sqrt(2D0))) + x*exp(-x**2/2D0)/sqrt(2D0*pi_dp) + case(relu) + dy_dx = merge(1D0, 0D0, x>0D0) + case(sigmoid) + dy_dx = exp(-x)/(1D0+exp(-x))**2 + case(step) + error stop "activation_s(double_precision_differentiate): non-differentiable activation" + case(swish) + associate(sigmoid_activation => 1D0/(1D0+exp(-x)), sigmoid_differentiate => exp(-x)/(1D0+exp(-x))**2) + dy_dx = sigmoid_activation + x * sigmoid_differentiate + end associate + case default + error stop "activation_s(double_precision_differentiate): unknown activation" + end select + end procedure + + module procedure function_name + select case(self%selection_) + case(gelu) + string = "gelu" + case(relu) + string = "relu" + case(sigmoid) + string = "sigmoid" + case(step) + string = "step" + case(swish) + string = "swish" + case default + error stop "activation_s(function_name): unknown activation" + end select + end procedure +end submodule activation_s diff --git a/src/inference_engine/inference_engine_m_.f90 b/src/inference_engine/inference_engine_m_.f90 index b9b2a9c60..0736f0d11 100644 --- a/src/inference_engine/inference_engine_m_.f90 +++ b/src/inference_engine/inference_engine_m_.f90 @@ -2,6 +2,7 @@ ! Terms of use are as specified in LICENSE.txt module inference_engine_m_ !! Define an abstraction that supports inference operationsn on a neural network + use activation_m, only : activation_t use activation_strategy_m, only : activation_strategy_t use double_precision_file_m, only : double_precision_file_t use kind_parameters_m, only : default_real, double_precision @@ -23,6 +24,7 @@ module inference_engine_m_ type(metadata_t), private :: metadata_ real(k), allocatable, private :: weights_(:,:,:), biases_(:,:) integer, allocatable, private :: nodes_(:) + type(activation_t), private :: activation_ class(activation_strategy_t), allocatable, private :: activation_strategy_ ! Strategy Pattern facilitates elemental activation contains generic :: operator(==) => default_real_approximately_equal, double_precision_approximately_equal From 3fe8fb74688706bdab2e9ea0e2628a1b2a4d1e6f Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Wed, 9 Oct 2024 23:11:59 -0700 Subject: [PATCH 075/105] feat(activation_t): add construct_from_name --- src/inference_engine/activation_m.f90 | 8 +++++++- src/inference_engine/activation_s.f90 | 21 +++++++++++++++++++++ src/inference_engine/inference_engine_s.F90 | 1 + 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/inference_engine/activation_m.f90 b/src/inference_engine/activation_m.f90 index c2f51bf42..a95294197 100644 --- a/src/inference_engine/activation_m.f90 +++ b/src/inference_engine/activation_m.f90 @@ -24,12 +24,18 @@ module activation_m interface activation_t - elemental module function construct(selection) result(activation) + elemental module function construct_from_component(selection) result(activation) implicit none integer(c_int), intent(in) :: selection type(activation_t) activation end function + elemental module function construct_from_name(name) result(activation) + implicit none + character(len=*), intent(in) :: name + type(activation_t) activation + end function + end interface interface diff --git a/src/inference_engine/activation_s.f90 b/src/inference_engine/activation_s.f90 index 63b672ef6..4c820d299 100644 --- a/src/inference_engine/activation_s.f90 +++ b/src/inference_engine/activation_s.f90 @@ -6,6 +6,27 @@ contains + module procedure construct_from_component + activation%selection_ = selection + end procedure + + module procedure construct_from_name + select case(name) + case("gelu") + activation%selection_ = gelu + case("relu") + activation%selection_ = relu + case("sigmoid") + activation%selection_ = sigmoid + case("step") + activation%selection_ = step + case("swish") + activation%selection_ = swish + case default + error stop "activation_s(construct_from_name): unknown name" + end select + end procedure + module procedure default_real_evaluate select case(self%selection_) case(gelu) diff --git a/src/inference_engine/inference_engine_s.F90 b/src/inference_engine/inference_engine_s.F90 index 64fde4e8c..201e7d013 100644 --- a/src/inference_engine/inference_engine_s.F90 +++ b/src/inference_engine/inference_engine_s.F90 @@ -323,6 +323,7 @@ impure function activation_factory_method(activation_name) result(activation) if (allocated(inference_engine%activation_strategy_)) deallocate(inference_engine%activation_strategy_) allocate(inference_engine%activation_strategy_, source = activation_factory_method(metadata(4)%string())) + inference_engine%activation_ = activation_t(metadata(4)%string()) call assert_consistency(inference_engine) From 8d6f3b89945bd7eb70b911f6d392a0a2d72a8076 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Thu, 10 Oct 2024 13:02:24 -0700 Subject: [PATCH 076/105] feat(tensor_map): mk map functions non_overridable --- src/inference_engine/tensor_map_m.f90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/inference_engine/tensor_map_m.f90 b/src/inference_engine/tensor_map_m.f90 index b11457030..b105f4613 100644 --- a/src/inference_engine/tensor_map_m.f90 +++ b/src/inference_engine/tensor_map_m.f90 @@ -16,9 +16,9 @@ module tensor_map_m real(k), dimension(:), allocatable, private :: intercept_, slope_ contains generic :: map_to_training_range => default_real_map_to_training_range, double_precision_map_to_training_range - procedure, private :: default_real_map_to_training_range, double_precision_map_to_training_range + procedure, private, non_overridable :: default_real_map_to_training_range, double_precision_map_to_training_range generic :: map_from_training_range => default_real_map_from_training_range, double_precision_map_from_training_range - procedure, private :: default_real_map_from_training_range, double_precision_map_from_training_range + procedure, private, non_overridable :: default_real_map_from_training_range, double_precision_map_from_training_range generic :: to_json => default_real_to_json, double_precision_to_json procedure, private :: default_real_to_json, double_precision_to_json generic :: operator(==) => default_real_equals, double_precision_equals From eba29a068976b7c0f5b4ec06cbf8383e5fedefed Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Thu, 10 Oct 2024 19:32:30 -0700 Subject: [PATCH 077/105] feat(trainable_engine): non-polymorphic activation --- example/learn-addition.F90 | 3 +- example/learn-exponentiation.F90 | 3 +- example/learn-microphysics-procedures.F90 | 3 +- example/learn-multiplication.F90 | 3 +- example/learn-power-series.F90 | 3 +- example/learn-saturated-mixing-ratio.F90 | 3 +- example/train-and-write.F90 | 3 +- src/inference_engine/activation_m.f90 | 11 ++++- src/inference_engine/activation_s.f90 | 4 ++ src/inference_engine/inference_engine_m_.f90 | 4 +- src/inference_engine/inference_engine_s.F90 | 51 +++++--------------- src/inference_engine/trainable_engine_m.F90 | 14 ++---- src/inference_engine/trainable_engine_s.F90 | 33 +++++-------- test/trainable_engine_test_m.F90 | 5 +- 14 files changed, 53 insertions(+), 90 deletions(-) diff --git a/example/learn-addition.F90 b/example/learn-addition.F90 index 8d5a35510..358e15fbb 100644 --- a/example/learn-addition.F90 +++ b/example/learn-addition.F90 @@ -142,8 +142,7 @@ function perturbed_identity_network(perturbation_magnitude) result(trainable_eng associate(w => identity + perturbation_magnitude*(w_harvest-0.5)/0.5, b => perturbation_magnitude*(b_harvest-0.5)/0.5) trainable_engine = trainable_engine_t( & - nodes = n, weights = w, biases = b, differentiable_activation_strategy = relu_t(), & - metadata = & + nodes = n, weights = w, biases = b, metadata = & [string_t("Perturbed Identity"), string_t("Damian Rouson"), string_t("2023-09-23"), string_t("relu"), string_t("false")] & ) diff --git a/example/learn-exponentiation.F90 b/example/learn-exponentiation.F90 index 9cdd0efba..3bcbe2b96 100644 --- a/example/learn-exponentiation.F90 +++ b/example/learn-exponentiation.F90 @@ -142,8 +142,7 @@ function perturbed_identity_network(perturbation_magnitude) result(trainable_eng associate(w => identity + perturbation_magnitude*(w_harvest-0.5)/0.5, b => perturbation_magnitude*(b_harvest-0.5)/0.5) trainable_engine = trainable_engine_t( & - nodes = n, weights = w, biases = b, differentiable_activation_strategy = relu_t(), & - metadata = & + nodes = n, weights = w, biases = b, metadata = & [string_t("Perturbed Identity"), string_t("Damian Rouson"), string_t("2023-09-23"), string_t("relu"), string_t("false")] & ) diff --git a/example/learn-microphysics-procedures.F90 b/example/learn-microphysics-procedures.F90 index fe4ac50e8..a76bfd2e8 100644 --- a/example/learn-microphysics-procedures.F90 +++ b/example/learn-microphysics-procedures.F90 @@ -190,8 +190,7 @@ function perturbed_identity_network(perturbation_magnitude, n) result(trainable_ associate(w => identity + perturbation_magnitude*(w_harvest-0.5)/0.5, b => perturbation_magnitude*(b_harvest-0.5)/0.5) trainable_engine = trainable_engine_t( & - nodes = n, weights = w, biases = b, differentiable_activation_strategy = sigmoid_t(), & - metadata = & + nodes = n, weights = w, biases = b, metadata = & [string_t("Thompson microphysics procedures"), string_t("Damian Rouson"), string_t("2023-09-23"), string_t("sigmoid"), & string_t("false")] & ) diff --git a/example/learn-multiplication.F90 b/example/learn-multiplication.F90 index 0c6a8349d..88d43633c 100644 --- a/example/learn-multiplication.F90 +++ b/example/learn-multiplication.F90 @@ -142,8 +142,7 @@ function perturbed_identity_network(perturbation_magnitude) result(trainable_eng associate(w => identity + perturbation_magnitude*(w_harvest-0.5)/0.5, b => perturbation_magnitude*(b_harvest-0.5)/0.5) trainable_engine = trainable_engine_t( & - nodes = n, weights = w, biases = b, differentiable_activation_strategy = relu_t(), & - metadata = & + nodes = n, weights = w, biases = b, metadata = & [string_t("Perturbed Identity"), string_t("Damian Rouson"), string_t("2023-09-23"), string_t("relu"), string_t("false")] & ) diff --git a/example/learn-power-series.F90 b/example/learn-power-series.F90 index e730fde1e..1fbe93973 100644 --- a/example/learn-power-series.F90 +++ b/example/learn-power-series.F90 @@ -144,8 +144,7 @@ function perturbed_identity_network(perturbation_magnitude) result(trainable_eng associate(w => identity + perturbation_magnitude*(w_harvest-0.5)/0.5, b => perturbation_magnitude*(b_harvest-0.5)/0.5) trainable_engine = trainable_engine_t( & - nodes = n, weights = w, biases = b, differentiable_activation_strategy = relu_t(), & - metadata = & + nodes = n, weights = w, biases = b, metadata = & [string_t("Perturbed Identity"), string_t("Damian Rouson"), string_t("2023-09-23"), string_t("relu"), string_t("false")] & ) diff --git a/example/learn-saturated-mixing-ratio.F90 b/example/learn-saturated-mixing-ratio.F90 index 1302bde97..9799c53d7 100644 --- a/example/learn-saturated-mixing-ratio.F90 +++ b/example/learn-saturated-mixing-ratio.F90 @@ -189,8 +189,7 @@ function perturbed_identity_network(perturbation_magnitude, n) result(trainable_ associate(w => identity + perturbation_magnitude*(w_harvest-0.5)/0.5, b => perturbation_magnitude*(b_harvest-0.5)/0.5) trainable_engine = trainable_engine_t( & - nodes = n, weights = w, biases = b, differentiable_activation_strategy = relu_t(), & - metadata = & + nodes = n, weights = w, biases = b, metadata = & [string_t("Saturated Mixing Ratio"), string_t("Damian Rouson"), string_t("2023-09-23"), string_t("relu"), & string_t("false")] & ) diff --git a/example/train-and-write.F90 b/example/train-and-write.F90 index d7e8b2b14..c770f088b 100644 --- a/example/train-and-write.F90 +++ b/example/train-and-write.F90 @@ -130,8 +130,7 @@ function perturbed_identity_network(perturbation_magnitude) result(trainable_eng associate(w => identity + perturbation_magnitude*(w_harvest-0.5)/0.5, b => perturbation_magnitude*(b_harvest-0.5)/0.5) trainable_engine = trainable_engine_t( & - nodes = nodes_per_layer, weights = w, biases = b, differentiable_activation_strategy = relu_t(), & - metadata = & + nodes = nodes_per_layer, weights = w, biases = b, metadata = & [string_t("Perturbed Identity"), string_t("Damian Rouson"), string_t("2023-09-23"), string_t("relu"), string_t("false")] & ) diff --git a/src/inference_engine/activation_m.f90 b/src/inference_engine/activation_m.f90 index a95294197..d326659db 100644 --- a/src/inference_engine/activation_m.f90 +++ b/src/inference_engine/activation_m.f90 @@ -15,11 +15,13 @@ module activation_m private integer(c_int) selection_ = sigmoid contains + procedure, non_overridable :: function_name + generic :: operator(==) => equals + procedure, private :: equals generic :: evaluate => default_real_evaluate , double_precision_evaluate procedure, non_overridable :: default_real_evaluate , double_precision_evaluate generic :: differentiate => default_real_differentiate, double_precision_differentiate procedure, non_overridable :: default_real_differentiate, double_precision_differentiate - procedure, non_overridable :: function_name end type interface activation_t @@ -40,6 +42,13 @@ elemental module function construct_from_name(name) result(activation) interface + elemental module function equals(self, rhs) result(self_eq_rhs) + implicit none + class(activation_t), intent(in) :: self + type(activation_t), intent(in) :: rhs + logical self_eq_rhs + end function + elemental module function default_real_evaluate(self, x) result(y) implicit none class(activation_t), intent(in) :: self diff --git a/src/inference_engine/activation_s.f90 b/src/inference_engine/activation_s.f90 index 4c820d299..d9721555e 100644 --- a/src/inference_engine/activation_s.f90 +++ b/src/inference_engine/activation_s.f90 @@ -10,6 +10,10 @@ activation%selection_ = selection end procedure + module procedure equals + self_eq_rhs = self%selection_ == rhs%selection_ + end procedure + module procedure construct_from_name select case(name) case("gelu") diff --git a/src/inference_engine/inference_engine_m_.f90 b/src/inference_engine/inference_engine_m_.f90 index 0736f0d11..d07f2779b 100644 --- a/src/inference_engine/inference_engine_m_.f90 +++ b/src/inference_engine/inference_engine_m_.f90 @@ -3,7 +3,6 @@ module inference_engine_m_ !! Define an abstraction that supports inference operationsn on a neural network use activation_m, only : activation_t - use activation_strategy_m, only : activation_strategy_t use double_precision_file_m, only : double_precision_file_t use kind_parameters_m, only : default_real, double_precision use julienne_m, only : file_t, string_t @@ -25,7 +24,6 @@ module inference_engine_m_ real(k), allocatable, private :: weights_(:,:,:), biases_(:,:) integer, allocatable, private :: nodes_(:) type(activation_t), private :: activation_ - class(activation_strategy_t), allocatable, private :: activation_strategy_ ! Strategy Pattern facilitates elemental activation contains generic :: operator(==) => default_real_approximately_equal, double_precision_approximately_equal generic :: infer => default_real_infer, double_precision_infer @@ -67,7 +65,7 @@ module inference_engine_m_ type(metadata_t) metadata_ real(k), allocatable :: weights_(:,:,:), biases_(:,:) integer, allocatable :: nodes_(:) - class(activation_strategy_t), allocatable :: activation_strategy_ ! Strategy Pattern facilitates elemental activation + type(activation_t) activation_ end type interface inference_engine_t diff --git a/src/inference_engine/inference_engine_s.F90 b/src/inference_engine/inference_engine_s.F90 index 201e7d013..566aadd7b 100644 --- a/src/inference_engine/inference_engine_s.F90 +++ b/src/inference_engine/inference_engine_s.F90 @@ -47,7 +47,7 @@ exchange%weights_ = self%weights_ exchange%biases_ = self%biases_ exchange%nodes_ = self%nodes_ - exchange%activation_strategy_ = self%activation_strategy_ + exchange%activation_ = self%activation_ end procedure module procedure double_precision_to_exchange @@ -59,7 +59,7 @@ exchange%weights_ = self%weights_ exchange%biases_ = self%biases_ exchange%nodes_ = self%nodes_ - exchange%activation_strategy_ = self%activation_strategy_ + exchange%activation_ = self%activation_ end procedure module procedure default_real_infer_unmapped @@ -80,7 +80,7 @@ do l = input_layer+1, output_layer associate(z => matmul(w(1:n(l),1:n(l-1),l), a(1:n(l-1),l-1)) + b(1:n(l),l)) if (l .lt. output_layer) then - a(1:n(l),l) = self%activation_strategy_%activation(z) + a(1:n(l),l) = self%activation_%evaluate(z) else a(1:n(l),l) = z(1:n(l)) end if @@ -111,7 +111,7 @@ do l = input_layer+1, output_layer associate(z => matmul(w(1:n(l),1:n(l-1),l), a(1:n(l-1),l-1)) + b(1:n(l),l)) if (l .lt. output_layer) then - a(1:n(l),l) = self%activation_strategy_%activation(z) + a(1:n(l),l) = self%activation_%evaluate(z) else a(1:n(l),l) = z(1:n(l)) end if @@ -151,7 +151,7 @@ feed_forward: & do l = input_layer+1, output_layer associate(z => matmul(w(1:n(l),1:n(l-1),l), a(1:n(l-1),l-1)) + b(1:n(l),l)) - a(1:n(l),l) = self%activation_strategy_%activation(z) + a(1:n(l),l) = self%activation_%evaluate(z) end associate end do feed_forward @@ -198,7 +198,7 @@ feed_forward: & do l = input_layer+1, output_layer associate(z => matmul(w(1:n(l),1:n(l-1),l), a(1:n(l-1),l-1)) + b(1:n(l),l)) - a(1:n(l),l) = self%activation_strategy_%activation(z) + a(1:n(l),l) = self%activation_%evaluate(z) end associate end do feed_forward @@ -227,7 +227,7 @@ pure subroutine default_real_consistency(self) integer, parameter :: input_layer=0 associate( & - all_allocated=>[allocated(self%weights_),allocated(self%biases_),allocated(self%nodes_),allocated(self%activation_strategy_)]& + all_allocated=>[allocated(self%weights_),allocated(self%biases_),allocated(self%nodes_)]& ) call assert(all(all_allocated),"inference_engine_s(inference_engine_consistency): fully_allocated", & intrinsic_array_t(all_allocated)) @@ -252,7 +252,7 @@ pure subroutine double_precision_consistency(self) integer, parameter :: input_layer=0 associate( & - all_allocated=>[allocated(self%weights_),allocated(self%biases_),allocated(self%nodes_),allocated(self%activation_strategy_)]& + all_allocated=>[allocated(self%weights_),allocated(self%biases_),allocated(self%nodes_)]& ) call assert(all(all_allocated),"inference_engine_s(inference_engine_consistency): fully_allocated", & intrinsic_array_t(all_allocated)) @@ -270,26 +270,6 @@ pure subroutine double_precision_consistency(self) end subroutine - impure function activation_factory_method(activation_name) result(activation) - character(len=*), intent(in) :: activation_name - class(activation_strategy_t), allocatable :: activation - - select case(activation_name) - case("swish") - activation = swish_t() - case("sigmoid") - activation = sigmoid_t() - case("step") - activation = step_t() - case("gelu") - activation = gelu_t() - case("relu") - activation = relu_t() - case default - error stop "inference_engine_s(activation_factory_method): unrecognized activation strategy '"//activation_name//"'" - end select - end function - module procedure default_real_construct_from_components inference_engine%metadata_ = metadata_t(metadata(1),metadata(2),metadata(3),metadata(4),metadata(5)) @@ -321,8 +301,6 @@ impure function activation_factory_method(activation_name) result(activation) end if end block - if (allocated(inference_engine%activation_strategy_)) deallocate(inference_engine%activation_strategy_) - allocate(inference_engine%activation_strategy_, source = activation_factory_method(metadata(4)%string())) inference_engine%activation_ = activation_t(metadata(4)%string()) call assert_consistency(inference_engine) @@ -360,9 +338,8 @@ impure function activation_factory_method(activation_name) result(activation) end if end block - if (allocated(inference_engine%activation_strategy_)) deallocate(inference_engine%activation_strategy_) associate(function_name => metadata%activation_name()) - allocate(inference_engine%activation_strategy_, source = activation_factory_method(function_name%string())) + inference_engine%activation_ = activation_t(function_name%string()) end associate call assert_consistency(inference_engine) @@ -692,9 +669,8 @@ impure function activation_factory_method(activation_name) result(activation) associate(metadata => metadata_t(lines(l : l + size(proto_meta%to_json()) - 1))) associate(metadata_strings => metadata%strings()) inference_engine = hidden_layers%inference_engine(metadata_strings, output_layer, input_map, output_map) - if (allocated(inference_engine%activation_strategy_)) deallocate(inference_engine%activation_strategy_) associate(function_name => metadata%activation_name()) - allocate(inference_engine%activation_strategy_, source = activation_factory_method(function_name%string())) + inference_engine%activation_ = activation_t(function_name%string()) end associate end associate end associate @@ -798,9 +774,8 @@ impure function activation_factory_method(activation_name) result(activation) associate(proto_meta => metadata_t(string_t(""),string_t(""),string_t(""),string_t(""),string_t(""))) associate(metadata => metadata_t(lines(l : l + size(proto_meta%to_json()) - 1))) inference_engine = hidden_layers%inference_engine(metadata, output_layer, input_map, output_map) - if (allocated(inference_engine%activation_strategy_)) deallocate(inference_engine%activation_strategy_) associate(function_name => metadata%activation_name()) - allocate(inference_engine%activation_strategy_, source = activation_factory_method(function_name%string())) + inference_engine%activation_ = activation_t(function_name%string()) end associate end associate end associate read_metadata @@ -822,7 +797,7 @@ impure function activation_factory_method(activation_name) result(activation) call assert(all(equal_shapes), "assert_conformable_with: all(equal_shapes)", intrinsic_array_t(equal_shapes)) end associate - call assert(same_type_as(self%activation_strategy_, inference_engine%activation_strategy_), "assert_conformable_with: types)") + call assert(self%activation_ == inference_engine%activation_, "assert_conformable_with: activation_") end procedure @@ -839,7 +814,7 @@ impure function activation_factory_method(activation_name) result(activation) call assert(all(equal_shapes), "assert_conformable_with: all(equal_shapes)", intrinsic_array_t(equal_shapes)) end associate - call assert(same_type_as(self%activation_strategy_, inference_engine%activation_strategy_), "assert_conformable_with: types)") + call assert(self%activation_ == inference_engine%activation_, "assert_conformable_with: activation_") end procedure diff --git a/src/inference_engine/trainable_engine_m.F90 b/src/inference_engine/trainable_engine_m.F90 index 13ae2baf8..2b6eb8467 100644 --- a/src/inference_engine/trainable_engine_m.F90 +++ b/src/inference_engine/trainable_engine_m.F90 @@ -2,9 +2,9 @@ ! Terms of use are as specified in LICENSE.txt module trainable_engine_m !! Define an abstraction that supports training a neural network + use activation_m, only : activation_t use julienne_string_m, only : string_t use inference_engine_m_, only : inference_engine_t - use differentiable_activation_strategy_m, only : differentiable_activation_strategy_t use kind_parameters_m, only : default_real use metadata_m, only : metadata_t use tensor_m, only : tensor_t @@ -21,9 +21,8 @@ module trainable_engine_m !! Encapsulate the information needed to perform training integer, kind :: k = default_real type(tensor_map_t(k)), private :: input_map_, output_map_ !! mappings to/from data ranges used during training - class(differentiable_activation_strategy_t), allocatable :: differentiable_activation_strategy_ + type(activation_t), private :: activation_ type(metadata_t) metadata_ !! metadata_ encapsulates strings for which default-kind suffices - !! stateless and thus not paremeterized; generic resolution supports different kinds real(k), allocatable :: w(:,:,:) !! weights real(k), allocatable :: b(:,:) !! biases integer, allocatable :: n(:) !! nodes per layer @@ -62,19 +61,14 @@ module trainable_engine_m interface trainable_engine_t #ifdef __INTEL_COMPILER - impure module function construct_trainable_engine_from_padded_arrays( & - nodes, weights, biases, differentiable_activation_strategy, metadata, input_map, output_map & - ) & + impure module function construct_trainable_engine_from_padded_arrays(nodes, weights, biases, metadata, input_map, output_map) & #else - impure module function construct_from_padded_arrays( & - nodes, weights, biases, differentiable_activation_strategy, metadata, input_map, output_map & - ) & + impure module function construct_from_padded_arrays( nodes, weights, biases, metadata, input_map, output_map) & #endif result(trainable_engine) implicit none integer, intent(in) :: nodes(input_layer:) real, intent(in) :: weights(:,:,:), biases(:,:) - class(differentiable_activation_strategy_t), intent(in) :: differentiable_activation_strategy type(string_t), intent(in) :: metadata(:) type(tensor_map_t), intent(in), optional :: input_map, output_map type(trainable_engine_t) trainable_engine diff --git a/src/inference_engine/trainable_engine_s.F90 b/src/inference_engine/trainable_engine_s.F90 index 1ce4f2d51..155a084cc 100644 --- a/src/inference_engine/trainable_engine_s.F90 +++ b/src/inference_engine/trainable_engine_s.F90 @@ -43,13 +43,7 @@ trainable_engine%w = exchange%weights_ trainable_engine%b = exchange%biases_ trainable_engine%n = exchange%nodes_ - select type(activation => exchange%activation_strategy_) - 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" - end select + trainable_engine%activation_ = exchange%activation_ #ifndef _CRAYFTN end associate #endif @@ -59,7 +53,7 @@ module procedure default_real_assert_consistent associate( & - fully_allocated=>[allocated(self%w),allocated(self%b),allocated(self%n),allocated(self%differentiable_activation_strategy_)] & + fully_allocated=>[allocated(self%w),allocated(self%b),allocated(self%n)] & ) call assert(all(fully_allocated),"trainable_engine_s(assert_consistent): fully_allocated",intrinsic_array_t(fully_allocated)) end associate @@ -100,9 +94,9 @@ feed_forward: & do l = 1,output_layer - a(1:n(l),l) = self%differentiable_activation_strategy_%activation( & - matmul(w(1:n(l),1:n(l-1),l), a(1:n(l-1),l-1)) + b(1:n(l),l) & - ) + associate(z=>matmul(w(1:n(l),1:n(l-1),l), a(1:n(l-1),l-1)) + b(1:n(l),l)) + a(1:n(l),l) = self%activation_%evaluate(z) + end associate end do feed_forward associate(normalized_outputs => tensor_t(a(1:n(output_layer), output_layer))) @@ -213,22 +207,21 @@ feed_forward: & do l = 1,output_layer z(1:n(l),l) = matmul(w(1:n(l),1:n(l-1),l), a(1:n(l-1),l-1)) + b(1:n(l),l) - a(1:n(l),l) = self%differentiable_activation_strategy_%activation(z(1:n(l),l)) + a(1:n(l),l) = self%activation_%evaluate(z(1:n(l),l)) end do feed_forward associate(y => expected_outputs(pair)%values()) if (present(cost)) pair_cost(pair) = sum((y(1:n(output_layer))-a(1:n(output_layer),output_layer))**2) - delta(1:n(output_layer),output_layer) = & - (a(1:n(output_layer),output_layer) - y(1:n(output_layer))) & - * self%differentiable_activation_strategy_%activation_derivative(z(1:n(output_layer),output_layer)) + delta(1:n(output_layer),output_layer) = (a(1:n(output_layer),output_layer) - y(1:n(output_layer))) & + * self%activation_%differentiate(z(1:n(output_layer),output_layer)) end associate associate(n_hidden => self%num_layers()-2) back_propagate_error: & do l = n_hidden,1,-1 delta(1:n(l),l) = matmul(transpose(w(1:n(l+1),1:n(l),l+1)), delta(1:n(l+1),l+1)) & - * self%differentiable_activation_strategy_%activation_derivative(z(1:n(l),l)) + * self%activation_%differentiate(z(1:n(l),l)) end do back_propagate_error end associate @@ -325,7 +318,7 @@ trainable_engine%n = nodes trainable_engine%w = weights trainable_engine%b = biases - trainable_engine%differentiable_activation_strategy_ = differentiable_activation_strategy + trainable_engine%activation_ = activation_t(metadata(4)%string()) block integer i @@ -370,12 +363,10 @@ associate( & w => identity + perturbation_magnitude*(w_harvest-0.5)/0.5, & - b => perturbation_magnitude*(b_harvest-0.5)/0.5, & - activation => training_configuration%differentiable_activation_strategy() & + b => perturbation_magnitude*(b_harvest-0.5)/0.5 & ) trainable_engine = trainable_engine_t( & - nodes = n, weights = w, biases = b, differentiable_activation_strategy = activation, metadata = metadata, & - input_map = input_map, output_map = output_map & + nodes = n, weights = w, biases = b, metadata = metadata, input_map = input_map, output_map = output_map & ) end associate end associate diff --git a/test/trainable_engine_test_m.F90 b/test/trainable_engine_test_m.F90 index 23a7fd694..b6a363ba3 100644 --- a/test/trainable_engine_test_m.F90 +++ b/test/trainable_engine_test_m.F90 @@ -184,7 +184,7 @@ function two_zeroed_hidden_layers() result(trainable_engine) b = 0. trainable_engine = trainable_engine_t( & - nodes = neurons, weights = w, biases = b, differentiable_activation_strategy = sigmoid_t(), metadata = & + nodes = neurons, weights = w, biases = b, metadata = & [string_t("2-hide|3-wide"), string_t("Damian Rouson"), string_t("2023-06-30"), string_t("sigmoid"), string_t("false")] & ) end function @@ -202,7 +202,7 @@ function two_random_hidden_layers() result(trainable_engine) w = 2*w trainable_engine = trainable_engine_t( & - nodes = neurons, weights = w, biases = b, differentiable_activation_strategy = sigmoid_t(), metadata = & + nodes = neurons, weights = w, biases = b, metadata = & [string_t("2-hide|3-wide"), string_t("Damian Rouson"), string_t("2023-06-30"), string_t("sigmoid"), string_t("false")] & ) end function @@ -424,7 +424,6 @@ function perturbed_identity_network(perturbation_magnitude) result(trainable_eng nodes = nodes_per_layer, & weights = identity + harvest , & biases = reshape([real:: [0,0], [0,0], [0,0]], [max_n, layers-1]), & - differentiable_activation_strategy = relu_t(), & metadata = [string_t("Identity"), string_t("Damian Rouson"), string_t("2023-09-18"), string_t("relu"), string_t("false")] & ) end function From 63ab0414fd52bd8e51282044ed63bcd65dc57d30 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Fri, 11 Oct 2024 21:48:54 -0700 Subject: [PATCH 078/105] refac: rm activation_strategy class hierachy --- example/learn-addition.F90 | 2 +- example/learn-exponentiation.F90 | 2 +- example/learn-microphysics-procedures.F90 | 2 +- example/learn-multiplication.F90 | 2 +- example/learn-power-series.F90 | 2 +- example/learn-saturated-mixing-ratio.F90 | 2 +- example/read-query-infer.f90 | 2 +- example/train-and-write.F90 | 2 +- example/write-read-infer.F90 | 2 +- src/inference_engine/activation_s.f90 | 1 + .../activation_strategy_m.f90 | 46 ---------------- .../differentiable_activation_strategy_m.f90 | 17 ------ src/inference_engine/gelu_m.f90 | 52 ------------------- src/inference_engine/gelu_s.f90 | 36 ------------- src/inference_engine/inference_engine_s.F90 | 5 -- src/inference_engine/relu_m.f90 | 52 ------------------- src/inference_engine/relu_s.f90 | 28 ---------- src/inference_engine/sigmoid_m.f90 | 52 ------------------- src/inference_engine/sigmoid_s.f90 | 28 ---------- src/inference_engine/step_m.f90 | 39 -------------- src/inference_engine/step_s.f90 | 20 ------- src/inference_engine/swish_m.f90 | 52 ------------------- src/inference_engine/swish_s.f90 | 33 ------------ .../training_configuration_m.f90 | 16 +++--- .../training_configuration_s.F90 | 26 +++++----- src/inference_engine_m.f90 | 7 --- test/trainable_engine_test_m.F90 | 2 +- 27 files changed, 31 insertions(+), 499 deletions(-) delete mode 100644 src/inference_engine/activation_strategy_m.f90 delete mode 100644 src/inference_engine/differentiable_activation_strategy_m.f90 delete mode 100644 src/inference_engine/gelu_m.f90 delete mode 100644 src/inference_engine/gelu_s.f90 delete mode 100644 src/inference_engine/relu_m.f90 delete mode 100644 src/inference_engine/relu_s.f90 delete mode 100644 src/inference_engine/sigmoid_m.f90 delete mode 100644 src/inference_engine/sigmoid_s.f90 delete mode 100644 src/inference_engine/step_m.f90 delete mode 100644 src/inference_engine/step_s.f90 delete mode 100644 src/inference_engine/swish_m.f90 delete mode 100644 src/inference_engine/swish_s.f90 diff --git a/example/learn-addition.F90 b/example/learn-addition.F90 index 358e15fbb..0e2efdf3f 100644 --- a/example/learn-addition.F90 +++ b/example/learn-addition.F90 @@ -21,7 +21,7 @@ elemental function y(x_tensor) result(a_tensor) program learn_addition !! This trains a neural network to learn the following six polynomial functions of its eight inputs. use inference_engine_m, only : & - inference_engine_t, trainable_engine_t, mini_batch_t, tensor_t, input_output_pair_t, shuffle, relu_t + inference_engine_t, trainable_engine_t, mini_batch_t, tensor_t, input_output_pair_t, shuffle use julienne_m, only : string_t, file_t, command_line_t, bin_t use assert_m, only : assert, intrinsic_array_t use addition_m, only : y diff --git a/example/learn-exponentiation.F90 b/example/learn-exponentiation.F90 index 3bcbe2b96..6b8ba0624 100644 --- a/example/learn-exponentiation.F90 +++ b/example/learn-exponentiation.F90 @@ -21,7 +21,7 @@ elemental function y(x_tensor) result(a_tensor) program learn_exponentiation !! This trains a neural network to learn the following six polynomial functions of its eight inputs. use inference_engine_m, only : & - inference_engine_t, trainable_engine_t, mini_batch_t, tensor_t, input_output_pair_t, shuffle, relu_t + inference_engine_t, trainable_engine_t, mini_batch_t, tensor_t, input_output_pair_t, shuffle use julienne_m, only : string_t, file_t, command_line_t, bin_t use assert_m, only : assert, intrinsic_array_t use exponentiation_m, only : y diff --git a/example/learn-microphysics-procedures.F90 b/example/learn-microphysics-procedures.F90 index a76bfd2e8..552bd9ac0 100644 --- a/example/learn-microphysics-procedures.F90 +++ b/example/learn-microphysics-procedures.F90 @@ -4,7 +4,7 @@ program learn_microphysics_procedures !! Train a neural network proxies for procedures in the Thompson microphysics model !! in of ICAR (https://github.com/BerkeleyLab/icar). use inference_engine_m, only : & - inference_engine_t, trainable_engine_t, mini_batch_t, tensor_t, input_output_pair_t, shuffle, sigmoid_t + inference_engine_t, trainable_engine_t, mini_batch_t, tensor_t, input_output_pair_t, shuffle use julienne_m, only : string_t, file_t, command_line_t, bin_t, csv use assert_m, only : assert, intrinsic_array_t use thompson_tensors_m, only : y, T, p diff --git a/example/learn-multiplication.F90 b/example/learn-multiplication.F90 index 88d43633c..797335dd9 100644 --- a/example/learn-multiplication.F90 +++ b/example/learn-multiplication.F90 @@ -21,7 +21,7 @@ elemental function y(x_tensor) result(a_tensor) program learn_multiplication !! This trains a neural network to learn the following six polynomial functions of its eight inputs. use inference_engine_m, only : & - inference_engine_t, trainable_engine_t, mini_batch_t, tensor_t, input_output_pair_t, shuffle, relu_t + inference_engine_t, trainable_engine_t, mini_batch_t, tensor_t, input_output_pair_t, shuffle use julienne_m, only : string_t, file_t, command_line_t, bin_t use assert_m, only : assert, intrinsic_array_t use multiply_inputs, only : y diff --git a/example/learn-power-series.F90 b/example/learn-power-series.F90 index 1fbe93973..c0490669e 100644 --- a/example/learn-power-series.F90 +++ b/example/learn-power-series.F90 @@ -21,7 +21,7 @@ elemental function y(x_in) result(a) program learn_power_series !! This trains a neural network to learn the following six polynomial functions of its eight inputs. use inference_engine_m, only : & - inference_engine_t, trainable_engine_t, mini_batch_t, tensor_t, input_output_pair_t, shuffle, relu_t + inference_engine_t, trainable_engine_t, mini_batch_t, tensor_t, input_output_pair_t, shuffle use julienne_m, only : string_t, file_t, command_line_t, bin_t use assert_m, only : assert, intrinsic_array_t use power_series, only : y diff --git a/example/learn-saturated-mixing-ratio.F90 b/example/learn-saturated-mixing-ratio.F90 index 9799c53d7..ba8bd599c 100644 --- a/example/learn-saturated-mixing-ratio.F90 +++ b/example/learn-saturated-mixing-ratio.F90 @@ -3,7 +3,7 @@ program train_saturated_mixture_ratio !! This program trains a neural network to learn the saturated mixing ratio function of ICAR. use inference_engine_m, only : & - inference_engine_t, trainable_engine_t, mini_batch_t, tensor_t, input_output_pair_t, shuffle, relu_t + inference_engine_t, trainable_engine_t, mini_batch_t, tensor_t, input_output_pair_t, shuffle use julienne_m, only : string_t, file_t, command_line_t, bin_t, csv use assert_m, only : assert, intrinsic_array_t use saturated_mixing_ratio_m, only : y, T, p diff --git a/example/read-query-infer.f90 b/example/read-query-infer.f90 index 45a593914..86889192b 100644 --- a/example/read-query-infer.f90 +++ b/example/read-query-infer.f90 @@ -4,7 +4,7 @@ program read_query_infer !! This program demonstrates how to read a neural network from a JSON file, !! query the network object for some of its properties, print those properties, !! and use the network to perform inference. - use inference_engine_m, only : inference_engine_t, relu_t, tensor_t + use inference_engine_m, only : inference_engine_t, tensor_t use julienne_m, only : string_t, command_line_t, file_t implicit none diff --git a/example/train-and-write.F90 b/example/train-and-write.F90 index c770f088b..04df61a55 100644 --- a/example/train-and-write.F90 +++ b/example/train-and-write.F90 @@ -9,7 +9,7 @@ program train_and_write !! The initial condition corresponds to the desired network with all weights and biases perturbed by a random variable !! that is uniformly distributed on the range [0,0.1]. use inference_engine_m, only : & - inference_engine_t, trainable_engine_t, mini_batch_t, tensor_t, input_output_pair_t, shuffle, relu_t + inference_engine_t, trainable_engine_t, mini_batch_t, tensor_t, input_output_pair_t, shuffle use julienne_m, only : string_t, file_t, command_line_t, bin_t use assert_m, only : assert, intrinsic_array_t implicit none diff --git a/example/write-read-infer.F90 b/example/write-read-infer.F90 index f6880cd3a..8786d4b24 100644 --- a/example/write-read-infer.F90 +++ b/example/write-read-infer.F90 @@ -7,7 +7,7 @@ program write_read_infer !! perform inference. The network performs an identity mapping from any !! non-negative inputs to the corresponding outputs using a RELU activation !! function. - use inference_engine_m, only : inference_engine_t, relu_t, tensor_t + use inference_engine_m, only : inference_engine_t, tensor_t use julienne_m, only : string_t, command_line_t, file_t implicit none diff --git a/src/inference_engine/activation_s.f90 b/src/inference_engine/activation_s.f90 index d9721555e..0e346f7a8 100644 --- a/src/inference_engine/activation_s.f90 +++ b/src/inference_engine/activation_s.f90 @@ -123,4 +123,5 @@ error stop "activation_s(function_name): unknown activation" end select end procedure + end submodule activation_s diff --git a/src/inference_engine/activation_strategy_m.f90 b/src/inference_engine/activation_strategy_m.f90 deleted file mode 100644 index 8bca0a026..000000000 --- a/src/inference_engine/activation_strategy_m.f90 +++ /dev/null @@ -1,46 +0,0 @@ -! Copyright (c), The Regents of the University of California -! Terms of use are as specified in LICENSE.txt -module activation_strategy_m - - ! External dependencies - use julienne_string_m, only : string_t - implicit none - - private - public :: activation_strategy_t - public :: default_real_activation_i - public :: double_precision_activation_i - public :: function_name_i - - type, abstract :: activation_strategy_t - contains - procedure(default_real_activation_i), nopass, deferred :: default_real_activation - procedure(double_precision_activation_i), nopass, deferred :: double_precision_activation - generic :: activation => default_real_activation, double_precision_activation - procedure(function_name_i), deferred :: function_name - end type - - abstract interface - - elemental function default_real_activation_i(x) result(y) - implicit none - real, intent(in) :: x - real y - end function - - elemental function double_precision_activation_i(x) result(y) - implicit none - double precision, intent(in) :: x - double precision y - end function - - elemental function function_name_i(self) result(string) - import string_t, activation_strategy_t - implicit none - class(activation_strategy_t), intent(in) :: self - type(string_t) string - end function - - end interface - -end module activation_strategy_m diff --git a/src/inference_engine/differentiable_activation_strategy_m.f90 b/src/inference_engine/differentiable_activation_strategy_m.f90 deleted file mode 100644 index b1e226472..000000000 --- a/src/inference_engine/differentiable_activation_strategy_m.f90 +++ /dev/null @@ -1,17 +0,0 @@ -! Copyright (c), The Regents of the University of California -! Terms of use are as specified in LICENSE.txt -module differentiable_activation_strategy_m - use activation_strategy_m, only : activation_strategy_t, default_real_activation_i, double_precision_activation_i - implicit none - - private - public :: differentiable_activation_strategy_t - - type, extends(activation_strategy_t), abstract :: differentiable_activation_strategy_t - contains - procedure(default_real_activation_i), nopass, deferred :: default_real_activation_derivative - procedure(double_precision_activation_i), nopass, deferred :: double_precision_activation_derivative - generic :: activation_derivative => default_real_activation_derivative, double_precision_activation_derivative - end type - -end module differentiable_activation_strategy_m diff --git a/src/inference_engine/gelu_m.f90 b/src/inference_engine/gelu_m.f90 deleted file mode 100644 index 51a872c46..000000000 --- a/src/inference_engine/gelu_m.f90 +++ /dev/null @@ -1,52 +0,0 @@ -! Copyright (c), The Regents of the University of California -! Terms of use are as specified in LICENSE.txt -module gelu_m - use differentiable_activation_strategy_m, only : differentiable_activation_strategy_t - use julienne_string_m, only : string_t - implicit none - - private - public :: gelu_t - - type, extends(differentiable_activation_strategy_t) :: gelu_t - contains - procedure, nopass :: default_real_activation, double_precision_activation - procedure, nopass :: default_real_activation_derivative, double_precision_activation_derivative - procedure :: function_name - end type - - interface - - elemental module function default_real_activation(x) result(y) - implicit none - real, intent(in) :: x - real y - end function - - elemental module function double_precision_activation(x) result(y) - implicit none - double precision, intent(in) :: x - double precision y - end function - - elemental module function default_real_activation_derivative(x) result(y) - implicit none - real, intent(in) :: x - real y - end function - - elemental module function double_precision_activation_derivative(x) result(y) - implicit none - double precision, intent(in) :: x - double precision y - end function - - elemental module function function_name(self) result(string) - implicit none - class(gelu_t), intent(in) :: self - type(string_t) string - end function - - end interface - -end module gelu_m diff --git a/src/inference_engine/gelu_s.f90 b/src/inference_engine/gelu_s.f90 deleted file mode 100644 index 6e926ff34..000000000 --- a/src/inference_engine/gelu_s.f90 +++ /dev/null @@ -1,36 +0,0 @@ -! Copyright (c), The Regents of the University of California -! Terms of use are as specified in LICENSE.txt -submodule(gelu_m) gelu_s - implicit none - - real, parameter :: pi = 3.141592653589793 - real, parameter :: half = 0.5, two = 2.D0 - real, parameter :: sqrt_2_pi = sqrt(2*pi), sqrt_2 = sqrt(2.) - - double precision, parameter :: pi_dp = 3.141592653589793D0 - double precision, parameter :: half_dp = 0.5D0, two_dp = 2.D0 - double precision, parameter :: sqrt_2_pi_dp = sqrt(two_dp*pi_dp), sqrt_2_dp = sqrt(2.D0) - -contains - - module procedure default_real_activation - y = half*x*(1. + erf(x/sqrt_2)) - end procedure - - module procedure double_precision_activation - y = half_dp*x*(1.D0 + erf(x/sqrt_2_dp)) - end procedure - - module procedure default_real_activation_derivative - y = half*(1. + erf(x/sqrt_2)) + x*exp(-x**2/two)/sqrt_2_pi - end procedure - - module procedure double_precision_activation_derivative - y = half_dp*(1.D0 + erf(x/sqrt_2_dp)) + x*exp(-x**2/2.D0)/sqrt_2_pi_dp - end procedure - - module procedure function_name - string = string_t("gelu") - end procedure - -end submodule gelu_s diff --git a/src/inference_engine/inference_engine_s.F90 b/src/inference_engine/inference_engine_s.F90 index 566aadd7b..9050888d3 100644 --- a/src/inference_engine/inference_engine_s.F90 +++ b/src/inference_engine/inference_engine_s.F90 @@ -3,14 +3,9 @@ submodule(inference_engine_m_) inference_engine_s use assert_m, only : assert, intrinsic_array_t use double_precision_string_m, only : double_precision_string_t - use gelu_m, only : gelu_t use kind_parameters_m, only : double_precision use layer_m, only : layer_t use neuron_m, only : neuron_t - use relu_m, only : relu_t - use step_m, only : step_t - use swish_m, only : swish_t - use sigmoid_m, only : sigmoid_t implicit none interface assert_consistency diff --git a/src/inference_engine/relu_m.f90 b/src/inference_engine/relu_m.f90 deleted file mode 100644 index d537dee01..000000000 --- a/src/inference_engine/relu_m.f90 +++ /dev/null @@ -1,52 +0,0 @@ -! Copyright (c), The Regents of the University of California -! Terms of use are as specified in LICENSE.txt -module relu_m - use differentiable_activation_strategy_m, only : differentiable_activation_strategy_t - use julienne_string_m, only : string_t - implicit none - - private - public :: relu_t - - type, extends(differentiable_activation_strategy_t) :: relu_t - contains - procedure, nopass :: default_real_activation, double_precision_activation - procedure, nopass :: default_real_activation_derivative, double_precision_activation_derivative - procedure :: function_name - end type - - interface - - elemental module function default_real_activation(x) result(y) - implicit none - real, intent(in) :: x - real y - end function - - elemental module function double_precision_activation(x) result(y) - implicit none - double precision, intent(in) :: x - double precision y - end function - - elemental module function default_real_activation_derivative(x) result(y) - implicit none - real, intent(in) :: x - real y - end function - - elemental module function double_precision_activation_derivative(x) result(y) - implicit none - double precision, intent(in) :: x - double precision y - end function - - elemental module function function_name(self) result(string) - implicit none - class(relu_t), intent(in) :: self - type(string_t) string - end function - - end interface - -end module relu_m diff --git a/src/inference_engine/relu_s.f90 b/src/inference_engine/relu_s.f90 deleted file mode 100644 index 29c3980e5..000000000 --- a/src/inference_engine/relu_s.f90 +++ /dev/null @@ -1,28 +0,0 @@ -! Copyright (c), The Regents of the University of California -! Terms of use are as specified in LICENSE.txt -submodule(relu_m) relu_s - implicit none - -contains - - module procedure default_real_activation - y = max(0., x) - end procedure - - module procedure double_precision_activation - y = max(0.D0, x) - end procedure - - module procedure default_real_activation_derivative - y = merge(1., 0., x>0.) - end procedure - - module procedure double_precision_activation_derivative - y = merge(1.D0, 0.D0, x>0.D0) - end procedure - - module procedure function_name - string = string_t("relu") - end procedure - -end submodule relu_s diff --git a/src/inference_engine/sigmoid_m.f90 b/src/inference_engine/sigmoid_m.f90 deleted file mode 100644 index 64160bc73..000000000 --- a/src/inference_engine/sigmoid_m.f90 +++ /dev/null @@ -1,52 +0,0 @@ -! Copyright (c), The Regents of the University of California -! Terms of use are as specified in LICENSE.txt -module sigmoid_m - use differentiable_activation_strategy_m, only : differentiable_activation_strategy_t - use julienne_string_m, only : string_t - implicit none - - private - public :: sigmoid_t - - type, extends(differentiable_activation_strategy_t) :: sigmoid_t - contains - procedure, nopass :: default_real_activation, double_precision_activation - procedure, nopass :: default_real_activation_derivative, double_precision_activation_derivative - procedure :: function_name - end type - - interface - - elemental module function default_real_activation(x) result(y) - implicit none - real, intent(in) :: x - real y - end function - - elemental module function double_precision_activation(x) result(y) - implicit none - double precision, intent(in) :: x - double precision y - end function - - elemental module function default_real_activation_derivative(x) result(y) - implicit none - real, intent(in) :: x - real y - end function - - elemental module function double_precision_activation_derivative(x) result(y) - implicit none - double precision, intent(in) :: x - double precision y - end function - - elemental module function function_name(self) result(string) - implicit none - class(sigmoid_t), intent(in) :: self - type(string_t) string - end function - - end interface - -end module sigmoid_m diff --git a/src/inference_engine/sigmoid_s.f90 b/src/inference_engine/sigmoid_s.f90 deleted file mode 100644 index 11d88dd6d..000000000 --- a/src/inference_engine/sigmoid_s.f90 +++ /dev/null @@ -1,28 +0,0 @@ -! Copyright (c), The Regents of the University of California -! Terms of use are as specified in LICENSE.txt -submodule(sigmoid_m) sigmoid_s - implicit none - -contains - - module procedure default_real_activation - y = 1./(1.+exp(-x)) - end procedure - - module procedure double_precision_activation - y = 1.D0/(1.D0+exp(-x)) - end procedure - - module procedure default_real_activation_derivative - y = exp(-x)/(1.+exp(-x))**2 - end procedure - - module procedure double_precision_activation_derivative - y = exp(-x)/(1.D0+exp(-x))**2 - end procedure - - module procedure function_name - string = string_t("sigmoid") - end procedure - -end submodule sigmoid_s diff --git a/src/inference_engine/step_m.f90 b/src/inference_engine/step_m.f90 deleted file mode 100644 index 90abab9cb..000000000 --- a/src/inference_engine/step_m.f90 +++ /dev/null @@ -1,39 +0,0 @@ -! Copyright (c), The Regents of the University of California -! Terms of use are as specified in LICENSE.txt -module step_m - use activation_strategy_m, only : activation_strategy_t - use julienne_string_m, only : string_t - implicit none - - private - public :: step_t - - type, extends(activation_strategy_t) :: step_t - contains - procedure, nopass :: default_real_activation, double_precision_activation - procedure :: function_name - end type - - interface - - elemental module function default_real_activation(x) result(y) - implicit none - real, intent(in) :: x - real y - end function - - elemental module function double_precision_activation(x) result(y) - implicit none - double precision, intent(in) :: x - double precision y - end function - - elemental module function function_name(self) result(string) - implicit none - class(step_t), intent(in) :: self - type(string_t) string - end function - - end interface - -end module step_m diff --git a/src/inference_engine/step_s.f90 b/src/inference_engine/step_s.f90 deleted file mode 100644 index e98d8d756..000000000 --- a/src/inference_engine/step_s.f90 +++ /dev/null @@ -1,20 +0,0 @@ -! Copyright (c), The Regents of the University of California -! Terms of use are as specified in LICENSE.txt -submodule(step_m) step_s - implicit none - -contains - - module procedure default_real_activation - y = merge(1., 0., x>0.) - end procedure - - module procedure double_precision_activation - y = merge(1.D0, 0.D0, x>0.D0) - end procedure - - module procedure function_name - string = string_t("step") - end procedure - -end submodule step_s diff --git a/src/inference_engine/swish_m.f90 b/src/inference_engine/swish_m.f90 deleted file mode 100644 index 476764004..000000000 --- a/src/inference_engine/swish_m.f90 +++ /dev/null @@ -1,52 +0,0 @@ -! Copyright (c), The Regents of the University of California -! Terms of use are as specified in LICENSE.txt -module swish_m - use differentiable_activation_strategy_m, only : differentiable_activation_strategy_t - use julienne_string_m, only : string_t - implicit none - - private - public :: swish_t - - type, extends(differentiable_activation_strategy_t) :: swish_t - contains - procedure, nopass :: default_real_activation , double_precision_activation - procedure, nopass :: default_real_activation_derivative, double_precision_activation_derivative - procedure :: function_name - end type - - interface - - elemental module function default_real_activation(x) result(y) - implicit none - real, intent(in) :: x - real y - end function - - elemental module function double_precision_activation(x) result(y) - implicit none - double precision, intent(in) :: x - double precision y - end function - - elemental module function default_real_activation_derivative(x) result(y) - implicit none - real , intent(in) :: x - real y - end function - - elemental module function double_precision_activation_derivative(x) result(y) - implicit none - double precision , intent(in) :: x - double precision y - end function - - elemental module function function_name(self) result(string) - implicit none - class(swish_t), intent(in) :: self - type(string_t) string - end function - - end interface - -end module swish_m diff --git a/src/inference_engine/swish_s.f90 b/src/inference_engine/swish_s.f90 deleted file mode 100644 index d85f020cf..000000000 --- a/src/inference_engine/swish_s.f90 +++ /dev/null @@ -1,33 +0,0 @@ -! Copyright (c), The Regents of the University of California -! Terms of use are as specified in LICENSE.txt -submodule(swish_m) swish_s - use sigmoid_m, only : sigmoid_t - implicit none - -contains - - module procedure default_real_activation - type(sigmoid_t) sigmoid - y = x*sigmoid%activation(x) - end procedure - - module procedure double_precision_activation - type(sigmoid_t) sigmoid - y = x*sigmoid%activation(x) - end procedure - - module procedure default_real_activation_derivative - type(sigmoid_t) sigmoid - y = sigmoid%activation(x) + x * sigmoid%activation_derivative(x) - end procedure - - module procedure double_precision_activation_derivative - type(sigmoid_t) sigmoid - y = sigmoid%activation(x) + x * sigmoid%activation_derivative(x) - end procedure - - module procedure function_name - string = string_t("swish") - end procedure - -end submodule swish_s diff --git a/src/inference_engine/training_configuration_m.f90 b/src/inference_engine/training_configuration_m.f90 index 3e5179d2f..eb20a30f0 100644 --- a/src/inference_engine/training_configuration_m.f90 +++ b/src/inference_engine/training_configuration_m.f90 @@ -1,12 +1,12 @@ ! Copyright (c), The Regents of the University of California ! Terms of use are as specified in LICENSE.txt module training_configuration_m + use activation_m, only : activation_t use julienne_string_m, only : string_t use julienne_file_m, only : file_t use hyperparameters_m, only : hyperparameters_t use network_configuration_m, only : network_configuration_t use kind_parameters_m, only : default_real, double_precision - use differentiable_activation_strategy_m, only : differentiable_activation_strategy_t use double_precision_file_m, only : double_precision_file_t implicit none @@ -28,10 +28,8 @@ module training_configuration_m procedure, private :: default_real_optimizer_name, double_precision_optimizer_name generic :: learning_rate => default_real_learning_rate, double_precision_learning_rate procedure, private :: default_real_learning_rate, double_precision_learning_rate - generic :: differentiable_activation_strategy => & - default_real_differentiable_activation_strategy, double_precision_differentiable_activation_strategy - procedure, private :: & - default_real_differentiable_activation_strategy, double_precision_differentiable_activation_strategy + generic :: differentiable_activation => default_real_differentiable_activation, double_precision_differentiable_activation + procedure, private :: default_real_differentiable_activation, double_precision_differentiable_activation generic :: nodes_per_layer => default_real_nodes_per_layer, double_precision_nodes_per_layer procedure, private :: default_real_nodes_per_layer, double_precision_nodes_per_layer generic :: skip_connections => default_real_skip_connections, double_precision_skip_connections @@ -130,16 +128,16 @@ elemental module function double_precision_learning_rate(self) result(rate) double precision rate end function - module function default_real_differentiable_activation_strategy(self) result(strategy) + module function default_real_differentiable_activation(self) result(activation) implicit none class(training_configuration_t), intent(in) :: self - class(differentiable_activation_strategy_t), allocatable :: strategy + type(activation_t) activation end function - module function double_precision_differentiable_activation_strategy(self) result(strategy) + module function double_precision_differentiable_activation(self) result(activation) implicit none class(training_configuration_t(double_precision)), intent(in) :: self - class(differentiable_activation_strategy_t), allocatable :: strategy + type(activation_t) activation end function pure module function default_real_nodes_per_layer(self) result(nodes) diff --git a/src/inference_engine/training_configuration_s.F90 b/src/inference_engine/training_configuration_s.F90 index 24b926ca1..3b982a202 100644 --- a/src/inference_engine/training_configuration_s.F90 +++ b/src/inference_engine/training_configuration_s.F90 @@ -2,8 +2,8 @@ ! Terms of use are as specified in LICENSE.txt submodule(training_configuration_m) training_configuration_s use assert_m, only : assert - use inference_engine_m, only : gelu_t, relu_t, sigmoid_t, swish_t use double_precision_string_m, only : double_precision_string_t + use activation_m, only : activation_t, gelu, relu, sigmoid, swish implicit none character(len=*), parameter :: header="{", footer="}", separator = "," @@ -181,7 +181,7 @@ using_skip = self%network_configuration_%skip_connections() end procedure - module procedure default_real_differentiable_activation_strategy + module procedure default_real_differentiable_activation #if defined __INTEL_COMPILER || _CRAYFTN type(string_t) :: activation_name activation_name = self%network_configuration_%activation_name() @@ -190,15 +190,15 @@ #endif select case(activation_name%string()) case ("gelu") - strategy = gelu_t() + activation = activation_t(gelu) case ("relu") - strategy = relu_t() + activation = activation_t(relu) case ("sigmoid") - strategy = sigmoid_t() + activation = activation_t(sigmoid) case ("swish") - strategy = swish_t() + activation = activation_t(swish) case default - error stop 'activation_strategy_factory_s(factory): unrecognized activation name "' // activation_name%string() // '"' + error stop 'activation_factory_s(factory): unrecognized activation name "' // activation_name%string() // '"' end select #if defined __INTEL_COMPILER || _CRAYFTN #else @@ -206,7 +206,7 @@ #endif end procedure - module procedure double_precision_differentiable_activation_strategy + module procedure double_precision_differentiable_activation #if defined __INTEL_COMPILER || _CRAYFTN type(string_t) :: activation_name activation_name = self%network_configuration_%activation_name() @@ -215,15 +215,15 @@ #endif select case(activation_name%string()) case ("gelu") - strategy = gelu_t() + activation = activation_t(gelu) case ("relu") - strategy = relu_t() + activation = activation_t(relu) case ("sigmoid") - strategy = sigmoid_t() + activation = activation_t(sigmoid) case ("swish") - strategy = swish_t() + activation = activation_t(swish) case default - error stop 'activation_strategy_factory_s(factory): unrecognized activation name "' // activation_name%string() // '"' + error stop 'activation_factory_s(factory): unrecognized activation name "' // activation_name%string() // '"' end select #if defined __INTEL_COMPILER || _CRAYFTN #else diff --git a/src/inference_engine_m.f90 b/src/inference_engine_m.f90 index 2b9c2a040..22f505671 100644 --- a/src/inference_engine_m.f90 +++ b/src/inference_engine_m.f90 @@ -2,11 +2,8 @@ ! Terms of use are as specified in LICENSE.txt module inference_engine_m !! Specify the user-facing modules, derived types, and type parameters - use activation_strategy_m, only : activation_strategy_t - use differentiable_activation_strategy_m, only : differentiable_activation_strategy_t use double_precision_file_m, only : double_precision_file_t use double_precision_string_m, only : double_precision_string_t - use gelu_m, only : gelu_t use hyperparameters_m, only : hyperparameters_t use input_output_pair_m, only : input_output_pair_t, shuffle, write_to_stdout use inference_engine_m_, only : inference_engine_t, unmapped_engine_t @@ -14,10 +11,6 @@ module inference_engine_m use metadata_m, only : metadata_t use mini_batch_m, only : mini_batch_t use network_configuration_m, only : network_configuration_t - use relu_m, only : relu_t - use sigmoid_m, only : sigmoid_t - use step_m, only : step_t - use swish_m, only : swish_t use tensor_m, only : tensor_t use tensor_map_m, only : tensor_map_t use trainable_engine_m, only : trainable_engine_t diff --git a/test/trainable_engine_test_m.F90 b/test/trainable_engine_test_m.F90 index b6a363ba3..929d40ea6 100644 --- a/test/trainable_engine_test_m.F90 +++ b/test/trainable_engine_test_m.F90 @@ -12,7 +12,7 @@ module trainable_engine_test_m #endif ! Internal dependencies - use inference_engine_m, only : trainable_engine_t, tensor_t, sigmoid_t, input_output_pair_t, mini_batch_t, relu_t, shuffle + use inference_engine_m, only : trainable_engine_t, tensor_t, input_output_pair_t, mini_batch_t, shuffle implicit none private From 1c1e4872900ca3f98101ac4daa390f517391ae73 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Fri, 11 Oct 2024 22:53:59 -0700 Subject: [PATCH 079/105] refac(activation_s): simplify function_name getter --- src/inference_engine/activation_m.f90 | 6 ++++-- src/inference_engine/activation_s.f90 | 27 ++++++++++----------------- 2 files changed, 14 insertions(+), 19 deletions(-) diff --git a/src/inference_engine/activation_m.f90 b/src/inference_engine/activation_m.f90 index d326659db..3566fbff8 100644 --- a/src/inference_engine/activation_m.f90 +++ b/src/inference_engine/activation_m.f90 @@ -8,9 +8,11 @@ module activation_m public :: gelu, relu, sigmoid, step, swish enum, bind(C) - enumerator :: gelu, relu, sigmoid, step, swish + enumerator :: gelu=1, relu, sigmoid, step, swish end enum - + + character(len=*), parameter :: activation_name(*) = [character(len("sigmoid")) :: "gelu", "relu", "sigmoid", "step", "swish"] + type activation_t private integer(c_int) selection_ = sigmoid diff --git a/src/inference_engine/activation_s.f90 b/src/inference_engine/activation_s.f90 index 0e346f7a8..876417de9 100644 --- a/src/inference_engine/activation_s.f90 +++ b/src/inference_engine/activation_s.f90 @@ -1,4 +1,5 @@ submodule(activation_m) activation_s + use assert_m, only : assert implicit none real , parameter :: pi = 3.141592653589793 @@ -14,6 +15,15 @@ self_eq_rhs = self%selection_ == rhs%selection_ end procedure + module procedure function_name + + call assert( lbound(activation_name,1) <= self%selection_ .and. self%selection_ <= ubound(activation_name,1), & + "activation_s(function_name): bounds") + + string = string_t(trim(activation_name(self%selection_))) + + end procedure + module procedure construct_from_name select case(name) case("gelu") @@ -107,21 +117,4 @@ end select end procedure - module procedure function_name - select case(self%selection_) - case(gelu) - string = "gelu" - case(relu) - string = "relu" - case(sigmoid) - string = "sigmoid" - case(step) - string = "step" - case(swish) - string = "swish" - case default - error stop "activation_s(function_name): unknown activation" - end select - end procedure - end submodule activation_s From d9a90731b4025f45ca58bc4ba94361b9de625bda Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Fri, 11 Oct 2024 23:05:28 -0700 Subject: [PATCH 080/105] refac(engines): make component constructors pure --- src/inference_engine/inference_engine_m_.f90 | 8 ++++---- src/inference_engine/trainable_engine_m.F90 | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/inference_engine/inference_engine_m_.f90 b/src/inference_engine/inference_engine_m_.f90 index d07f2779b..daeeccf8e 100644 --- a/src/inference_engine/inference_engine_m_.f90 +++ b/src/inference_engine/inference_engine_m_.f90 @@ -70,7 +70,7 @@ module inference_engine_m_ interface inference_engine_t - impure module function default_real_construct_from_components(metadata, weights, biases, nodes, input_map, output_map) & + module function default_real_construct_from_components(metadata, weights, biases, nodes, input_map, output_map) & result(inference_engine) implicit none type(string_t), intent(in) :: metadata(:) @@ -80,7 +80,7 @@ impure module function default_real_construct_from_components(metadata, weights, type(inference_engine_t) inference_engine end function - impure module function double_precision_construct_from_components(metadata, weights, biases, nodes, input_map, output_map) & + module function double_precision_construct_from_components(metadata, weights, biases, nodes, input_map, output_map) & result(inference_engine) implicit none type(metadata_t), intent(in) :: metadata @@ -163,13 +163,13 @@ elemental module function double_precision_map_from_output_range(self, normalize type(tensor_t(double_precision)) tensor end function - impure module function default_real_to_exchange(self) result(exchange) + module function default_real_to_exchange(self) result(exchange) implicit none class(inference_engine_t), intent(in) :: self type(exchange_t) exchange end function - impure module function double_precision_to_exchange(self) result(exchange) + module function double_precision_to_exchange(self) result(exchange) implicit none class(inference_engine_t(double_precision)), intent(in) :: self type(exchange_t(double_precision)) exchange diff --git a/src/inference_engine/trainable_engine_m.F90 b/src/inference_engine/trainable_engine_m.F90 index 2b6eb8467..6a0a46f9e 100644 --- a/src/inference_engine/trainable_engine_m.F90 +++ b/src/inference_engine/trainable_engine_m.F90 @@ -61,9 +61,9 @@ module trainable_engine_m interface trainable_engine_t #ifdef __INTEL_COMPILER - impure module function construct_trainable_engine_from_padded_arrays(nodes, weights, biases, metadata, input_map, output_map) & + module function construct_trainable_engine_from_padded_arrays(nodes, weights, biases, metadata, input_map, output_map) & #else - impure module function construct_from_padded_arrays( nodes, weights, biases, metadata, input_map, output_map) & + module function construct_from_padded_arrays( nodes, weights, biases, metadata, input_map, output_map) & #endif result(trainable_engine) implicit none @@ -74,7 +74,7 @@ impure module function construct_from_padded_arrays( nodes, weights, biases, met type(trainable_engine_t) trainable_engine end function - impure module function construct_from_inference_engine(inference_engine) result(trainable_engine) + module function construct_from_inference_engine(inference_engine) result(trainable_engine) implicit none type(inference_engine_t), intent(in) :: inference_engine type(trainable_engine_t) trainable_engine From 6ef2d1cb8a1d0d9c52450743d6b3682ebb23cae6 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Fri, 11 Oct 2024 23:21:38 -0700 Subject: [PATCH 081/105] feat: non_overridble for inference & training --- src/inference_engine/input_output_pair_m.f90 | 8 ++++---- src/inference_engine/mini_batch_m.f90 | 2 +- src/inference_engine/trainable_engine_m.F90 | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/inference_engine/input_output_pair_m.f90 b/src/inference_engine/input_output_pair_m.f90 index fcbfab493..047f2ba5c 100644 --- a/src/inference_engine/input_output_pair_m.f90 +++ b/src/inference_engine/input_output_pair_m.f90 @@ -14,10 +14,10 @@ module input_output_pair_m integer, kind :: k = default_real type(tensor_t(k)), private :: inputs_, expected_outputs_ contains - generic :: inputs => default_real_inputs, double_precision_inputs - procedure, private :: default_real_inputs, double_precision_inputs - generic :: expected_outputs => default_real_expected_outputs, double_precision_expected_outputs - procedure, private :: default_real_expected_outputs, double_precision_expected_outputs + generic :: inputs => default_real_inputs, double_precision_inputs + procedure, private, non_overridable :: default_real_inputs, double_precision_inputs + generic :: expected_outputs => default_real_expected_outputs, double_precision_expected_outputs + procedure, private, non_overridable :: default_real_expected_outputs, double_precision_expected_outputs end type interface input_output_pair_t diff --git a/src/inference_engine/mini_batch_m.f90 b/src/inference_engine/mini_batch_m.f90 index 10fb0b567..7bb44d7cb 100644 --- a/src/inference_engine/mini_batch_m.f90 +++ b/src/inference_engine/mini_batch_m.f90 @@ -13,7 +13,7 @@ module mini_batch_m type(input_output_pair_t(k)), private, allocatable :: input_output_pairs_(:) contains generic :: input_output_pairs => default_real_input_output_pairs, double_precision_input_output_pairs - procedure :: default_real_input_output_pairs, double_precision_input_output_pairs + procedure, non_overridable :: default_real_input_output_pairs, double_precision_input_output_pairs end type interface mini_batch_t diff --git a/src/inference_engine/trainable_engine_m.F90 b/src/inference_engine/trainable_engine_m.F90 index 6a0a46f9e..b7acc6e83 100644 --- a/src/inference_engine/trainable_engine_m.F90 +++ b/src/inference_engine/trainable_engine_m.F90 @@ -30,8 +30,8 @@ module trainable_engine_m real(k), allocatable, dimension(:,:,:) :: dcdw, vdw, sdw, vdwc, sdwc real(k), allocatable, dimension(:,:) :: z, delta, dcdb, vdb, sdb, vdbc, sdbc contains - generic :: train => default_real_train - procedure, private :: default_real_train + generic :: train => default_real_train + procedure, private, non_overridable :: default_real_train generic :: predict => default_real_predict generic :: infer => default_real_predict procedure, private :: default_real_predict @@ -40,8 +40,8 @@ module trainable_engine_m generic :: num_layers => default_real_num_layers procedure, private :: default_real_num_layers generic :: num_inputs => default_real_num_inputs - procedure, private :: default_real_num_inputs - generic :: num_outputs => default_real_num_outputs + procedure, private, non_overridable :: default_real_num_inputs + generic :: num_outputs => default_real_num_outputs procedure, private :: default_real_num_outputs generic :: to_inference_engine => default_real_to_inference_engine procedure, private :: default_real_to_inference_engine From 9958bb781fd99a8f08dc981dcd997a2ad68189f4 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Sun, 13 Oct 2024 17:00:36 -0700 Subject: [PATCH 082/105] feat(trainable_network): extend inference_engine_t --- src/inference_engine/inference_engine_m_.f90 | 77 +++++-- src/inference_engine/inference_engine_s.F90 | 205 ++++++++++++++++++- src/inference_engine/trainable_engine_s.F90 | 1 - src/inference_engine/trainable_network_m.f90 | 42 ++++ src/inference_engine/trainable_network_s.f90 | 15 ++ src/inference_engine/workspace_s.f90 | 117 +++++++++++ src/inference_engine_m.f90 | 1 + 7 files changed, 434 insertions(+), 24 deletions(-) create mode 100644 src/inference_engine/trainable_network_m.f90 create mode 100644 src/inference_engine/trainable_network_s.f90 create mode 100644 src/inference_engine/workspace_s.f90 diff --git a/src/inference_engine/inference_engine_m_.f90 b/src/inference_engine/inference_engine_m_.f90 index daeeccf8e..a196fd6ee 100644 --- a/src/inference_engine/inference_engine_m_.f90 +++ b/src/inference_engine/inference_engine_m_.f90 @@ -7,6 +7,7 @@ module inference_engine_m_ use kind_parameters_m, only : default_real, double_precision use julienne_m, only : file_t, string_t use metadata_m, only : metadata_t + use mini_batch_m, only : mini_batch_t use tensor_m, only : tensor_t use tensor_map_m, only : tensor_map_t implicit none @@ -15,6 +16,7 @@ module inference_engine_m_ public :: inference_engine_t public :: unmapped_engine_t public :: exchange_t + public :: workspace_t type inference_engine_t(k) !! Encapsulate the minimal information needed to perform inference @@ -38,19 +40,33 @@ module inference_engine_m_ generic :: skip => default_real_skip, double_precision_skip generic :: activation_function_name => default_real_activation_name, double_precision_activation_name generic :: to_exchange => default_real_to_exchange, double_precision_to_exchange - procedure, private :: default_real_approximately_equal, double_precision_approximately_equal + generic :: learn => default_real_learn + procedure, private, non_overridable :: default_real_approximately_equal, double_precision_approximately_equal procedure, private, non_overridable :: default_real_infer, double_precision_infer - procedure, private :: default_real_to_json, double_precision_to_json - procedure, private :: default_real_map_to_input_range, double_precision_map_to_input_range - procedure, private :: default_real_map_from_output_range, double_precision_map_from_output_range - procedure, private :: default_real_num_hidden_layers, double_precision_num_hidden_layers - procedure, private :: default_real_num_inputs, double_precision_num_inputs - procedure, private :: default_real_num_outputs, double_precision_num_outputs - procedure, private :: default_real_nodes_per_layer, double_precision_nodes_per_layer - procedure, private :: default_real_assert_conformable_with, double_precision_assert_conformable_with - procedure, private :: default_real_skip, double_precision_skip - procedure, private :: default_real_activation_name, double_precision_activation_name - procedure, private :: default_real_to_exchange, double_precision_to_exchange + procedure, private, non_overridable :: default_real_learn + procedure, private, non_overridable :: default_real_to_json, double_precision_to_json + procedure, private, non_overridable :: default_real_map_to_input_range, double_precision_map_to_input_range + procedure, private, non_overridable :: default_real_map_from_output_range, double_precision_map_from_output_range + procedure, private, non_overridable :: default_real_num_hidden_layers, double_precision_num_hidden_layers + procedure, private, non_overridable :: default_real_num_inputs, double_precision_num_inputs + procedure, private, non_overridable :: default_real_num_outputs, double_precision_num_outputs + procedure, private, non_overridable :: default_real_nodes_per_layer, double_precision_nodes_per_layer + procedure, private, non_overridable :: default_real_assert_conformable_with, double_precision_assert_conformable_with + procedure, private, non_overridable :: default_real_skip, double_precision_skip + procedure, private, non_overridable :: default_real_activation_name, double_precision_activation_name + procedure, private, non_overridable :: default_real_to_exchange, double_precision_to_exchange + end type + + type workspace_t(k) + integer, kind :: k = default_real + real(k), allocatable, dimension(:,:) :: a + real(k), allocatable, dimension(:,:,:) :: dcdw, vdw, sdw, vdwc, sdwc + real(k), allocatable, dimension(:,:) :: z, delta, dcdb, vdb, sdb, vdbc, sdbc + contains + generic :: fully_allocated => default_real_allocated + generic :: allocate_if_necessary => default_real_allocate + procedure, non_overridable, private :: default_real_allocated + procedure, non_overridable, private :: default_real_allocate end type type, extends(inference_engine_t) :: unmapped_engine_t @@ -115,8 +131,35 @@ impure elemental module function double_precision_unmapped_from_json(file) resul end interface + interface workspace_t + + pure module function default_real_workspace(inference_engine) result(workspace) + implicit none + type(inference_engine_t), intent(in) :: inference_engine + type(workspace_t) workspace + end function + + end interface + interface + module subroutine default_real_allocate(self, inference_engine) + implicit none + class(workspace_t), intent(inout) :: self + type(inference_engine_t), intent(in) :: inference_engine + end subroutine + + pure module function default_real_allocated(self) result(all_allocated) + implicit none + class(workspace_t), intent(in) :: self + logical all_allocated + end function + + end interface + + + interface ! inference_engine_t type-bound procedures + elemental module function default_real_approximately_equal(lhs, rhs) result(lhs_eq_rhs) !! The result is true if lhs and rhs are the same to within a tolerance implicit none @@ -299,6 +342,16 @@ pure module function double_precision_skip(self) result(use_skip_connections) logical use_skip_connections end function + pure module subroutine default_real_learn(self, mini_batches_arr, cost, adam, learning_rate, workspace) + implicit none + class(inference_engine_t), intent(inout) :: self + type(mini_batch_t), intent(in) :: mini_batches_arr(:) + real, intent(out), allocatable, optional :: cost(:) + logical, intent(in) :: adam + real, intent(in) :: learning_rate + type(workspace_t), intent(inout) :: workspace + end subroutine + end interface end module inference_engine_m_ diff --git a/src/inference_engine/inference_engine_s.F90 b/src/inference_engine/inference_engine_s.F90 index 9050888d3..ce8b5c7ea 100644 --- a/src/inference_engine/inference_engine_s.F90 +++ b/src/inference_engine/inference_engine_s.F90 @@ -14,6 +14,7 @@ end interface character(len=*), parameter :: acceptable_engine_tag = "0.13.0" ! git tag capable of reading the current json file format + integer, parameter :: input_layer = 0 contains @@ -60,7 +61,6 @@ module procedure default_real_infer_unmapped real, allocatable :: a(:,:) - integer, parameter :: input_layer = 0 integer l call assert_consistency(self) @@ -91,7 +91,6 @@ module procedure double_precision_infer_unmapped double precision, allocatable :: a(:,:) - integer, parameter :: input_layer = 0 integer l call assert_consistency(self) @@ -122,7 +121,6 @@ module procedure default_real_infer real, allocatable :: a(:,:) - integer, parameter :: input_layer = 0 integer l call assert_consistency(self) @@ -169,7 +167,6 @@ module procedure double_precision_infer double precision, allocatable :: a(:,:) - integer, parameter :: input_layer = 0 integer l call assert_consistency(self) @@ -219,8 +216,6 @@ pure subroutine default_real_consistency(self) class(inference_engine_t), intent(in) :: self - integer, parameter :: input_layer=0 - associate( & all_allocated=>[allocated(self%weights_),allocated(self%biases_),allocated(self%nodes_)]& ) @@ -244,7 +239,6 @@ pure subroutine double_precision_consistency(self) class(inference_engine_t(double_precision)), intent(in) :: self - integer, parameter :: input_layer=0 associate( & all_allocated=>[allocated(self%weights_),allocated(self%biases_),allocated(self%nodes_)]& @@ -906,18 +900,18 @@ pure subroutine double_precision_consistency(self) end procedure module procedure default_real_num_hidden_layers - integer, parameter :: input_layer = 1, output_layer = 1 + integer, parameter :: num_non_hidden_layers = 2 call assert_consistency(self) associate(num_layers => size(self%nodes_)) - hidden_layer_count = num_layers - (input_layer + output_layer) + hidden_layer_count = num_layers - num_non_hidden_layers end associate end procedure module procedure double_precision_num_hidden_layers - integer, parameter :: input_layer = 1, output_layer = 1 + integer, parameter :: num_non_hidden_layers = 2 call assert_consistency(self) associate(num_layers => size(self%nodes_)) - hidden_layer_count = num_layers - (input_layer + output_layer) + hidden_layer_count = num_layers - num_non_hidden_layers end associate end procedure @@ -965,4 +959,193 @@ pure subroutine double_precision_consistency(self) end associate end procedure + module procedure default_real_learn + integer l, batch, mini_batch_size, pair + type(tensor_t), allocatable :: inputs(:), expected_outputs(:) + + call assert_consistency(self) + call assert(workspace%fully_allocated(), "inference_engine_s(default_real_learn): workspace%fully_allocated()") + + associate(output_layer => ubound(self%nodes_,1)) + + associate( & + dcdw => workspace%dcdw, vdw => workspace%vdw, sdw => workspace%sdw , vdwc => workspace%vdwc, sdwc => workspace%sdwc & + ,dcdb => workspace%dcdb, vdb => workspace%vdb, sdb => workspace%sdb , vdbc => workspace%vdbc, sdbc => workspace%sdbc & + ,a => workspace%a , z => workspace%z , delta => workspace%delta & + ) + vdw = 0.; sdw = 1.; vdb = 0.; sdb = 1. + + associate(w => self%weights_, b => self%biases_, n => self%nodes_, num_mini_batches => size(mini_batches_arr)) + + if (present(cost)) allocate(cost(num_mini_batches)) + + iterate_across_batches: & + do batch = 1, num_mini_batches + + dcdw = 0.; dcdb = 0. + +#ifndef _CRAYFTN + associate(input_output_pairs => mini_batches_arr(batch)%input_output_pairs()) +#else + block + type(input_output_pair_t), allocatable :: input_output_pairs(:) + input_output_pairs = mini_batches_arr(batch)%input_output_pairs() +#endif + inputs = input_output_pairs%inputs() + expected_outputs = input_output_pairs%expected_outputs() + mini_batch_size = size(input_output_pairs) +#ifndef _CRAYFTN + end associate +#else + end block +#endif + sum_cost: & + block + real, allocatable :: pair_cost(:) + if (present(cost)) allocate(pair_cost(mini_batch_size)) + +#if F2023_LOCALITY + iterate_through_batch: & + do concurrent (pair = 1:mini_batch_size) local(a,z,delta) reduce(+: dcdb, dcdw) + +#elif F2018_LOCALITY + + reduce_gradients: & + block + real reduce_dcdb(size(dcdb,1),size(dcdb,2),mini_batch_size) + real reduce_dcdw(size(dcdw,1),size(dcdw,2),size(dcdw,3),mini_batch_size) + reduce_dcdb = 0. + reduce_dcdw = 0. + + iterate_through_batch: & + do concurrent (pair = 1:mini_batch_size) local(a,z,delta) + +#else + + reduce_gradients: & + block + real reduce_dcdb(size(dcdb,1),size(dcdb,2),mini_batch_size) + real reduce_dcdw(size(dcdw,1),size(dcdw,2),size(dcdw,3),mini_batch_size) + reduce_dcdb = 0. + reduce_dcdw = 0. + + iterate_through_batch: & + do concurrent (pair = 1:mini_batch_size) + + iteration: & + block + + real a(maxval(self%nodes_), input_layer:output_layer) ! Activations + real z(size(b,1),size(b,2)), delta(size(b,1),size(b,2)) +#endif + + a(1:self%num_inputs(), input_layer) = inputs(pair)%values() + + feed_forward: & + do l = 1,output_layer + z(1:n(l),l) = matmul(w(1:n(l),1:n(l-1),l), a(1:n(l-1),l-1)) + b(1:n(l),l) ! z_j^l = sum_k(w_jk^{l} a_k^{l-1}) + b_j^l + a(1:n(l),l) = self%activation_%evaluate(z(1:n(l),l)) + end do feed_forward + + associate(y => expected_outputs(pair)%values()) + if (present(cost)) pair_cost(pair) = sum((y(1:n(output_layer))-a(1:n(output_layer),output_layer))**2) + + delta(1:n(output_layer),output_layer) = (a(1:n(output_layer),output_layer) - y(1:n(output_layer))) & + * self%activation_%differentiate(z(1:n(output_layer),output_layer)) + end associate + + associate(n_hidden => self%num_hidden_layers()) + back_propagate_error: & + do l = n_hidden,1,-1 + delta(1:n(l),l) = matmul(transpose(w(1:n(l+1),1:n(l),l+1)), delta(1:n(l+1),l+1)) & + * self%activation_%differentiate(z(1:n(l),l)) + end do back_propagate_error + end associate + + + + block + integer j + sum_gradients: & + do l = 1,output_layer +#if F2023_LOCALITY + dcdb(1:n(l),l) = dcdb(1:n(l),l) + delta(1:n(l),l) + do concurrent(j = 1:n(l)) reduce(+: dcdw) + dcdw(j,1:n(l-1),l) = dcdw(j,1:n(l-1),l) + a(1:n(l-1),l-1)*delta(j,l) + end do +#else + reduce_dcdb(1:n(l),l,pair) = reduce_dcdb(1:n(l),l,pair) + delta(1:n(l),l) + do j = 1,n(l) + reduce_dcdw(j,1:n(l-1),l,pair) = reduce_dcdw(j,1:n(l-1),l,pair) + a(1:n(l-1),l-1)*delta(j,l) + end do +#endif + end do sum_gradients + end block + +#if F2023_LOCALITY + end do iterate_through_batch +#elif F2018_LOCALITY + + end do iterate_through_batch + dcdb = sum(reduce_dcdb,dim=3) + dcdw = sum(reduce_dcdw,dim=4) + + end block reduce_gradients +#else + end block iteration + end do iterate_through_batch + dcdb = sum(reduce_dcdb,dim=3) + dcdw = sum(reduce_dcdw,dim=4) + + end block reduce_gradients +#endif + + if (present(cost)) cost(batch) = sum(pair_cost)/(2*mini_batch_size) + end block sum_cost + + if (adam) then + adam: & + block + ! Adam parameters + real, parameter :: beta(*) = [.9, .999] + real, parameter :: obeta(*) = [1.- beta(1), 1.- beta(2)] + real, parameter :: epsilon = 1.E-08 + + associate(alpha => learning_rate) + adam_adjust_weights_and_biases: & + do concurrent(l = 1:output_layer) + dcdw(1:n(l),1:n(l-1),l) = dcdw(1:n(l),1:n(l-1),l)/(mini_batch_size) + vdw(1:n(l),1:n(l-1),l) = beta(1)*vdw(1:n(l),1:n(l-1),l) + obeta(1)*dcdw(1:n(l),1:n(l-1),l) + sdw (1:n(l),1:n(l-1),l) = beta(2)*sdw(1:n(l),1:n(l-1),l) + obeta(2)*(dcdw(1:n(l),1:n(l-1),l)**2) + vdwc(1:n(l),1:n(l-1),l) = vdw(1:n(l),1:n(l-1),l)/(1.- beta(1)**num_mini_batches) + sdwc(1:n(l),1:n(l-1),l) = sdw(1:n(l),1:n(l-1),l)/(1.- beta(2)**num_mini_batches) + w(1:n(l),1:n(l-1),l) = w(1:n(l),1:n(l-1),l) & + - alpha*vdwc(1:n(l),1:n(l-1),l)/(sqrt(sdwc(1:n(l),1:n(l-1),l))+epsilon) ! Adjust weights + + dcdb(1:n(l),l) = dcdb(1:n(l),l)/mini_batch_size + vdb(1:n(l),l) = beta(1)*vdb(1:n(l),l) + obeta(1)*dcdb(1:n(l),l) + sdb(1:n(l),l) = beta(2)*sdb(1:n(l),l) + obeta(2)*(dcdb(1:n(l),l)**2) + vdbc(1:n(l),l) = vdb(1:n(l),l)/(1. - beta(1)**num_mini_batches) + sdbc(1:n(l),l) = sdb(1:n(l),l)/(1. - beta(2)**num_mini_batches) + b(1:n(l),l) = b(1:n(l),l) - alpha*vdbc(1:n(l),l)/(sqrt(sdbc(1:n(l),l))+epsilon) ! Adjust weights + end do adam_adjust_weights_and_biases + end associate + end block adam + else + associate(eta => learning_rate) + adjust_weights_and_biases: & + do concurrent(l = 1:output_layer) + dcdb(1:n(l),l) = dcdb(1:n(l),l)/mini_batch_size + b(1:n(l),l) = b(1:n(l),l) - eta*dcdb(1:n(l),l) ! Adjust biases + dcdw(1:n(l),1:n(l-1),l) = dcdw(1:n(l),1:n(l-1),l)/mini_batch_size + w(1:n(l),1:n(l-1),l) = w(1:n(l),1:n(l-1),l) - eta*dcdw(1:n(l),1:n(l-1),l) ! Adjust weights + end do adjust_weights_and_biases + end associate + end if + end do iterate_across_batches + end associate + end associate + end associate + end procedure default_real_learn + end submodule inference_engine_s diff --git a/src/inference_engine/trainable_engine_s.F90 b/src/inference_engine/trainable_engine_s.F90 index 155a084cc..f72096fe6 100644 --- a/src/inference_engine/trainable_engine_s.F90 +++ b/src/inference_engine/trainable_engine_s.F90 @@ -412,6 +412,5 @@ pure function e(j,n) result(unit_vector) module procedure default_real_map_from_output_training_range unnormalized_tensor = self%output_map_%map_from_training_range(tensor) end procedure - end submodule trainable_engine_s diff --git a/src/inference_engine/trainable_network_m.f90 b/src/inference_engine/trainable_network_m.f90 new file mode 100644 index 000000000..3338f0f69 --- /dev/null +++ b/src/inference_engine/trainable_network_m.f90 @@ -0,0 +1,42 @@ +module trainable_network_m + use inference_engine_m_, only : inference_engine_t, workspace_t + use mini_batch_m, only : mini_batch_t + use kind_parameters_m, only : default_real + implicit none + + private + public :: trainable_network_t + + type, extends(inference_engine_t) :: trainable_network_t(k) + integer, kind :: k = default_real + private + type(workspace_t), private :: workspace_ + contains + generic :: train => default_real_train + procedure, non_overridable :: default_real_train + end type + + interface trainable_network_t + + pure module function default_real_network(inference_engine) result(trainable_network) + implicit none + type(inference_engine_t), intent(in) :: inference_engine + type(trainable_network_t) trainable_network + end function + + end interface + + interface + + pure module subroutine default_real_train(self, mini_batches_arr, cost, adam, learning_rate) + implicit none + class(trainable_network_t), intent(inout) :: self + type(mini_batch_t), intent(in) :: mini_batches_arr(:) + real, intent(out), allocatable, optional :: cost(:) + logical, intent(in) :: adam + real, intent(in) :: learning_rate + end subroutine + + end interface + +end module trainable_network_m diff --git a/src/inference_engine/trainable_network_s.f90 b/src/inference_engine/trainable_network_s.f90 new file mode 100644 index 000000000..d6e1d83a6 --- /dev/null +++ b/src/inference_engine/trainable_network_s.f90 @@ -0,0 +1,15 @@ +submodule(trainable_network_m) trainable_network_s + implicit none + +contains + + module procedure default_real_network + trainable_network%inference_engine_t = inference_engine + trainable_network%workspace_ = workspace_t(inference_engine) + end procedure + + module procedure default_real_train + call self%inference_engine_t%default_real_learn(mini_batches_arr, cost, adam, learning_rate, self%workspace_) + end procedure + +end submodule trainable_network_s diff --git a/src/inference_engine/workspace_s.f90 b/src/inference_engine/workspace_s.f90 new file mode 100644 index 000000000..f3d80139c --- /dev/null +++ b/src/inference_engine/workspace_s.f90 @@ -0,0 +1,117 @@ +submodule(inference_engine_m_) workspace_s + use assert_m, only : assert + implicit none + + integer, parameter :: input_layer = 0 + +contains + + module procedure default_real_workspace + + allocate(workspace%dcdw, mold=inference_engine%weights_) ! Gradient of cost function with respect to weights + allocate(workspace%vdw , mold=inference_engine%weights_) + allocate(workspace%sdw , mold=inference_engine%weights_) + allocate(workspace%vdwc, mold=inference_engine%weights_) + allocate(workspace%sdwc, mold=inference_engine%weights_) + + allocate(workspace%dcdb, mold=inference_engine%biases_ ) ! Gradient of cost function with respect with biases + allocate(workspace%vdb , mold=inference_engine%biases_ ) + allocate(workspace%sdb , mold=inference_engine%biases_ ) + allocate(workspace%vdbc, mold=inference_engine%biases_ ) + allocate(workspace%sdbc, mold=inference_engine%biases_ ) + + ! TODO: #if ! (F2023_LOCALITY || F2018_LOCALITY) + ! then don't allocate a, z, and delta + + allocate(workspace%z , mold=inference_engine%biases_) + allocate(workspace%delta, mold=inference_engine%biases_) + + associate(output_layer => ubound(inference_engine%nodes_,1)) + allocate(workspace%a(maxval(inference_engine%nodes_), input_layer:output_layer)) ! Activations + end associate + + call assert(workspace%fully_allocated(), "workspace_s(defalt_real_workspace): workspace allocated") + + end procedure + + module procedure default_real_allocated + + ! TODO: #if ! (F2023_LOCALITY || F2018_LOCALITY) + ! then don't check a, z, and delta allocations + + all_allocated = all( [ & + allocated(self%a), allocated(self%dcdw), allocated(self%vdw), allocated(self%sdw), allocated(self%vdwc), allocated(self%sdwc)& + ,allocated(self%z), allocated(self%dcdb), allocated(self%vdb), allocated(self%sdb), allocated(self%vdbc), allocated(self%sdbc)& + ,allocated(self%delta) & + ]) + end procedure + + + module procedure default_real_allocate + + call allocate_if_necessary(self%dcdw, mold=inference_engine%weights_) ! Gradient of cost function with respect to weights + call allocate_if_necessary(self%vdw , mold=inference_engine%weights_) + call allocate_if_necessary(self%sdw , mold=inference_engine%weights_) + call allocate_if_necessary(self%vdwc, mold=inference_engine%weights_) + call allocate_if_necessary(self%sdwc, mold=inference_engine%weights_) + + call allocate_if_necessary(self%dcdb , mold=inference_engine%biases_) ! Gradient of cost function with respect with biases + call allocate_if_necessary(self%vdb , mold=inference_engine%biases_) + call allocate_if_necessary(self%sdb , mold=inference_engine%biases_) + call allocate_if_necessary(self%vdbc , mold=inference_engine%biases_) + call allocate_if_necessary(self%sdbc , mold=inference_engine%biases_) + + ! TODO: #if ! (F2023_LOCALITY || F2018_LOCALITY) + ! then don't allocate a, z, and delta + + call allocate_if_necessary(self%z , mold=inference_engine%biases_) + call allocate_if_necessary(self%delta, mold=inference_engine%biases_) + + associate(output_layer => ubound(inference_engine%nodes_,1)) + allocate(self%a(maxval(inference_engine%nodes_), input_layer:output_layer)) ! Activations + end associate + + contains + + subroutine allocate_if_necessary(array, mold) + real, allocatable, intent(inout) :: array(..) + real, intent(in) :: mold(..) + + select rank(array) + rank(2) + select rank(mold) + rank(2) + if (.not. allocated(array)) then + allocate(array, mold=mold) + else + if (any(shape(array) /= shape(mold))) then + deallocate(array) + allocate(array, mold=mold) + end if + end if + rank default + error stop "workspace_s(allocate_if_necessary): mold-rank mismatch with rank-2 'array'" + end select + rank(3) + select rank(mold) + rank(3) + if (.not. allocated(array)) then + allocate(array, mold=mold) + else + if (any(shape(array) /= shape(mold))) then + deallocate(array) + allocate(array, mold=mold) + end if + end if + rank default + error stop "workspace_s(allocate_if_necessary): mold-rank mismatch with rank-3 'array'" + end select + rank default + error stop "workspace_s(allocate_if_necessary): unsupported 'array' rank" + end select + + end subroutine allocate_if_necessary + + end procedure default_real_allocate + +end submodule workspace_s diff --git a/src/inference_engine_m.f90 b/src/inference_engine_m.f90 index 22f505671..732b20ca0 100644 --- a/src/inference_engine_m.f90 +++ b/src/inference_engine_m.f90 @@ -13,6 +13,7 @@ module inference_engine_m use network_configuration_m, only : network_configuration_t use tensor_m, only : tensor_t use tensor_map_m, only : tensor_map_t + use trainable_network_m, only : trainable_network_t use trainable_engine_m, only : trainable_engine_t use training_configuration_m, only : training_configuration_t use ubounds_m, only : ubounds_t From 5ef6625dc3cb747f7e96394fd91bdacb4e2a39ae Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Sun, 13 Oct 2024 20:43:20 -0700 Subject: [PATCH 083/105] feat(sat-mix-rat):adjust to use trainale_network_t --- example/learn-saturated-mixing-ratio.F90 | 45 +++++++++++------------- 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/example/learn-saturated-mixing-ratio.F90 b/example/learn-saturated-mixing-ratio.F90 index ba8bd599c..641f33f3e 100644 --- a/example/learn-saturated-mixing-ratio.F90 +++ b/example/learn-saturated-mixing-ratio.F90 @@ -3,7 +3,7 @@ program train_saturated_mixture_ratio !! This program trains a neural network to learn the saturated mixing ratio function of ICAR. use inference_engine_m, only : & - inference_engine_t, trainable_engine_t, mini_batch_t, tensor_t, input_output_pair_t, shuffle + inference_engine_t, trainable_network_t, mini_batch_t, tensor_t, input_output_pair_t, shuffle use julienne_m, only : string_t, file_t, command_line_t, bin_t, csv use assert_m, only : assert, intrinsic_array_t use saturated_mixing_ratio_m, only : y, T, p @@ -30,29 +30,29 @@ program train_saturated_mixture_ratio type(mini_batch_t), allocatable :: mini_batches(:) type(input_output_pair_t), allocatable :: input_output_pairs(:) type(tensor_t), allocatable :: inputs(:), desired_outputs(:) - type(trainable_engine_t) trainable_engine + type(trainable_network_t) trainable_network type(bin_t), allocatable :: bins(:) real, allocatable :: cost(:), random_numbers(:) integer io_status, network_unit, plot_unit - integer, parameter :: io_success=0, diagnostics_print_interval = 1000, network_save_interval = 10000 + integer, parameter :: io_success=0, diagnostics_print_interval = 100, network_save_interval = 1000 integer, parameter :: nodes_per_layer(*) = [2, 4, 72, 2, 1] - real, parameter :: cost_tolerance = 1.E-08 + real, parameter :: cost_tolerance = 1.E-06 call random_init(image_distinct=.true., repeatable=.true.) open(newunit=network_unit, file=network_file%string(), form='formatted', status='old', iostat=io_status, action='read') if (io_status == io_success) then print *,"Reading network from file " // network_file%string() - trainable_engine = trainable_engine_t(inference_engine_t(file_t(network_file))) + trainable_network = trainable_network_t(inference_engine_t(file_t(network_file))) close(network_unit) else close(network_unit) print *,"Initializing a new network" - trainable_engine = perturbed_identity_network(perturbation_magnitude=0.05, n = nodes_per_layer) + trainable_network = perturbed_identity_network(perturbation_magnitude=0.05, n = nodes_per_layer) end if - call output(trainable_engine%to_inference_engine(), string_t("initial-network.json")) + call output(trainable_network, string_t("initial-network.json")) - associate(num_inputs => trainable_engine%num_inputs(), num_outputs => trainable_engine%num_outputs()) + associate(num_inputs => trainable_network%num_inputs(), num_outputs => trainable_network%num_outputs()) block integer i, j @@ -84,7 +84,7 @@ program train_saturated_mixture_ratio call random_number(random_numbers) call shuffle(input_output_pairs) mini_batches = [(mini_batch_t(input_output_pairs(bins(b)%first():bins(b)%last())), b = 1, size(bins))] - call trainable_engine%train(mini_batches, cost, adam=.true., learning_rate=1.5) + call trainable_network%train(mini_batches, cost, adam=.true., learning_rate=1.5) call system_clock(counter_end, clock_rate) associate( & @@ -95,7 +95,7 @@ program train_saturated_mixture_ratio write_and_exit_if_converged: & if (cost_avg < cost_tolerance) then call print_diagnostics(plot_unit, e, cost_avg, cumulative_clock_time, nodes_per_layer) - call output(trainable_engine%to_inference_engine(), network_file) + call output(trainable_network, network_file) exit end if write_and_exit_if_converged @@ -104,13 +104,13 @@ program train_saturated_mixture_ratio write_and_exit_if_stop_file_exists: & if (io_status==0) then call print_diagnostics(plot_unit, e, cost_avg, cumulative_clock_time, nodes_per_layer) - call output(trainable_engine%to_inference_engine(), network_file) + call output(trainable_network, network_file) exit end if write_and_exit_if_stop_file_exists if (mod(e,diagnostics_print_interval)==0 .or. loop_ending) & call print_diagnostics(plot_unit, e, cost_avg, cumulative_clock_time, nodes_per_layer) - if (mod(e,network_save_interval)==0 .or. loop_ending) call output(trainable_engine%to_inference_engine(), network_file) + if (mod(e,network_save_interval)==0 .or. loop_ending) call output(trainable_network, network_file) end associate end do @@ -121,9 +121,9 @@ program train_saturated_mixture_ratio integer p #if defined _CRAYFTN || __GFORTRAN__ type(tensor_t), allocatable :: network_outputs(:) - network_outputs = trainable_engine%infer(inputs) + network_outputs = trainable_network%infer(inputs) #else - associate(network_outputs => trainable_engine%infer(inputs)) + associate(network_outputs => trainable_network%infer(inputs)) #endif print *,"Inputs (normalized) | Outputs | Desired outputs" do p = 1, num_pairs @@ -139,7 +139,7 @@ program train_saturated_mixture_ratio end associate - call output(trainable_engine%to_inference_engine(), network_file) + call output(trainable_network, network_file) end block @@ -156,7 +156,7 @@ subroutine print_diagnostics(plot_file_unit, epoch, cost, clock, nodes) end subroutine subroutine output(inference_engine, file_name) - type(inference_engine_t), intent(in) :: inference_engine + class(inference_engine_t), intent(in) :: inference_engine type(string_t), intent(in) :: file_name type(file_t) json_file json_file = inference_engine%to_json() @@ -170,8 +170,8 @@ pure function e(j,n) result(unit_vector) unit_vector = real([(merge(1,0,j==k),k=1,n)]) end function - function perturbed_identity_network(perturbation_magnitude, n) result(trainable_engine) - type(trainable_engine_t) trainable_engine + function perturbed_identity_network(perturbation_magnitude, n) result(trainable_network) + type(trainable_network_t) trainable_network real, intent(in) :: perturbation_magnitude integer, intent(in) :: n(:) integer k, l @@ -187,12 +187,9 @@ function perturbed_identity_network(perturbation_magnitude, n) result(trainable_ call random_number(b_harvest) associate(w => identity + perturbation_magnitude*(w_harvest-0.5)/0.5, b => perturbation_magnitude*(b_harvest-0.5)/0.5) - - trainable_engine = trainable_engine_t( & - nodes = n, weights = w, biases = b, metadata = & - [string_t("Saturated Mixing Ratio"), string_t("Damian Rouson"), string_t("2023-09-23"), string_t("relu"), & - string_t("false")] & - ) + trainable_network = trainable_network_t( inference_engine_t(nodes=n, weights=w, biases=b, & + metadata=[string_t("Saturated Mixing Ratio"),string_t("Rouson"),string_t("20241013"),string_t("relu"),string_t("false")] & + )) end associate end associate end function From d45c57eebf4ff5f1b3ff8716b0e528da04af11a8 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Sun, 13 Oct 2024 20:56:46 -0700 Subject: [PATCH 084/105] fix(activation_t):add :: in default initialization --- src/inference_engine/activation_m.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/inference_engine/activation_m.f90 b/src/inference_engine/activation_m.f90 index 3566fbff8..966f6beea 100644 --- a/src/inference_engine/activation_m.f90 +++ b/src/inference_engine/activation_m.f90 @@ -15,7 +15,7 @@ module activation_m type activation_t private - integer(c_int) selection_ = sigmoid + integer(c_int) :: selection_ = sigmoid contains procedure, non_overridable :: function_name generic :: operator(==) => equals From 4c76b7a85a94f3dc031ad1f80f2236963dbc908c Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Sun, 13 Oct 2024 21:50:51 -0700 Subject: [PATCH 085/105] fix(unmapped_network): rm ambiguous specific proc also adjust demo/app/infer-aerosol.f90 to reflect the renaming of unmapped_engine_t to unmapped_network_t --- demo/app/infer-aerosol.f90 | 6 +- src/inference_engine/inference_engine_m_.f90 | 158 +++++++++++-------- src/inference_engine/inference_engine_s.F90 | 142 ++++------------- src/inference_engine/unmapped_network_s.f90 | 88 +++++++++++ src/inference_engine_m.f90 | 2 +- 5 files changed, 216 insertions(+), 180 deletions(-) create mode 100644 src/inference_engine/unmapped_network_s.f90 diff --git a/demo/app/infer-aerosol.f90 b/demo/app/infer-aerosol.f90 index 6810af0cc..d2211f2e9 100644 --- a/demo/app/infer-aerosol.f90 +++ b/demo/app/infer-aerosol.f90 @@ -7,7 +7,7 @@ program infer_aerosol use iso_fortran_env, only : int64, real64 ! External dependencies: - use inference_engine_m, only : unmapped_engine_t, tensor_t, double_precision, double_precision_file_t + use inference_engine_m, only : unmapped_network_t, tensor_t, double_precision, double_precision_file_t use julienne_m, only : string_t, command_line_t use omp_lib @@ -49,7 +49,7 @@ subroutine read_stats_and_perform_inference(path) double precision cube_root double precision, allocatable, dimension(:,:) :: aerosol_data, input_components, output_components type(tensor_statistics_t) input_stats, output_stats - type(unmapped_engine_t(double_precision)) inference_engine + type(unmapped_network_t(double_precision)) inference_engine integer i, j input_stats = read_tensor_statistics(path // "meanxp.txt", path // "stdxp.txt", num_inputs) !for pre-processing normalization @@ -76,7 +76,7 @@ subroutine read_stats_and_perform_inference(path) !$omp end parallel do print *, "Reading the neural network from " // network_file_name - inference_engine = unmapped_engine_t(double_precision_file_t(path // network_file_name)) + inference_engine = unmapped_network_t(double_precision_file_t(path // network_file_name)) time_inference: & block diff --git a/src/inference_engine/inference_engine_m_.f90 b/src/inference_engine/inference_engine_m_.f90 index a196fd6ee..40121b4cf 100644 --- a/src/inference_engine/inference_engine_m_.f90 +++ b/src/inference_engine/inference_engine_m_.f90 @@ -14,7 +14,7 @@ module inference_engine_m_ private public :: inference_engine_t - public :: unmapped_engine_t + public :: unmapped_network_t public :: exchange_t public :: workspace_t @@ -41,6 +41,8 @@ module inference_engine_m_ generic :: activation_function_name => default_real_activation_name, double_precision_activation_name generic :: to_exchange => default_real_to_exchange, double_precision_to_exchange generic :: learn => default_real_learn + generic :: assert_consistency => default_real_consistency, double_precision_consistency + procedure, private, non_overridable :: default_real_consistency, double_precision_consistency procedure, private, non_overridable :: default_real_approximately_equal, double_precision_approximately_equal procedure, private, non_overridable :: default_real_infer, double_precision_infer procedure, private, non_overridable :: default_real_learn @@ -69,11 +71,31 @@ module inference_engine_m_ procedure, non_overridable, private :: default_real_allocate end type - type, extends(inference_engine_t) :: unmapped_engine_t - contains - generic :: infer => default_real_infer_unmapped, double_precision_infer_unmapped - procedure, private :: default_real_infer_unmapped, double_precision_infer_unmapped - end type + interface workspace_t + + pure module function default_real_workspace(inference_engine) result(workspace) + implicit none + type(inference_engine_t), intent(in) :: inference_engine + type(workspace_t) workspace + end function + + end interface + + interface + + module subroutine default_real_allocate(self, inference_engine) + implicit none + class(workspace_t), intent(inout) :: self + type(inference_engine_t), intent(in) :: inference_engine + end subroutine + + pure module function default_real_allocated(self) result(all_allocated) + implicit none + class(workspace_t), intent(in) :: self + logical all_allocated + end function + + end interface type exchange_t(k) integer, kind :: k = default_real @@ -84,6 +106,22 @@ module inference_engine_m_ type(activation_t) activation_ end type + interface + + module function default_real_to_exchange(self) result(exchange) + implicit none + class(inference_engine_t), intent(in) :: self + type(exchange_t) exchange + end function + + module function double_precision_to_exchange(self) result(exchange) + implicit none + class(inference_engine_t(double_precision)), intent(in) :: self + type(exchange_t(double_precision)) exchange + end function + + end interface + interface inference_engine_t module function default_real_construct_from_components(metadata, weights, biases, nodes, input_map, output_map) & @@ -120,42 +158,7 @@ impure elemental module function double_precision_from_json(file) result(inferen end interface - interface unmapped_engine_t - - impure elemental module function double_precision_unmapped_from_json(file) result(unmapped_engine) - implicit none - type(double_precision_file_t), intent(in) :: file - type(unmapped_engine_t(double_precision)) unmapped_engine - end function - - end interface - - - interface workspace_t - - pure module function default_real_workspace(inference_engine) result(workspace) - implicit none - type(inference_engine_t), intent(in) :: inference_engine - type(workspace_t) workspace - end function - - end interface - - interface - - module subroutine default_real_allocate(self, inference_engine) - implicit none - class(workspace_t), intent(inout) :: self - type(inference_engine_t), intent(in) :: inference_engine - end subroutine - - pure module function default_real_allocated(self) result(all_allocated) - implicit none - class(workspace_t), intent(in) :: self - logical all_allocated - end function - end interface interface ! inference_engine_t type-bound procedures @@ -206,18 +209,6 @@ elemental module function double_precision_map_from_output_range(self, normalize type(tensor_t(double_precision)) tensor end function - module function default_real_to_exchange(self) result(exchange) - implicit none - class(inference_engine_t), intent(in) :: self - type(exchange_t) exchange - end function - - module function double_precision_to_exchange(self) result(exchange) - implicit none - class(inference_engine_t(double_precision)), intent(in) :: self - type(exchange_t(double_precision)) exchange - end function - impure elemental module function default_real_to_json(self) result(json_file) implicit none class(inference_engine_t), intent(in) :: self @@ -249,20 +240,6 @@ elemental module function default_real_infer(self, inputs) result(outputs) type(tensor_t) outputs end function - elemental module function default_real_infer_unmapped(self, inputs) result(outputs) - implicit none - class(unmapped_engine_t), intent(in) :: self - type(tensor_t), intent(in) :: inputs - type(tensor_t) outputs - end function - - elemental module function double_precision_infer_unmapped(self, inputs) result(outputs) - implicit none - class(unmapped_engine_t(double_precision)), intent(in) :: self - type(tensor_t(double_precision)), intent(in) :: inputs - type(tensor_t(double_precision)) outputs - end function - elemental module function double_precision_infer(self, inputs) result(outputs) implicit none class(inference_engine_t(double_precision)), intent(in) :: self @@ -352,6 +329,53 @@ pure module subroutine default_real_learn(self, mini_batches_arr, cost, adam, le type(workspace_t), intent(inout) :: workspace end subroutine + pure module subroutine default_real_consistency(self) + implicit none + class(inference_engine_t), intent(in) :: self + end subroutine + + pure module subroutine double_precision_consistency(self) + implicit none + class(inference_engine_t(double_precision)), intent(in) :: self + end subroutine + + end interface + + type unmapped_network_t(k) + integer, kind :: k = default_real + private + type(inference_engine_t(k)) inference_engine_ + contains + generic :: infer => default_real_infer_unmapped, double_precision_infer_unmapped + procedure, private, non_overridable :: default_real_infer_unmapped, double_precision_infer_unmapped + end type + + interface unmapped_network_t + + impure elemental module function double_precision_unmapped_from_json(file) result(unmapped_network) + implicit none + type(double_precision_file_t), intent(in) :: file + type(unmapped_network_t(double_precision)) unmapped_network + end function + + end interface + + interface + + elemental module function default_real_infer_unmapped(self, inputs) result(outputs) + implicit none + class(unmapped_network_t), intent(in) :: self + type(tensor_t), intent(in) :: inputs + type(tensor_t) outputs + end function + + elemental module function double_precision_infer_unmapped(self, inputs) result(outputs) + implicit none + class(unmapped_network_t(double_precision)), intent(in) :: self + type(tensor_t(double_precision)), intent(in) :: inputs + type(tensor_t(double_precision)) outputs + end function + end interface end module inference_engine_m_ diff --git a/src/inference_engine/inference_engine_s.F90 b/src/inference_engine/inference_engine_s.F90 index ce8b5c7ea..ac31642f6 100644 --- a/src/inference_engine/inference_engine_s.F90 +++ b/src/inference_engine/inference_engine_s.F90 @@ -8,11 +8,6 @@ use neuron_m, only : neuron_t implicit none - interface assert_consistency - procedure default_real_consistency - procedure double_precision_consistency - end interface - character(len=*), parameter :: acceptable_engine_tag = "0.13.0" ! git tag capable of reading the current json file format integer, parameter :: input_layer = 0 @@ -58,72 +53,12 @@ exchange%activation_ = self%activation_ end procedure - module procedure default_real_infer_unmapped - - real, allocatable :: a(:,:) - integer l - - call assert_consistency(self) - - associate(w => self%weights_, b => self%biases_, n => self%nodes_, output_layer => ubound(self%nodes_,1)) - - allocate(a(maxval(n), input_layer:output_layer)) - - a(1:n(input_layer),input_layer) = inputs%values() - - feed_forward: & - do l = input_layer+1, output_layer - associate(z => matmul(w(1:n(l),1:n(l-1),l), a(1:n(l-1),l-1)) + b(1:n(l),l)) - if (l .lt. output_layer) then - a(1:n(l),l) = self%activation_%evaluate(z) - else - a(1:n(l),l) = z(1:n(l)) - end if - end associate - end do feed_forward - - outputs = tensor_t(a(1:n(output_layer), output_layer)) - - end associate - - end procedure - - module procedure double_precision_infer_unmapped - - double precision, allocatable :: a(:,:) - integer l - - call assert_consistency(self) - - associate(w => self%weights_, b => self%biases_, n => self%nodes_, output_layer => ubound(self%nodes_,1)) - - allocate(a(maxval(n), input_layer:output_layer)) - - a(1:n(input_layer),input_layer) = inputs%values() - - feed_forward: & - do l = input_layer+1, output_layer - associate(z => matmul(w(1:n(l),1:n(l-1),l), a(1:n(l-1),l-1)) + b(1:n(l),l)) - if (l .lt. output_layer) then - a(1:n(l),l) = self%activation_%evaluate(z) - else - a(1:n(l),l) = z(1:n(l)) - end if - end associate - end do feed_forward - - outputs = tensor_t(a(1:n(output_layer), output_layer)) - - end associate - - end procedure - module procedure default_real_infer real, allocatable :: a(:,:) integer l - call assert_consistency(self) + call self%assert_consistency() associate(w => self%weights_, b => self%biases_, n => self%nodes_, output_layer => ubound(self%nodes_,1)) @@ -169,7 +104,7 @@ double precision, allocatable :: a(:,:) integer l - call assert_consistency(self) + call self%assert_consistency() associate(w => self%weights_, b => self%biases_, n => self%nodes_, output_layer => ubound(self%nodes_,1)) @@ -212,52 +147,47 @@ end procedure - pure subroutine default_real_consistency(self) - - class(inference_engine_t), intent(in) :: self + module procedure default_real_consistency associate( & all_allocated=>[allocated(self%weights_),allocated(self%biases_),allocated(self%nodes_)]& ) - call assert(all(all_allocated),"inference_engine_s(inference_engine_consistency): fully_allocated", & + call assert(all(all_allocated),"inference_engine_s(default_real_consistency): fully_allocated", & intrinsic_array_t(all_allocated)) end associate associate(max_width=>maxval(self%nodes_), component_dims=>[size(self%biases_,1), size(self%weights_,1), size(self%weights_,2)]) - call assert(all(component_dims == max_width), "inference_engine_s(inference_engine_consistency): conformable arrays", & + call assert(all(component_dims == max_width), "inference_engine_s(default_real_consistency): conformable arrays", & intrinsic_array_t([max_width,component_dims])) end associate associate(input_subscript => lbound(self%nodes_,1)) - call assert(input_subscript == input_layer, "inference_engine_s(inference_engine_consistency): n base subsscript", & + call assert(input_subscript == input_layer, "inference_engine_s(default_real_consistency): n base subsscript", & input_subscript) end associate - end subroutine - - pure subroutine double_precision_consistency(self) - - class(inference_engine_t(double_precision)), intent(in) :: self + end procedure + module procedure double_precision_consistency associate( & all_allocated=>[allocated(self%weights_),allocated(self%biases_),allocated(self%nodes_)]& ) - call assert(all(all_allocated),"inference_engine_s(inference_engine_consistency): fully_allocated", & + call assert(all(all_allocated),"inference_engine_s(default_real_consistency): fully_allocated", & intrinsic_array_t(all_allocated)) end associate associate(max_width=>maxval(self%nodes_), component_dims=>[size(self%biases_,1), size(self%weights_,1), size(self%weights_,2)]) - call assert(all(component_dims == max_width), "inference_engine_s(inference_engine_consistency): conformable arrays", & + call assert(all(component_dims == max_width), "inference_engine_s(default_real_consistency): conformable arrays", & intrinsic_array_t([max_width,component_dims])) end associate associate(input_subscript => lbound(self%nodes_,1)) - call assert(input_subscript == input_layer, "inference_engine_s(inference_engine_consistency): n base subsscript", & + call assert(input_subscript == input_layer, "inference_engine_s(default_real_consistency): n base subsscript", & input_subscript) end associate - end subroutine + end procedure module procedure default_real_construct_from_components @@ -292,7 +222,7 @@ pure subroutine double_precision_consistency(self) inference_engine%activation_ = activation_t(metadata(4)%string()) - call assert_consistency(inference_engine) + call inference_engine%assert_consistency() end procedure default_real_construct_from_components @@ -331,7 +261,7 @@ pure subroutine double_precision_consistency(self) inference_engine%activation_ = activation_t(function_name%string()) end associate - call assert_consistency(inference_engine) + call inference_engine%assert_consistency() end procedure double_precision_construct_from_components @@ -346,7 +276,7 @@ pure subroutine double_precision_consistency(self) proto_neuron = neuron_t([0.],1.) #endif - call assert_consistency(self) + call self%assert_consistency() associate( & num_hidden_layers => self%num_hidden_layers() & @@ -462,7 +392,7 @@ pure subroutine double_precision_consistency(self) proto_neuron = neuron_t([0D0],1D0) #endif - call assert_consistency(self) + call self%assert_consistency() associate( & num_hidden_layers => self%num_hidden_layers() & @@ -665,14 +595,10 @@ pure subroutine double_precision_consistency(self) end associate end associate read_metadata - call assert_consistency(inference_engine) + call inference_engine%assert_consistency() end procedure default_real_from_json - module procedure double_precision_unmapped_from_json - unmapped_engine%inference_engine_t = double_precision_from_json(file) - end procedure - module procedure double_precision_from_json character(len=:), allocatable :: justified_line @@ -769,14 +695,13 @@ pure subroutine double_precision_consistency(self) end associate end associate read_metadata - call assert_consistency(inference_engine) + call inference_engine%assert_consistency() end procedure double_precision_from_json module procedure default_real_assert_conformable_with - call assert_consistency(self) - call assert_consistency(inference_engine) + call self%assert_consistency() associate(equal_shapes => [ & shape(self%weights_) == shape(inference_engine%weights_), & @@ -792,8 +717,7 @@ pure subroutine double_precision_consistency(self) module procedure double_precision_assert_conformable_with - call assert_consistency(self) - call assert_consistency(inference_engine) + call self%assert_consistency() associate(equal_shapes => [ & shape(self%weights_) == shape(inference_engine%weights_), & @@ -813,8 +737,8 @@ pure subroutine double_precision_consistency(self) nodes_eq = all(lhs%nodes_ == rhs%nodes_) - call assert_consistency(lhs) - call assert_consistency(rhs) + call lhs%assert_consistency() + call rhs%assert_consistency() call lhs%assert_conformable_with(rhs) block @@ -854,8 +778,8 @@ pure subroutine double_precision_consistency(self) nodes_eq = all(lhs%nodes_ == rhs%nodes_) - call assert_consistency(lhs) - call assert_consistency(rhs) + call lhs%assert_consistency() + call rhs%assert_consistency() call lhs%assert_conformable_with(rhs) block @@ -890,18 +814,18 @@ pure subroutine double_precision_consistency(self) end procedure module procedure default_real_num_outputs - call assert_consistency(self) + call self%assert_consistency() output_count = self%nodes_(ubound(self%nodes_,1)) end procedure module procedure double_precision_num_outputs - call assert_consistency(self) + call self%assert_consistency() output_count = self%nodes_(ubound(self%nodes_,1)) end procedure module procedure default_real_num_hidden_layers integer, parameter :: num_non_hidden_layers = 2 - call assert_consistency(self) + call self%assert_consistency() associate(num_layers => size(self%nodes_)) hidden_layer_count = num_layers - num_non_hidden_layers end associate @@ -909,29 +833,29 @@ pure subroutine double_precision_consistency(self) module procedure double_precision_num_hidden_layers integer, parameter :: num_non_hidden_layers = 2 - call assert_consistency(self) + call self%assert_consistency() associate(num_layers => size(self%nodes_)) hidden_layer_count = num_layers - num_non_hidden_layers end associate end procedure module procedure default_real_num_inputs - call assert_consistency(self) + call self%assert_consistency() input_count = self%nodes_(lbound(self%nodes_,1)) end procedure module procedure double_precision_num_inputs - call assert_consistency(self) + call self%assert_consistency() input_count = self%nodes_(lbound(self%nodes_,1)) end procedure module procedure default_real_nodes_per_layer - call assert_consistency(self) + call self%assert_consistency() node_count = self%nodes_ end procedure module procedure double_precision_nodes_per_layer - call assert_consistency(self) + call self%assert_consistency() node_count = self%nodes_ end procedure @@ -963,7 +887,7 @@ pure subroutine double_precision_consistency(self) integer l, batch, mini_batch_size, pair type(tensor_t), allocatable :: inputs(:), expected_outputs(:) - call assert_consistency(self) + call self%assert_consistency() call assert(workspace%fully_allocated(), "inference_engine_s(default_real_learn): workspace%fully_allocated()") associate(output_layer => ubound(self%nodes_,1)) diff --git a/src/inference_engine/unmapped_network_s.f90 b/src/inference_engine/unmapped_network_s.f90 new file mode 100644 index 000000000..9ced289e5 --- /dev/null +++ b/src/inference_engine/unmapped_network_s.f90 @@ -0,0 +1,88 @@ +! Copyright (c), The Regents of the University of California +! Terms of use are as specified in LICENSE.txt +submodule(inference_engine_m_) unmapped_network_s + implicit none + + integer, parameter :: input_layer = 0 + +contains + + module procedure double_precision_unmapped_from_json + unmapped_network%inference_engine_ = double_precision_from_json(file) + end procedure + + module procedure default_real_infer_unmapped + + real, allocatable :: a(:,:) + integer l + + associate(inference_engine => self%inference_engine_) + + call inference_engine%assert_consistency() + + associate( & + w => inference_engine%weights_ & + ,b => inference_engine%biases_ & + ,n => inference_engine%nodes_ & + ,output_layer => ubound(inference_engine%nodes_,1) & + ) + allocate(a(maxval(n), input_layer:output_layer)) + + a(1:n(input_layer),input_layer) = inputs%values() + + feed_forward: & + do l = input_layer+1, output_layer + associate(z => matmul(w(1:n(l),1:n(l-1),l), a(1:n(l-1),l-1)) + b(1:n(l),l)) + if (l .lt. output_layer) then + a(1:n(l),l) = inference_engine%activation_%evaluate(z) + else + a(1:n(l),l) = z(1:n(l)) + end if + end associate + end do feed_forward + + outputs = tensor_t(a(1:n(output_layer), output_layer)) + + end associate + end associate + + end procedure + + module procedure double_precision_infer_unmapped + + double precision, allocatable :: a(:,:) + integer l + + associate(inference_engine => self%inference_engine_) + + call inference_engine%assert_consistency() + + associate( & + w => inference_engine%weights_ & + ,b => inference_engine%biases_ & + ,n => inference_engine%nodes_ & + ,output_layer => ubound(inference_engine%nodes_,1) & + ) + + allocate(a(maxval(n), input_layer:output_layer)) + + a(1:n(input_layer),input_layer) = inputs%values() + + feed_forward: & + do l = input_layer+1, output_layer + associate(z => matmul(w(1:n(l),1:n(l-1),l), a(1:n(l-1),l-1)) + b(1:n(l),l)) + if (l .lt. output_layer) then + a(1:n(l),l) = inference_engine%activation_%evaluate(z) + else + a(1:n(l),l) = z(1:n(l)) + end if + end associate + end do feed_forward + + outputs = tensor_t(a(1:n(output_layer), output_layer)) + end associate + end associate + + end procedure + +end submodule unmapped_network_s diff --git a/src/inference_engine_m.f90 b/src/inference_engine_m.f90 index 732b20ca0..5b6820d1e 100644 --- a/src/inference_engine_m.f90 +++ b/src/inference_engine_m.f90 @@ -6,7 +6,7 @@ module inference_engine_m use double_precision_string_m, only : double_precision_string_t use hyperparameters_m, only : hyperparameters_t use input_output_pair_m, only : input_output_pair_t, shuffle, write_to_stdout - use inference_engine_m_, only : inference_engine_t, unmapped_engine_t + use inference_engine_m_, only : inference_engine_t, unmapped_network_t use kind_parameters_m, only : default_real, double_precision use metadata_m, only : metadata_t use mini_batch_m, only : mini_batch_t From e4f8ba9c8784c373d98529b43c2966491c085582 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Sun, 13 Oct 2024 23:35:30 -0700 Subject: [PATCH 086/105] chore(trainable_network):replaces trainable_engine --- demo/app/train-cloud-microphysics.F90 | 20 +- example/learn-addition.F90 | 29 +- example/learn-exponentiation.F90 | 29 +- example/learn-microphysics-procedures.F90 | 236 ---------- example/learn-multiplication.F90 | 29 +- example/learn-power-series.F90 | 29 +- example/learn-saturated-mixing-ratio.F90 | 4 +- example/supporting-modules/mp_thompson.f90 | 102 ----- .../supporting-modules/thompson_tensors_m.f90 | 33 -- example/train-and-write.F90 | 39 +- src/inference_engine/tmp | 0 src/inference_engine/trainable_engine_m.F90 | 179 -------- src/inference_engine/trainable_engine_s.F90 | 416 ------------------ src/inference_engine/trainable_network_m.f90 | 29 +- src/inference_engine/trainable_network_s.f90 | 48 +- src/inference_engine_m.f90 | 1 - test/main.F90 | 6 +- ...est_m.F90 => trainable_network_test_m.F90} | 106 ++--- 18 files changed, 216 insertions(+), 1119 deletions(-) delete mode 100644 example/learn-microphysics-procedures.F90 delete mode 100644 example/supporting-modules/mp_thompson.f90 delete mode 100644 example/supporting-modules/thompson_tensors_m.f90 create mode 100644 src/inference_engine/tmp delete mode 100644 src/inference_engine/trainable_engine_m.F90 delete mode 100644 src/inference_engine/trainable_engine_s.F90 rename test/{trainable_engine_test_m.F90 => trainable_network_test_m.F90} (86%) diff --git a/demo/app/train-cloud-microphysics.F90 b/demo/app/train-cloud-microphysics.F90 index fcb952056..e96038f5d 100644 --- a/demo/app/train-cloud-microphysics.F90 +++ b/demo/app/train-cloud-microphysics.F90 @@ -13,7 +13,7 @@ program train_on_flat_distribution use julienne_m, only : string_t, file_t, command_line_t, bin_t use assert_m, only : assert, intrinsic_array_t use inference_engine_m, only : & - inference_engine_t, mini_batch_t, input_output_pair_t, tensor_t, trainable_engine_t, tensor_map_t, & + inference_engine_t, mini_batch_t, input_output_pair_t, tensor_t, trainable_network_t, tensor_map_t, & training_configuration_t, shuffle !! Internal dependencies: @@ -271,7 +271,7 @@ subroutine read_train_write(training_configuration, args, plot_file) train_network: & block - type(trainable_engine_t) trainable_engine + type(trainable_network_t) trainable_network type(mini_batch_t), allocatable :: mini_batches(:) type(bin_t), allocatable :: bins(:) type(input_output_pair_t), allocatable :: input_output_pairs(:) @@ -319,7 +319,7 @@ subroutine read_train_write(training_configuration, args, plot_file) read_or_initialize_engine: & if (io_status==0) then print *,"Reading network from file " // network_file - trainable_engine = trainable_engine_t(inference_engine_t(file_t(string_t(network_file)))) + trainable_network = trainable_network_t(inference_engine_t(file_t(string_t(network_file)))) close(network_unit) else close(network_unit) @@ -339,9 +339,9 @@ subroutine read_train_write(training_configuration, args, plot_file) maxima = [maxval(pressure_in), maxval(potential_temperature_in), maxval(temperature_in), & maxval(qv_in), maxval(qc_in), maxval(qr_in), maxval(qs_in)] & ) ) - associate(activation => training_configuration%differentiable_activation_strategy()) + associate(activation => training_configuration%differentiable_activation()) associate(residual_network=> string_t(trim(merge("true ", "false", training_configuration%skip_connections())))) - trainable_engine = trainable_engine_t( & + trainable_network = trainable_network_t( & training_configuration, & perturbation_magnitude = 0.05, & metadata = [ & @@ -379,7 +379,7 @@ subroutine read_train_write(training_configuration, args, plot_file) end associate output_extrema print *,"Normalizing the remaining input and output tensors" - input_output_pairs = trainable_engine%map_to_training_ranges(input_output_pairs) + input_output_pairs = trainable_network%map_to_training_ranges(input_output_pairs) associate( & num_pairs => size(input_output_pairs), & @@ -417,7 +417,7 @@ subroutine read_train_write(training_configuration, args, plot_file) if (size(bins)>1) call shuffle(input_output_pairs) ! set up for stochastic gradient descent mini_batches = [(mini_batch_t(input_output_pairs(bins(b)%first():bins(b)%last())), b = 1, size(bins))] - call trainable_engine%train(mini_batches, cost, adam, learning_rate) + call trainable_network%train(mini_batches, cost, adam, learning_rate) associate(average_cost => sum(cost)/size(cost)) associate(converged => average_cost <= args%cost_tolerance) @@ -432,10 +432,8 @@ subroutine read_train_write(training_configuration, args, plot_file) integer net_unit open(newunit=net_unit, file=network_file, form='formatted', status='unknown', iostat=io_status, action='write') - associate(inference_engine => trainable_engine%to_inference_engine()) - associate(json_file => inference_engine%to_json()) - call json_file%write_lines(string_t(network_file)) - end associate + associate(json_file => trainable_network%to_json()) + call json_file%write_lines(string_t(network_file)) end associate close(net_unit) end block diff --git a/example/learn-addition.F90 b/example/learn-addition.F90 index 0e2efdf3f..b6174650f 100644 --- a/example/learn-addition.F90 +++ b/example/learn-addition.F90 @@ -21,7 +21,7 @@ elemental function y(x_tensor) result(a_tensor) program learn_addition !! This trains a neural network to learn the following six polynomial functions of its eight inputs. use inference_engine_m, only : & - inference_engine_t, trainable_engine_t, mini_batch_t, tensor_t, input_output_pair_t, shuffle + inference_engine_t, trainable_network_t, mini_batch_t, tensor_t, input_output_pair_t, shuffle use julienne_m, only : string_t, file_t, command_line_t, bin_t use assert_m, only : assert, intrinsic_array_t use addition_m, only : y @@ -43,15 +43,15 @@ program learn_addition type(mini_batch_t), allocatable :: mini_batches(:) type(input_output_pair_t), allocatable :: input_output_pairs(:) type(tensor_t), allocatable :: inputs(:), desired_outputs(:) - type(trainable_engine_t) trainable_engine + type(trainable_network_t) trainable_network type(bin_t), allocatable :: bins(:) real, allocatable :: cost(:), random_numbers(:) call random_init(image_distinct=.true., repeatable=.true.) - trainable_engine = perturbed_identity_network(perturbation_magnitude=0.05) - call output(trainable_engine%to_inference_engine(), string_t("initial-network.json")) + trainable_network = perturbed_identity_network(perturbation_magnitude=0.05) + call output(trainable_network, string_t("initial-network.json")) - associate(num_inputs => trainable_engine%num_inputs(), num_outputs => trainable_engine%num_outputs()) + associate(num_inputs => trainable_network%num_inputs(), num_outputs => trainable_network%num_outputs()) block integer i, j @@ -76,19 +76,18 @@ program learn_addition call random_number(random_numbers) call shuffle(input_output_pairs) mini_batches = [(mini_batch_t(input_output_pairs(bins(b)%first():bins(b)%last())), b = 1, size(bins))] - call trainable_engine%train(mini_batches, cost, adam=.true., learning_rate=1.5) + call trainable_network%train(mini_batches, cost, adam=.true., learning_rate=1.5) print *,sum(cost)/size(cost) end do end block block - real, parameter :: tolerance = 1.E-06 integer p #if defined _CRAYFTN || __GFORTRAN__ type(tensor_t), allocatable :: network_outputs(:) - network_outputs = trainable_engine%infer(inputs) + network_outputs = trainable_network%infer(inputs) #else - associate(network_outputs => trainable_engine%infer(inputs)) + associate(network_outputs => trainable_network%infer(inputs)) #endif print "(a,69x,a)"," Outputs", "| Desired outputs" do p = 1, num_pairs @@ -102,14 +101,14 @@ program learn_addition end associate - call output(trainable_engine%to_inference_engine(), final_network_file) + call output(trainable_network, final_network_file) end block contains subroutine output(inference_engine, file_name) - type(inference_engine_t), intent(in) :: inference_engine + class(inference_engine_t), intent(in) :: inference_engine type(string_t), intent(in) :: file_name type(file_t) json_file json_file = inference_engine%to_json() @@ -123,8 +122,8 @@ pure function e(j,n) result(unit_vector) unit_vector = real([(merge(1,0,j==k),k=1,n)]) end function - function perturbed_identity_network(perturbation_magnitude) result(trainable_engine) - type(trainable_engine_t) trainable_engine + function perturbed_identity_network(perturbation_magnitude) result(trainable_network) + type(trainable_network_t) trainable_network real, intent(in) :: perturbation_magnitude integer, parameter :: n(*) = [8, 64, 64, 64, 6] integer, parameter :: n_max = maxval(n), layers = size(n) @@ -141,10 +140,10 @@ function perturbed_identity_network(perturbation_magnitude) result(trainable_eng associate(w => identity + perturbation_magnitude*(w_harvest-0.5)/0.5, b => perturbation_magnitude*(b_harvest-0.5)/0.5) - trainable_engine = trainable_engine_t( & + trainable_network = trainable_network_t( inference_engine_t( & nodes = n, weights = w, biases = b, metadata = & [string_t("Perturbed Identity"), string_t("Damian Rouson"), string_t("2023-09-23"), string_t("relu"), string_t("false")] & - ) + )) end associate end function diff --git a/example/learn-exponentiation.F90 b/example/learn-exponentiation.F90 index 6b8ba0624..a09b9359d 100644 --- a/example/learn-exponentiation.F90 +++ b/example/learn-exponentiation.F90 @@ -21,7 +21,7 @@ elemental function y(x_tensor) result(a_tensor) program learn_exponentiation !! This trains a neural network to learn the following six polynomial functions of its eight inputs. use inference_engine_m, only : & - inference_engine_t, trainable_engine_t, mini_batch_t, tensor_t, input_output_pair_t, shuffle + inference_engine_t, trainable_network_t, mini_batch_t, tensor_t, input_output_pair_t, shuffle use julienne_m, only : string_t, file_t, command_line_t, bin_t use assert_m, only : assert, intrinsic_array_t use exponentiation_m, only : y @@ -43,15 +43,15 @@ program learn_exponentiation type(mini_batch_t), allocatable :: mini_batches(:) type(input_output_pair_t), allocatable :: input_output_pairs(:) type(tensor_t), allocatable :: inputs(:), desired_outputs(:) - type(trainable_engine_t) trainable_engine + type(trainable_network_t) trainable_network type(bin_t), allocatable :: bins(:) real, allocatable :: cost(:), random_numbers(:) call random_init(image_distinct=.true., repeatable=.true.) - trainable_engine = perturbed_identity_network(perturbation_magnitude=0.05) - call output(trainable_engine%to_inference_engine(), string_t("initial-network.json")) + trainable_network = perturbed_identity_network(perturbation_magnitude=0.05) + call output(trainable_network, string_t("initial-network.json")) - associate(num_inputs => trainable_engine%num_inputs(), num_outputs => trainable_engine%num_outputs()) + associate(num_inputs => trainable_network%num_inputs(), num_outputs => trainable_network%num_outputs()) block integer i, j @@ -76,19 +76,18 @@ program learn_exponentiation call random_number(random_numbers) call shuffle(input_output_pairs) mini_batches = [(mini_batch_t(input_output_pairs(bins(b)%first():bins(b)%last())), b = 1, size(bins))] - call trainable_engine%train(mini_batches, cost, adam=.true., learning_rate=1.5) + call trainable_network%train(mini_batches, cost, adam=.true., learning_rate=1.5) print *,sum(cost)/size(cost) end do end block block - real, parameter :: tolerance = 1.E-06 integer p #if defined _CRAYFTN || __GFORTRAN__ type(tensor_t), allocatable :: network_outputs(:) - network_outputs = trainable_engine%infer(inputs) + network_outputs = trainable_network%infer(inputs) #else - associate(network_outputs => trainable_engine%infer(inputs)) + associate(network_outputs => trainable_network%infer(inputs)) #endif print "(a,69x,a)"," Outputs", "| Desired outputs" do p = 1, num_pairs @@ -102,14 +101,14 @@ program learn_exponentiation end associate - call output(trainable_engine%to_inference_engine(), final_network_file) + call output(trainable_network, final_network_file) end block contains subroutine output(inference_engine, file_name) - type(inference_engine_t), intent(in) :: inference_engine + class(inference_engine_t), intent(in) :: inference_engine type(string_t), intent(in) :: file_name type(file_t) json_file json_file = inference_engine%to_json() @@ -123,8 +122,8 @@ pure function e(j,n) result(unit_vector) unit_vector = real([(merge(1,0,j==k),k=1,n)]) end function - function perturbed_identity_network(perturbation_magnitude) result(trainable_engine) - type(trainable_engine_t) trainable_engine + function perturbed_identity_network(perturbation_magnitude) result(trainable_network) + type(trainable_network_t) trainable_network real, intent(in) :: perturbation_magnitude 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) @@ -141,10 +140,10 @@ function perturbed_identity_network(perturbation_magnitude) result(trainable_eng associate(w => identity + perturbation_magnitude*(w_harvest-0.5)/0.5, b => perturbation_magnitude*(b_harvest-0.5)/0.5) - trainable_engine = trainable_engine_t( & + trainable_network = trainable_network_t( inference_engine_t( & nodes = n, weights = w, biases = b, metadata = & [string_t("Perturbed Identity"), string_t("Damian Rouson"), string_t("2023-09-23"), string_t("relu"), string_t("false")] & - ) + )) end associate end function diff --git a/example/learn-microphysics-procedures.F90 b/example/learn-microphysics-procedures.F90 deleted file mode 100644 index 552bd9ac0..000000000 --- a/example/learn-microphysics-procedures.F90 +++ /dev/null @@ -1,236 +0,0 @@ -! Copyright (c), The Regents of the University of California -! Terms of use are as specified in LICENSE.txt -program learn_microphysics_procedures - !! Train a neural network proxies for procedures in the Thompson microphysics model - !! in of ICAR (https://github.com/BerkeleyLab/icar). - use inference_engine_m, only : & - inference_engine_t, trainable_engine_t, mini_batch_t, tensor_t, input_output_pair_t, shuffle - use julienne_m, only : string_t, file_t, command_line_t, bin_t, csv - use assert_m, only : assert, intrinsic_array_t - use thompson_tensors_m, only : y, T, p - use iso_fortran_env, only : int64, output_unit - implicit none - - type(string_t) network_file - type(command_line_t) command_line - integer(int64) counter_start, counter_end, clock_rate - - network_file = string_t(command_line%flag_value("--output-file")) - - if (len(network_file%string())==0) then - error stop new_line('a') // new_line('a') // & - 'Usage: fpm run learn-microphysics-procedures --profile release --flag "-fopenmp" -- --output-file ""' - end if - - call system_clock(counter_start, clock_rate) - - block - integer, parameter :: max_num_epochs = 10000000, num_mini_batches = 10 - integer num_pairs ! number of input/output pairs - - type(mini_batch_t), allocatable :: mini_batches(:) - type(input_output_pair_t), allocatable :: input_output_pairs(:) - type(tensor_t), allocatable :: inputs(:), desired_outputs(:) - type(trainable_engine_t) trainable_engine - type(bin_t), allocatable :: bins(:) - real, allocatable :: cost(:), random_numbers(:) - integer io_status, network_unit, plot_unit - integer, parameter :: io_success=0, diagnostics_print_interval = 1000, network_save_interval = 10000 - integer, parameter :: nodes_per_layer(*) = [2, 72, 2] - real, parameter :: cost_tolerance = 1.E-08 - - call random_init(image_distinct=.true., repeatable=.true.) - open(newunit=network_unit, file=network_file%string(), form='formatted', status='old', iostat=io_status, action='read') - - if (io_status == io_success) then - print *,"Reading network from file " // network_file%string() - trainable_engine = trainable_engine_t(inference_engine_t(file_t(network_file))) - close(network_unit) - else - close(network_unit) - print *,"Initializing a new network" - trainable_engine = perturbed_identity_network(perturbation_magnitude=0.05, n = nodes_per_layer) - end if - call output(trainable_engine%to_inference_engine(), string_t("initial-network.json")) - - associate(num_inputs => trainable_engine%num_inputs(), num_outputs => trainable_engine%num_outputs()) - - block - integer i, j - integer, allocatable :: output_sizes(:) - inputs = [( [(tensor_t([T(i), p(j)]), j=1,size(p))], i = 1,size(T))] - num_pairs = size(inputs) - call assert(num_pairs == size(T)*size(p), "train_cloud_microphysics: inputs tensor array complete") - desired_outputs = y(inputs) - output_sizes = [(size(desired_outputs(i)%values()),i=1,size(desired_outputs))] - call assert(all([num_outputs==output_sizes]), "fit-polynomials: # outputs", intrinsic_array_t([num_outputs,output_sizes])) - end block - - input_output_pairs = input_output_pair_t(inputs, desired_outputs) - - block - integer b - bins = [(bin_t(num_items=num_pairs, num_bins=num_mini_batches, bin_number=b), b = 1, num_mini_batches)] - end block - - block - integer e, b, stop_unit, previous_epoch - real previous_clock_time - - call open_plot_file_for_appending("cost.plt", plot_unit, previous_epoch, previous_clock_time) - print *, " Epoch | Cost Function| System_Clock | Nodes per Layer" - allocate(random_numbers(2:size(input_output_pairs))) - - do e = previous_epoch + 1, previous_epoch + max_num_epochs - call random_number(random_numbers) - call shuffle(input_output_pairs) - mini_batches = [(mini_batch_t(input_output_pairs(bins(b)%first():bins(b)%last())), b = 1, size(bins))] - call trainable_engine%train(mini_batches, cost, adam=.true., learning_rate=1.5) - call system_clock(counter_end, clock_rate) - - associate( & - cost_avg => sum(cost)/size(cost), & - cumulative_clock_time => previous_clock_time + real(counter_end - counter_start) / real(clock_rate), & - loop_ending => e == previous_epoch + max_num_epochs & - ) - write_and_exit_if_converged: & - if (cost_avg < cost_tolerance) then - call print_diagnostics(plot_unit, e, cost_avg, cumulative_clock_time, nodes_per_layer) - call output(trainable_engine%to_inference_engine(), network_file) - exit - end if write_and_exit_if_converged - - open(newunit=stop_unit, file="stop", form='formatted', status='old', iostat=io_status) - - write_and_exit_if_stop_file_exists: & - if (io_status==0) then - call print_diagnostics(plot_unit, e, cost_avg, cumulative_clock_time, nodes_per_layer) - call output(trainable_engine%to_inference_engine(), network_file) - exit - end if write_and_exit_if_stop_file_exists - - if (mod(e,diagnostics_print_interval)==0 .or. loop_ending) & - call print_diagnostics(plot_unit, e, cost_avg, cumulative_clock_time, nodes_per_layer) - if (mod(e,network_save_interval)==0 .or. loop_ending) call output(trainable_engine%to_inference_engine(), network_file) - end associate - end do - - close(plot_unit) - - report_network_performance: & - block - integer p -#if defined _CRAYFTN || __GFORTRAN__ - type(tensor_t), allocatable :: network_outputs(:) - network_outputs = trainable_engine%infer(inputs) -#else - associate(network_outputs => trainable_engine%infer(inputs)) -#endif - print *," Inputs (normalized) | Outputs | Desired outputs" - do p = 1, num_pairs - print "(6(G13.5,2x))", inputs(p)%values(), network_outputs(p)%values(), desired_outputs(p)%values() - end do -#if defined _CRAYFTN || __GFORTRAN__ -#else - end associate -#endif - end block report_network_performance - - end block - - end associate - - call output(trainable_engine%to_inference_engine(), network_file) - - end block - -contains - - subroutine print_diagnostics(plot_file_unit, epoch, cost, clock, nodes) - integer, intent(in) :: plot_file_unit, epoch, nodes(:) - real, intent(in) :: cost, clock - - write(unit=output_unit, fmt='(3(g13.5,2x))', advance='no') epoch, cost, clock - write(unit=output_unit, fmt=csv) nodes - write(unit=plot_file_unit, fmt='(3(g13.5,2x))', advance='no') epoch, cost, clock - write(unit=plot_file_unit, fmt=csv) nodes - end subroutine - - subroutine output(inference_engine, file_name) - type(inference_engine_t), intent(in) :: inference_engine - type(string_t), intent(in) :: file_name - type(file_t) json_file - json_file = inference_engine%to_json() - call json_file%write_lines(file_name) - end subroutine - - pure function e(j,n) result(unit_vector) - integer, intent(in) :: j, n - integer k - real, allocatable :: unit_vector(:) - unit_vector = real([(merge(1,0,j==k),k=1,n)]) - end function - - function perturbed_identity_network(perturbation_magnitude, n) result(trainable_engine) - type(trainable_engine_t) trainable_engine - real, intent(in) :: perturbation_magnitude - integer, intent(in) :: n(:) - integer j, k, l - real, allocatable :: identity(:,:,:), w_harvest(:,:,:), b_harvest(:,:) - - associate(n_max => maxval(n), layers => size(n)) - identity = reshape( [( [(e(k,n_max), k=1,n_max)], l = 1, layers-1 )], [n_max, n_max, layers-1]) - - allocate(w_harvest, mold = identity) - allocate(b_harvest(size(identity,1), size(identity,3))) - - call random_number(w_harvest) - call random_number(b_harvest) - - associate(w => identity + perturbation_magnitude*(w_harvest-0.5)/0.5, b => perturbation_magnitude*(b_harvest-0.5)/0.5) - - trainable_engine = trainable_engine_t( & - nodes = n, weights = w, biases = b, metadata = & - [string_t("Thompson microphysics procedures"), string_t("Damian Rouson"), string_t("2023-09-23"), string_t("sigmoid"), & - string_t("false")] & - ) - end associate - end associate - end function - - subroutine open_plot_file_for_appending(plot_file_name, plot_unit, previous_epoch, previous_clock) - character(len=*), intent(in) :: plot_file_name - integer, intent(out) :: plot_unit, previous_epoch - real, intent(out) :: previous_clock - - type(file_t) plot_file - type(string_t), allocatable :: lines(:) - character(len=:), allocatable :: last_line - integer io_status - integer, parameter :: io_success = 0 - logical preexisting_plot_file - real cost - - inquire(file=plot_file_name, exist=preexisting_plot_file) - open(newunit=plot_unit,file="cost.plt",status="unknown",position="append") - - associate(header => " Epoch | Cost Function| System_Clock | Nodes per Layer") - if (.not. preexisting_plot_file) then - write(plot_unit,*) header - previous_epoch = 0 - previous_clock = 0 - else - plot_file = file_t(string_t(plot_file_name)) - lines = plot_file%lines() - last_line = lines(size(lines))%string() - read(last_line,*, iostat=io_status) previous_epoch, cost, previous_clock - if ((io_status /= io_success .and. last_line == header) .or. len(trim(last_line))==0) then - previous_epoch = 0 - previous_clock = 0 - end if - end if - end associate - - end subroutine - -end program diff --git a/example/learn-multiplication.F90 b/example/learn-multiplication.F90 index 797335dd9..81d72e88d 100644 --- a/example/learn-multiplication.F90 +++ b/example/learn-multiplication.F90 @@ -21,7 +21,7 @@ elemental function y(x_tensor) result(a_tensor) program learn_multiplication !! This trains a neural network to learn the following six polynomial functions of its eight inputs. use inference_engine_m, only : & - inference_engine_t, trainable_engine_t, mini_batch_t, tensor_t, input_output_pair_t, shuffle + inference_engine_t, trainable_network_t, mini_batch_t, tensor_t, input_output_pair_t, shuffle use julienne_m, only : string_t, file_t, command_line_t, bin_t use assert_m, only : assert, intrinsic_array_t use multiply_inputs, only : y @@ -43,15 +43,15 @@ program learn_multiplication type(mini_batch_t), allocatable :: mini_batches(:) type(input_output_pair_t), allocatable :: input_output_pairs(:) type(tensor_t), allocatable :: inputs(:), desired_outputs(:) - type(trainable_engine_t) trainable_engine + type(trainable_network_t) trainable_network type(bin_t), allocatable :: bins(:) real, allocatable :: cost(:), random_numbers(:) call random_init(image_distinct=.true., repeatable=.true.) - trainable_engine = perturbed_identity_network(perturbation_magnitude=0.05) - call output(trainable_engine%to_inference_engine(), string_t("initial-network.json")) + trainable_network = perturbed_identity_network(perturbation_magnitude=0.05) + call output(trainable_network, string_t("initial-network.json")) - associate(num_inputs => trainable_engine%num_inputs(), num_outputs => trainable_engine%num_outputs()) + associate(num_inputs => trainable_network%num_inputs(), num_outputs => trainable_network%num_outputs()) block integer i, j @@ -76,19 +76,18 @@ program learn_multiplication call random_number(random_numbers) call shuffle(input_output_pairs) mini_batches = [(mini_batch_t(input_output_pairs(bins(b)%first():bins(b)%last())), b = 1, size(bins))] - call trainable_engine%train(mini_batches, cost, adam=.true., learning_rate=1.5) + call trainable_network%train(mini_batches, cost, adam=.true., learning_rate=1.5) print *,sum(cost)/size(cost) end do end block block - real, parameter :: tolerance = 1.E-06 integer p #if defined _CRAYFTN || __GFORTRAN__ type(tensor_t), allocatable :: network_outputs(:) - network_outputs = trainable_engine%infer(inputs) + network_outputs = trainable_network%infer(inputs) #else - associate(network_outputs => trainable_engine%infer(inputs)) + associate(network_outputs => trainable_network%infer(inputs)) #endif print "(a,69x,a)"," Outputs", "| Desired outputs" do p = 1, num_pairs @@ -102,14 +101,14 @@ program learn_multiplication end associate - call output(trainable_engine%to_inference_engine(), final_network_file) + call output(trainable_network, final_network_file) end block contains subroutine output(inference_engine, file_name) - type(inference_engine_t), intent(in) :: inference_engine + class(inference_engine_t), intent(in) :: inference_engine type(string_t), intent(in) :: file_name type(file_t) json_file json_file = inference_engine%to_json() @@ -123,8 +122,8 @@ pure function e(j,n) result(unit_vector) unit_vector = real([(merge(1,0,j==k),k=1,n)]) end function - function perturbed_identity_network(perturbation_magnitude) result(trainable_engine) - type(trainable_engine_t) trainable_engine + function perturbed_identity_network(perturbation_magnitude) result(trainable_network) + type(trainable_network_t) trainable_network real, intent(in) :: perturbation_magnitude integer, parameter :: n(*) = [8, 64, 64, 64, 6] integer, parameter :: n_max = maxval(n), layers = size(n) @@ -141,10 +140,10 @@ function perturbed_identity_network(perturbation_magnitude) result(trainable_eng associate(w => identity + perturbation_magnitude*(w_harvest-0.5)/0.5, b => perturbation_magnitude*(b_harvest-0.5)/0.5) - trainable_engine = trainable_engine_t( & + trainable_network = trainable_network_t( inference_engine_t( & nodes = n, weights = w, biases = b, metadata = & [string_t("Perturbed Identity"), string_t("Damian Rouson"), string_t("2023-09-23"), string_t("relu"), string_t("false")] & - ) + )) end associate end function diff --git a/example/learn-power-series.F90 b/example/learn-power-series.F90 index c0490669e..e8a46e6dc 100644 --- a/example/learn-power-series.F90 +++ b/example/learn-power-series.F90 @@ -21,7 +21,7 @@ elemental function y(x_in) result(a) program learn_power_series !! This trains a neural network to learn the following six polynomial functions of its eight inputs. use inference_engine_m, only : & - inference_engine_t, trainable_engine_t, mini_batch_t, tensor_t, input_output_pair_t, shuffle + inference_engine_t, trainable_network_t, mini_batch_t, tensor_t, input_output_pair_t, shuffle use julienne_m, only : string_t, file_t, command_line_t, bin_t use assert_m, only : assert, intrinsic_array_t use power_series, only : y @@ -43,15 +43,15 @@ program learn_power_series type(mini_batch_t), allocatable :: mini_batches(:) type(input_output_pair_t), allocatable :: input_output_pairs(:) type(tensor_t), allocatable :: inputs(:), desired_outputs(:) - type(trainable_engine_t) trainable_engine + type(trainable_network_t) trainable_network type(bin_t), allocatable :: bins(:) real, allocatable :: cost(:), random_numbers(:) call random_init(image_distinct=.true., repeatable=.true.) - trainable_engine = perturbed_identity_network(perturbation_magnitude=0.05) - call output(trainable_engine%to_inference_engine(), string_t("initial-network.json")) + trainable_network = perturbed_identity_network(perturbation_magnitude=0.05) + call output(trainable_network, string_t("initial-network.json")) - associate(num_inputs => trainable_engine%num_inputs(), num_outputs => trainable_engine%num_outputs()) + associate(num_inputs => trainable_network%num_inputs(), num_outputs => trainable_network%num_outputs()) block integer i, j @@ -78,19 +78,18 @@ program learn_power_series call random_number(random_numbers) call shuffle(input_output_pairs) mini_batches = [(mini_batch_t(input_output_pairs(bins(b)%first():bins(b)%last())), b = 1, size(bins))] - call trainable_engine%train(mini_batches, cost, adam=.true., learning_rate=1.5) + call trainable_network%train(mini_batches, cost, adam=.true., learning_rate=1.5) print *,sum(cost)/size(cost) end do end block block - real, parameter :: tolerance = 1.E-06 integer p #if defined _CRAYFTN || __GFORTRAN__ type(tensor_t), allocatable :: network_outputs(:) - network_outputs = trainable_engine%infer(inputs) + network_outputs = trainable_network%infer(inputs) #else - associate(network_outputs => trainable_engine%infer(inputs)) + associate(network_outputs => trainable_network%infer(inputs)) #endif print "(a,69x,a)"," Outputs", "| Desired outputs" do p = 1, num_pairs @@ -104,14 +103,14 @@ program learn_power_series end associate - call output(trainable_engine%to_inference_engine(), final_network_file) + call output(trainable_network, final_network_file) end block contains subroutine output(inference_engine, file_name) - type(inference_engine_t), intent(in) :: inference_engine + class(inference_engine_t), intent(in) :: inference_engine type(string_t), intent(in) :: file_name type(file_t) json_file json_file = inference_engine%to_json() @@ -125,8 +124,8 @@ pure function e(j,n) result(unit_vector) unit_vector = real([(merge(1,0,j==k),k=1,n)]) end function - function perturbed_identity_network(perturbation_magnitude) result(trainable_engine) - type(trainable_engine_t) trainable_engine + function perturbed_identity_network(perturbation_magnitude) result(trainable_network) + type(trainable_network_t) trainable_network real, intent(in) :: perturbation_magnitude integer, parameter :: n(*) = [8, 196, 196, 196, 196, 6] integer, parameter :: n_max = maxval(n), layers = size(n) @@ -143,10 +142,10 @@ function perturbed_identity_network(perturbation_magnitude) result(trainable_eng associate(w => identity + perturbation_magnitude*(w_harvest-0.5)/0.5, b => perturbation_magnitude*(b_harvest-0.5)/0.5) - trainable_engine = trainable_engine_t( & + trainable_network = trainable_network_t( inference_engine_t( & nodes = n, weights = w, biases = b, metadata = & [string_t("Perturbed Identity"), string_t("Damian Rouson"), string_t("2023-09-23"), string_t("relu"), string_t("false")] & - ) + )) end associate end function diff --git a/example/learn-saturated-mixing-ratio.F90 b/example/learn-saturated-mixing-ratio.F90 index 641f33f3e..bf49da58c 100644 --- a/example/learn-saturated-mixing-ratio.F90 +++ b/example/learn-saturated-mixing-ratio.F90 @@ -34,9 +34,9 @@ program train_saturated_mixture_ratio type(bin_t), allocatable :: bins(:) real, allocatable :: cost(:), random_numbers(:) integer io_status, network_unit, plot_unit - integer, parameter :: io_success=0, diagnostics_print_interval = 100, network_save_interval = 1000 + integer, parameter :: io_success=0, diagnostics_print_interval = 1000, network_save_interval = 10000 integer, parameter :: nodes_per_layer(*) = [2, 4, 72, 2, 1] - real, parameter :: cost_tolerance = 1.E-06 + real, parameter :: cost_tolerance = 1.E-08 call random_init(image_distinct=.true., repeatable=.true.) open(newunit=network_unit, file=network_file%string(), form='formatted', status='old', iostat=io_status, action='read') diff --git a/example/supporting-modules/mp_thompson.f90 b/example/supporting-modules/mp_thompson.f90 deleted file mode 100644 index 2e765471d..000000000 --- a/example/supporting-modules/mp_thompson.f90 +++ /dev/null @@ -1,102 +0,0 @@ -! Copyright (c), The Regents of the University of California -! Terms of use are as specified in LICENSE.txt - -! MIT License -! -! Copyright (c) 2017 National Center for Atmospheric Research -! -! Permission is hereby granted, free of charge, to any person obtaining a copy -! of this software and associated documentation files (the "Software"), to deal -! in the Software without restriction, including without limitation the rights -! to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -! copies of the Software, and to permit persons to whom the Software is -! furnished to do so, subject to the following conditions: -! -! The above copyright notice and this permission notice shall be included in all -! copies or substantial portions of the Software. -! -! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -! IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -! FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -! AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -! LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -! OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -! SOFTWARE. - - MODULE module_mp_thompson - - ! Adapted from https://github.com/BerkeleyLab/icar - - IMPLICIT NONE - - CONTAINS - -!+---+-----------------------------------------------------------------+ -! THIS FUNCTION CALCULATES THE LIQUID SATURATION VAPOR MIXING RATIO AS -! A FUNCTION OF TEMPERATURE AND PRESSURE -! - REAL FUNCTION RSLF(P,T) - - IMPLICIT NONE - REAL, INTENT(IN):: P, T - REAL:: ESL,X - REAL, PARAMETER:: C0= .611583699E03 - REAL, PARAMETER:: C1= .444606896E02 - REAL, PARAMETER:: C2= .143177157E01 - REAL, PARAMETER:: C3= .264224321E-1 - REAL, PARAMETER:: C4= .299291081E-3 - REAL, PARAMETER:: C5= .203154182E-5 - REAL, PARAMETER:: C6= .702620698E-8 - REAL, PARAMETER:: C7= .379534310E-11 - REAL, PARAMETER:: C8=-.321582393E-13 - - X=MAX(-80.,T-273.16) - -! ESL=612.2*EXP(17.67*X/(T-29.65)) - ESL=C0+X*(C1+X*(C2+X*(C3+X*(C4+X*(C5+X*(C6+X*(C7+X*C8))))))) - RSLF=.622*ESL/(P-ESL) - -! ALTERNATIVE -! ; Source: Murphy and Koop, Review of the vapour pressure of ice and -! supercooled water for atmospheric applications, Q. J. R. -! Meteorol. Soc (2005), 131, pp. 1539-1565. -! ESL = EXP(54.842763 - 6763.22 / T - 4.210 * ALOG(T) + 0.000367 * T -! + TANH(0.0415 * (T - 218.8)) * (53.878 - 1331.22 -! / T - 9.44523 * ALOG(T) + 0.014025 * T)) - - END FUNCTION RSLF -!+---+-----------------------------------------------------------------+ -! THIS FUNCTION CALCULATES THE ICE SATURATION VAPOR MIXING RATIO AS A -! FUNCTION OF TEMPERATURE AND PRESSURE -! - REAL FUNCTION RSIF(P,T) - - IMPLICIT NONE - REAL, INTENT(IN):: P, T - REAL:: ESI,X - REAL, PARAMETER:: C0= .609868993E03 - REAL, PARAMETER:: C1= .499320233E02 - REAL, PARAMETER:: C2= .184672631E01 - REAL, PARAMETER:: C3= .402737184E-1 - REAL, PARAMETER:: C4= .565392987E-3 - REAL, PARAMETER:: C5= .521693933E-5 - REAL, PARAMETER:: C6= .307839583E-7 - REAL, PARAMETER:: C7= .105785160E-9 - REAL, PARAMETER:: C8= .161444444E-12 - - X=MAX(-80.,T-273.16) - ESI=C0+X*(C1+X*(C2+X*(C3+X*(C4+X*(C5+X*(C6+X*(C7+X*C8))))))) - RSIF=.622*ESI/(P-ESI) - -! ALTERNATIVE -! ; Source: Murphy and Koop, Review of the vapour pressure of ice and -! supercooled water for atmospheric applications, Q. J. R. -! Meteorol. Soc (2005), 131, pp. 1539-1565. -! ESI = EXP(9.550426 - 5723.265/T + 3.53068*ALOG(T) - 0.00728332*T) - - END FUNCTION RSIF -!+---+-----------------------------------------------------------------+ - -!+---+-----------------------------------------------------------------+ -END MODULE module_mp_thompson -!+---+-----------------------------------------------------------------+ diff --git a/example/supporting-modules/thompson_tensors_m.f90 b/example/supporting-modules/thompson_tensors_m.f90 deleted file mode 100644 index 548ab5d52..000000000 --- a/example/supporting-modules/thompson_tensors_m.f90 +++ /dev/null @@ -1,33 +0,0 @@ -! Copyright (c), The Regents of the University of California -! Terms of use are as specified in LICENSE.txt -module thompson_tensors_m - !! This module supports the program in the file example/learn-microphysics-procedures.f90. - use module_mp_thompson, only : rslf, rsif - use inference_engine_m, only : tensor_t - use assert_m, only : assert - implicit none - - private - public :: T, p, y - - real, parameter :: T_min = 236.352524, T_max = 307.610779 - real, parameter :: p_min = 29671.1348, p_max = 98596.7578 - integer, parameter :: resolution = 10 - integer i - real, parameter :: T(*) = [(real(i)/real(resolution), i=0,resolution)] - real, parameter :: p(*) = [(real(i)/real(resolution), i=0,resolution)] - -contains - - elemental impure function y(x_in) result(a) - type(tensor_t), intent(in) :: x_in - type(tensor_t) a - associate(x => x_in%values()) - call assert(lbound(x,1)==1 .and. ubound(x,1)==2,"y(x) :: sufficient input") - associate(temperature => T_min + (T_max - T_min)*x(1), pressure => p_min + (p_max - p_min)*x(2) ) - a = tensor_t([rslf(pressure, temperature), rsif(pressure, temperature)]) - end associate - end associate - end function - -end module diff --git a/example/train-and-write.F90 b/example/train-and-write.F90 index 04df61a55..7105afde0 100644 --- a/example/train-and-write.F90 +++ b/example/train-and-write.F90 @@ -9,7 +9,7 @@ program train_and_write !! The initial condition corresponds to the desired network with all weights and biases perturbed by a random variable !! that is uniformly distributed on the range [0,0.1]. use inference_engine_m, only : & - inference_engine_t, trainable_engine_t, mini_batch_t, tensor_t, input_output_pair_t, shuffle + inference_engine_t, trainable_network_t, mini_batch_t, tensor_t, input_output_pair_t, shuffle use julienne_m, only : string_t, file_t, command_line_t, bin_t use assert_m, only : assert, intrinsic_array_t implicit none @@ -30,17 +30,17 @@ program train_and_write type(mini_batch_t), allocatable :: mini_batches(:) type(input_output_pair_t), allocatable :: input_output_pairs(:) type(tensor_t), allocatable :: inputs(:) - type(trainable_engine_t) trainable_engine + type(trainable_network_t) trainable_network type(bin_t), allocatable :: bins(:) real, allocatable :: cost(:), random_numbers(:) call random_init(image_distinct=.true., repeatable=.true.) - trainable_engine = perturbed_identity_network(perturbation_magnitude=0.2) - call output(trainable_engine%to_inference_engine(), string_t("initial-network.json")) + trainable_network = perturbed_identity_network(perturbation_magnitude=0.2) + call output(trainable_network, string_t("initial-network.json")) - associate(num_inputs => trainable_engine%num_inputs(), num_outputs => trainable_engine%num_outputs()) + associate(num_inputs => trainable_network%num_inputs(), num_outputs => trainable_network%num_outputs()) - call assert(num_inputs == num_outputs,"trainable_engine_test_m(identity_mapping): # inputs == # outputs", & + call assert(num_inputs == num_outputs,"trainable_network_test_m(identity_mapping): # inputs == # outputs", & intrinsic_array_t([num_inputs, num_outputs]) & ) block @@ -64,7 +64,7 @@ program train_and_write call random_number(random_numbers) call shuffle(input_output_pairs) mini_batches = [(mini_batch_t(input_output_pairs(bins(b)%first():bins(b)%last())), b = 1, size(bins))] - call trainable_engine%train(mini_batches, cost, adam=.true., learning_rate=1.5) + call trainable_network%train(mini_batches, cost, adam=.true., learning_rate=1.5) print *,sum(cost)/size(cost) end do end block @@ -74,14 +74,15 @@ program train_and_write integer p #if defined _CRAYFTN || __GFORTRAN__ type(tensor_t), allocatable :: network_outputs(:) - network_outputs = trainable_engine%infer(inputs) + network_outputs = trainable_network%infer(inputs) #else - associate(network_outputs => trainable_engine%infer(inputs)) + associate(network_outputs => trainable_network%infer(inputs)) #endif - print "(a,62x,a,53x,a)", " Output", "| Desired outputs", "| Errors" - + print "(a,43x,a,35x,a)", " Output", "| Desired outputs", "| Errors" + do p = 1, num_pairs - print *,network_outputs(p)%values(),"|", inputs(p)%values(), "|", network_outputs(p)%values() - inputs(p)%values() + print "(2(3(G11.5,', '), G11.5, a), 3(G11.5,', '), G11.5)", & + network_outputs(p)%values(),"| ", inputs(p)%values(), "| ", network_outputs(p)%values() - inputs(p)%values() end do #if defined _CRAYFTN || __GFORTRAN__ #else @@ -91,14 +92,14 @@ program train_and_write end associate - call output(trainable_engine%to_inference_engine(), final_network_file) + call output(trainable_network, final_network_file) end block contains subroutine output(inference_engine, file_name) - type(inference_engine_t), intent(in) :: inference_engine + class(inference_engine_t), intent(in) :: inference_engine type(string_t), intent(in) :: file_name type(file_t) json_file json_file = inference_engine%to_json() @@ -111,8 +112,8 @@ pure function e(m,n) result(e_mn) e_mn = real(merge(1,0,m==n)) end function - function perturbed_identity_network(perturbation_magnitude) result(trainable_engine) - type(trainable_engine_t) trainable_engine + function perturbed_identity_network(perturbation_magnitude) result(trainable_network) + type(trainable_network_t) trainable_network real, intent(in) :: perturbation_magnitude integer, parameter :: nodes_per_layer(*) = [4, 4, 4, 4] integer, parameter :: max_n = maxval(nodes_per_layer), layers = size(nodes_per_layer) @@ -129,10 +130,10 @@ function perturbed_identity_network(perturbation_magnitude) result(trainable_eng associate(w => identity + perturbation_magnitude*(w_harvest-0.5)/0.5, b => perturbation_magnitude*(b_harvest-0.5)/0.5) - trainable_engine = trainable_engine_t( & + trainable_network = trainable_network_t( inference_engine_t( & nodes = nodes_per_layer, weights = w, biases = b, metadata = & - [string_t("Perturbed Identity"), string_t("Damian Rouson"), string_t("2023-09-23"), string_t("relu"), string_t("false")] & - ) + [string_t("Perturbed Identity"), string_t("Damian Rouson"), string_t("2024-10-13"), string_t("relu"), string_t("false")] & + )) end associate diff --git a/src/inference_engine/tmp b/src/inference_engine/tmp new file mode 100644 index 000000000..e69de29bb diff --git a/src/inference_engine/trainable_engine_m.F90 b/src/inference_engine/trainable_engine_m.F90 deleted file mode 100644 index b7acc6e83..000000000 --- a/src/inference_engine/trainable_engine_m.F90 +++ /dev/null @@ -1,179 +0,0 @@ -! Copyright (c), The Regents of the University of California -! Terms of use are as specified in LICENSE.txt -module trainable_engine_m - !! Define an abstraction that supports training a neural network - use activation_m, only : activation_t - use julienne_string_m, only : string_t - use inference_engine_m_, only : inference_engine_t - use kind_parameters_m, only : default_real - use metadata_m, only : metadata_t - use tensor_m, only : tensor_t - use tensor_map_m, only : tensor_map_t - use mini_batch_m, only : mini_batch_t - use training_configuration_m, only : training_configuration_t - use input_output_pair_m, only : input_output_pair_t - implicit none - - private - public :: trainable_engine_t - - type trainable_engine_t(k) - !! Encapsulate the information needed to perform training - integer, kind :: k = default_real - type(tensor_map_t(k)), private :: input_map_, output_map_ !! mappings to/from data ranges used during training - type(activation_t), private :: activation_ - type(metadata_t) metadata_ !! metadata_ encapsulates strings for which default-kind suffices - real(k), allocatable :: w(:,:,:) !! weights - real(k), allocatable :: b(:,:) !! biases - integer, allocatable :: n(:) !! nodes per layer - real(k), allocatable, dimension(:,:) :: a - real(k), allocatable, dimension(:,:,:) :: dcdw, vdw, sdw, vdwc, sdwc - real(k), allocatable, dimension(:,:) :: z, delta, dcdb, vdb, sdb, vdbc, sdbc - contains - generic :: train => default_real_train - procedure, private, non_overridable :: default_real_train - generic :: predict => default_real_predict - generic :: infer => default_real_predict - procedure, private :: default_real_predict - generic :: assert_consistent => default_real_assert_consistent - procedure, private :: default_real_assert_consistent - generic :: num_layers => default_real_num_layers - procedure, private :: default_real_num_layers - generic :: num_inputs => default_real_num_inputs - procedure, private, non_overridable :: default_real_num_inputs - generic :: num_outputs => default_real_num_outputs - procedure, private :: default_real_num_outputs - generic :: to_inference_engine => default_real_to_inference_engine - procedure, private :: default_real_to_inference_engine - generic :: map_to_input_training_range => default_real_map_to_input_training_range - procedure, private :: default_real_map_to_input_training_range - generic :: map_from_input_training_range => default_real_map_from_input_training_range - procedure, private :: default_real_map_from_input_training_range - generic :: map_to_output_training_range => default_real_map_to_output_training_range - procedure, private :: default_real_map_to_output_training_range - generic :: map_from_output_training_range => default_real_map_from_output_training_range - procedure, private :: default_real_map_from_output_training_range - generic :: map_to_training_ranges => default_real_map_to_training_ranges - procedure, private :: default_real_map_to_training_ranges - end type - - integer, parameter :: input_layer = 0 - - interface trainable_engine_t -#ifdef __INTEL_COMPILER - module function construct_trainable_engine_from_padded_arrays(nodes, weights, biases, metadata, input_map, output_map) & -#else - module function construct_from_padded_arrays( nodes, weights, biases, metadata, input_map, output_map) & -#endif - result(trainable_engine) - implicit none - integer, intent(in) :: nodes(input_layer:) - real, intent(in) :: weights(:,:,:), biases(:,:) - type(string_t), intent(in) :: metadata(:) - type(tensor_map_t), intent(in), optional :: input_map, output_map - type(trainable_engine_t) trainable_engine - end function - - module function construct_from_inference_engine(inference_engine) result(trainable_engine) - implicit none - type(inference_engine_t), intent(in) :: inference_engine - type(trainable_engine_t) trainable_engine - end function - - module function perturbed_identity_network(training_configuration, perturbation_magnitude, metadata, input_map, output_map)& - result(trainable_engine) - implicit none - type(training_configuration_t), intent(in) :: training_configuration - type(string_t), intent(in) :: metadata(:) - real, intent(in) :: perturbation_magnitude - type(tensor_map_t) input_map, output_map - type(trainable_engine_t) trainable_engine - end function - - end interface - - interface - - pure module subroutine default_real_assert_consistent(self) - implicit none - class(trainable_engine_t), intent(in) :: self - end subroutine - - pure module subroutine default_real_train(self, mini_batches_arr, cost, adam, learning_rate) - implicit none - class(trainable_engine_t), intent(inout) :: self - type(mini_batch_t), intent(in) :: mini_batches_arr(:) - real, intent(out), allocatable, optional :: cost(:) - logical, intent(in) :: adam - real, intent(in) :: learning_rate - end subroutine - - elemental module function default_real_predict(self, inputs) result(outputs) - implicit none - class(trainable_engine_t), intent(in) :: self - type(tensor_t), intent(in) :: inputs - type(tensor_t) outputs - end function - - elemental module function default_real_num_inputs(self) result(n_in) - implicit none - class(trainable_engine_t), intent(in) :: self - integer n_in - end function - - elemental module function default_real_num_outputs(self) result(n_out) - implicit none - class(trainable_engine_t), intent(in) :: self - integer n_out - end function - - elemental module function default_real_num_layers(self) result(n_layers) - implicit none - class(trainable_engine_t), intent(in) :: self - integer n_layers - end function - - module function default_real_to_inference_engine(self) result(inference_engine) - implicit none - class(trainable_engine_t), intent(in) :: self - type(inference_engine_t) :: inference_engine - end function - - elemental module function default_real_map_to_input_training_range(self, tensor) result(normalized_tensor) - implicit none - class(trainable_engine_t), intent(in) :: self - type(tensor_t), intent(in) :: tensor - type(tensor_t) normalized_tensor - end function - - elemental module function default_real_map_from_input_training_range(self, tensor) result(unnormalized_tensor) - implicit none - class(trainable_engine_t), intent(in) :: self - type(tensor_t), intent(in) :: tensor - type(tensor_t) unnormalized_tensor - end function - - elemental module function default_real_map_to_output_training_range(self, tensor) result(normalized_tensor) - implicit none - class(trainable_engine_t), intent(in) :: self - type(tensor_t), intent(in) :: tensor - type(tensor_t) normalized_tensor - end function - - elemental module function default_real_map_from_output_training_range(self, tensor) result(unnormalized_tensor) - implicit none - class(trainable_engine_t), intent(in) :: self - type(tensor_t), intent(in) :: tensor - type(tensor_t) unnormalized_tensor - end function - - elemental module function default_real_map_to_training_ranges(self, input_output_pair) result(normalized_input_output_pair) - implicit none - class(trainable_engine_t), intent(in) :: self - type(input_output_pair_t), intent(in) :: input_output_pair - type(input_output_pair_t) normalized_input_output_pair - end function - - end interface - -end module trainable_engine_m diff --git a/src/inference_engine/trainable_engine_s.F90 b/src/inference_engine/trainable_engine_s.F90 deleted file mode 100644 index f72096fe6..000000000 --- a/src/inference_engine/trainable_engine_s.F90 +++ /dev/null @@ -1,416 +0,0 @@ -! Copyright (c), The Regents of the University of California -! Terms of use are as specified in LICENSE.txt - -#include "language-support.F90" - -submodule(trainable_engine_m) trainable_engine_s - use assert_m, only : assert - use intrinsic_array_m, only : intrinsic_array_t - use tensor_m, only : tensor_t -#ifdef _CRAYFTN - use input_output_pair_m, only : input_output_pair_t -#endif - implicit none - - integer, parameter :: input_layer = 0 - -contains - - module procedure default_real_num_inputs - n_in = self%n(input_layer) - end procedure - - module procedure default_real_num_layers - n_layers = size(self%n,1) - end procedure - - module procedure default_real_num_outputs - n_out = self%n(ubound(self%n,1)) - end procedure - - module procedure construct_from_inference_engine - -#ifndef _CRAYFTN - associate(exchange => inference_engine%to_exchange()) -#else - use inference_engine_m_, only: exchange_t - type(exchange_t) exchange - exchange = inference_engine%to_exchange() -#endif - trainable_engine%input_map_ = exchange%input_map_ - trainable_engine%output_map_ = exchange%output_map_ - trainable_engine%metadata_ = exchange%metadata_ - trainable_engine%w = exchange%weights_ - trainable_engine%b = exchange%biases_ - trainable_engine%n = exchange%nodes_ - trainable_engine%activation_ = exchange%activation_ -#ifndef _CRAYFTN - end associate -#endif - - end procedure - - module procedure default_real_assert_consistent - - associate( & - fully_allocated=>[allocated(self%w),allocated(self%b),allocated(self%n)] & - ) - call assert(all(fully_allocated),"trainable_engine_s(assert_consistent): fully_allocated",intrinsic_array_t(fully_allocated)) - end associate - - associate(max_width => maxval(self%n), component_dims => [size(self%b,1), size(self%w,1), size(self%w,2)]) - call assert(all(component_dims == max_width), "trainable_engine_s(assert_consistent): conformable arrays", & - intrinsic_array_t([max_width,component_dims])) - end associate - - call assert(lbound(self%n,1)==input_layer, "trainable_engine_s(assert_consistent): n base subsscript", lbound(self%n,1)) - - end procedure - - module procedure default_real_predict - - real, allocatable :: a(:,:) - integer l - - call self%assert_consistent - - associate(w => self%w, b => self%b, n => self%n, output_layer => ubound(self%n,1)) - - allocate(a(maxval(n), input_layer:output_layer)) ! Activations - -#ifndef _CRAYFTN - associate(normalized_inputs => self%input_map_%map_to_training_range(inputs)) -#else - block - type(tensor_t) normalized_inputs - normalized_inputs = self%input_map_%map_to_training_range(inputs) -#endif - a(1:n(input_layer),input_layer) = normalized_inputs%values() -#ifndef _CRAYFTN - end associate -#else - end block -#endif - - feed_forward: & - do l = 1,output_layer - associate(z=>matmul(w(1:n(l),1:n(l-1),l), a(1:n(l-1),l-1)) + b(1:n(l),l)) - a(1:n(l),l) = self%activation_%evaluate(z) - end associate - end do feed_forward - - associate(normalized_outputs => tensor_t(a(1:n(output_layer), output_layer))) - outputs = self%output_map_%map_from_training_range(normalized_outputs) - end associate - - end associate - - end procedure - - module procedure default_real_train - integer l, batch, mini_batch_size, pair - type(tensor_t), allocatable :: inputs(:), expected_outputs(:) - - call self%assert_consistent - - if (.not. allocated(self%dcdw)) allocate(self%dcdw, mold=self%w) ! Gradient of cost function with respect to weights - if (.not. allocated(self%vdw)) allocate(self%vdw, mold=self%w) - if (.not. allocated(self%sdw)) allocate(self%sdw, mold=self%w) - if (.not. allocated(self%vdwc)) allocate(self%vdwc, mold=self%w) - if (.not. allocated(self%sdwc)) allocate(self%sdwc, mold=self%w) - - if (.not. allocated(self%dcdb)) allocate(self%dcdb, mold=self%b) ! Gradient of cost function with respect with biases - if (.not. allocated(self%vdb)) allocate(self%vdb, mold=self%b) - if (.not. allocated(self%sdb)) allocate(self%sdb, mold=self%b) - if (.not. allocated(self%vdbc)) allocate(self%vdbc, mold=self%b) - if (.not. allocated(self%sdbc)) allocate(self%sdbc, mold=self%b) - - associate(output_layer => ubound(self%n,1)) - -#if F2023_LOCALITY || F2018_LOCALITY - if (.not. allocated(self%z)) allocate(self%z, mold=self%b) ! z-values: Sum z_j^l = w_jk^{l} a_k^{l-1} + b_j^l - if (.not. allocated(self%delta)) allocate(self%delta, mold=self%b) - if (.not. allocated(self%a)) allocate(self%a(maxval(self%n), input_layer:output_layer)) ! Activations -#endif - - associate( & - a => self%a, dcdw => self%dcdw, vdw => self%vdw, sdw => self%sdw, vdwc => self%vdwc, sdwc => self%sdwc, & - z => self%z, delta => self%delta, dcdb => self%dcdb, vdb => self%vdb, sdb => self%sdb, vdbc => self%vdbc, sdbc=> self%sdbc & - ) - vdw = 0.; sdw = 1.; vdb = 0.; sdb = 1. - - associate(w => self%w, b => self%b, n => self%n, num_mini_batches => size(mini_batches_arr)) - - if (present(cost)) allocate(cost(num_mini_batches)) - - iterate_across_batches: & - do batch = 1, num_mini_batches - - dcdw = 0.; dcdb = 0. - -#ifndef _CRAYFTN - associate(input_output_pairs => mini_batches_arr(batch)%input_output_pairs()) -#else - block - type(input_output_pair_t), allocatable :: input_output_pairs(:) - input_output_pairs = mini_batches_arr(batch)%input_output_pairs() -#endif - inputs = input_output_pairs%inputs() - expected_outputs = input_output_pairs%expected_outputs() - mini_batch_size = size(input_output_pairs) -#ifndef _CRAYFTN - end associate -#else - end block -#endif - block - real, allocatable :: pair_cost(:) - if (present(cost)) allocate(pair_cost(mini_batch_size)) - -#if F2023_LOCALITY - iterate_through_batch: & - do concurrent (pair = 1:mini_batch_size) local(a,z,delta) reduce(+: dcdb, dcdw) - -#elif F2018_LOCALITY - - reduce_gradients: & - block - real reduce_dcdb(size(dcdb,1),size(dcdb,2),mini_batch_size) - real reduce_dcdw(size(dcdw,1),size(dcdw,2),size(dcdw,3),mini_batch_size) - reduce_dcdb = 0. - reduce_dcdw = 0. - - iterate_through_batch: & - do concurrent (pair = 1:mini_batch_size) local(a,z,delta) - -#else - - reduce_gradients: & - block - real reduce_dcdb(size(dcdb,1),size(dcdb,2),mini_batch_size) - real reduce_dcdw(size(dcdw,1),size(dcdw,2),size(dcdw,3),mini_batch_size) - reduce_dcdb = 0. - reduce_dcdw = 0. - - iterate_through_batch: & - do concurrent (pair = 1:mini_batch_size) - - iteration: & - block - - real a(maxval(self%n), input_layer:output_layer) ! Activations - real z(size(b,1),size(b,2)), delta(size(b,1),size(b,2)) -#endif - - a(1:self%num_inputs(), input_layer) = inputs(pair)%values() - - feed_forward: & - do l = 1,output_layer - z(1:n(l),l) = matmul(w(1:n(l),1:n(l-1),l), a(1:n(l-1),l-1)) + b(1:n(l),l) - a(1:n(l),l) = self%activation_%evaluate(z(1:n(l),l)) - end do feed_forward - - associate(y => expected_outputs(pair)%values()) - if (present(cost)) pair_cost(pair) = sum((y(1:n(output_layer))-a(1:n(output_layer),output_layer))**2) - - delta(1:n(output_layer),output_layer) = (a(1:n(output_layer),output_layer) - y(1:n(output_layer))) & - * self%activation_%differentiate(z(1:n(output_layer),output_layer)) - end associate - - associate(n_hidden => self%num_layers()-2) - back_propagate_error: & - do l = n_hidden,1,-1 - delta(1:n(l),l) = matmul(transpose(w(1:n(l+1),1:n(l),l+1)), delta(1:n(l+1),l+1)) & - * self%activation_%differentiate(z(1:n(l),l)) - end do back_propagate_error - end associate - - block - integer j - sum_gradients: & - do l = 1,output_layer -#if F2023_LOCALITY - dcdb(1:n(l),l) = dcdb(1:n(l),l) + delta(1:n(l),l) - do concurrent(j = 1:n(l)) reduce(+: dcdw) - dcdw(j,1:n(l-1),l) = dcdw(j,1:n(l-1),l) + a(1:n(l-1),l-1)*delta(j,l) - end do -#else - reduce_dcdb(1:n(l),l,pair) = reduce_dcdb(1:n(l),l,pair) + delta(1:n(l),l) - do j = 1,n(l) - reduce_dcdw(j,1:n(l-1),l,pair) = reduce_dcdw(j,1:n(l-1),l,pair) + a(1:n(l-1),l-1)*delta(j,l) - end do -#endif - end do sum_gradients - end block - -#if F2023_LOCALITY - end do iterate_through_batch -#elif F2018_LOCALITY - - end do iterate_through_batch - dcdb = sum(reduce_dcdb,dim=3) - dcdw = sum(reduce_dcdw,dim=4) - - end block reduce_gradients -#else - end block iteration - end do iterate_through_batch - dcdb = sum(reduce_dcdb,dim=3) - dcdw = sum(reduce_dcdw,dim=4) - - end block reduce_gradients -#endif - - if (present(cost)) cost(batch) = sum(pair_cost)/(2*mini_batch_size) - end block - - if (adam) then - block - ! Adam parameters - real, parameter :: beta(*) = [.9, .999] - real, parameter :: obeta(*) = [1.- beta(1), 1.- beta(2)] - real, parameter :: epsilon = 1.E-08 - - associate(alpha => learning_rate) - adam_adjust_weights_and_biases: & - do concurrent(l = 1:output_layer) - dcdw(1:n(l),1:n(l-1),l) = dcdw(1:n(l),1:n(l-1),l)/(mini_batch_size) - vdw(1:n(l),1:n(l-1),l) = beta(1)*vdw(1:n(l),1:n(l-1),l) + obeta(1)*dcdw(1:n(l),1:n(l-1),l) - sdw (1:n(l),1:n(l-1),l) = beta(2)*sdw(1:n(l),1:n(l-1),l) + obeta(2)*(dcdw(1:n(l),1:n(l-1),l)**2) - vdwc(1:n(l),1:n(l-1),l) = vdw(1:n(l),1:n(l-1),l)/(1.- beta(1)**num_mini_batches) - sdwc(1:n(l),1:n(l-1),l) = sdw(1:n(l),1:n(l-1),l)/(1.- beta(2)**num_mini_batches) - w(1:n(l),1:n(l-1),l) = w(1:n(l),1:n(l-1),l) & - - alpha*vdwc(1:n(l),1:n(l-1),l)/(sqrt(sdwc(1:n(l),1:n(l-1),l))+epsilon) ! Adjust weights - - dcdb(1:n(l),l) = dcdb(1:n(l),l)/mini_batch_size - vdb(1:n(l),l) = beta(1)*vdb(1:n(l),l) + obeta(1)*dcdb(1:n(l),l) - sdb(1:n(l),l) = beta(2)*sdb(1:n(l),l) + obeta(2)*(dcdb(1:n(l),l)**2) - vdbc(1:n(l),l) = vdb(1:n(l),l)/(1. - beta(1)**num_mini_batches) - sdbc(1:n(l),l) = sdb(1:n(l),l)/(1. - beta(2)**num_mini_batches) - b(1:n(l),l) = b(1:n(l),l) - alpha*vdbc(1:n(l),l)/(sqrt(sdbc(1:n(l),l))+epsilon) ! Adjust weights - end do adam_adjust_weights_and_biases - end associate - end block - else - associate(eta => learning_rate) - adjust_weights_and_biases: & - do concurrent(l = 1:output_layer) - dcdb(1:n(l),l) = dcdb(1:n(l),l)/mini_batch_size - b(1:n(l),l) = b(1:n(l),l) - eta*dcdb(1:n(l),l) ! Adjust biases - dcdw(1:n(l),1:n(l-1),l) = dcdw(1:n(l),1:n(l-1),l)/mini_batch_size - w(1:n(l),1:n(l-1),l) = w(1:n(l),1:n(l-1),l) - eta*dcdw(1:n(l),1:n(l-1),l) ! Adjust weights - end do adjust_weights_and_biases - end associate - end if - end do iterate_across_batches - end associate - end associate - end associate - end procedure - -#ifdef __INTEL_COMPILER - module procedure construct_trainable_engine_from_padded_arrays -#else - module procedure construct_from_padded_arrays -#endif - - trainable_engine%metadata_ = metadata_t(metadata(1),metadata(2),metadata(3),metadata(4),metadata(5)) - trainable_engine%n = nodes - trainable_engine%w = weights - trainable_engine%b = biases - trainable_engine%activation_ = activation_t(metadata(4)%string()) - - block - integer i - - if (present(input_map)) then - trainable_engine%input_map_ = input_map - else - associate(num_inputs => nodes(lbound(nodes,1))) - trainable_engine%input_map_ = tensor_map_t("inputs", minima=[(0., i=1,num_inputs)], maxima=[(1., i=1,num_inputs)]) - end associate - end if - - if (present(output_map)) then - trainable_engine%output_map_ = output_map - else - associate(num_outputs => nodes(ubound(nodes,1))) - trainable_engine%output_map_ = tensor_map_t("outputs", minima=[(0., i=1,num_outputs)], maxima=[(1., i=1,num_outputs)]) - end associate - end if - end block - - call trainable_engine%assert_consistent - end procedure - - module procedure default_real_to_inference_engine - inference_engine = inference_engine_t(self%metadata_%strings(), self%w, self%b, self%n, self%input_map_, self%output_map_) - end procedure - - module procedure perturbed_identity_network - - integer k, l - real, allocatable :: identity(:,:,:), w_harvest(:,:,:), b_harvest(:,:) - - associate(n=>training_configuration%nodes_per_layer()) - associate(n_max => maxval(n), layers => size(n)) - - identity = reshape( [( [(e(k,n_max), k=1,n_max)], l = 1, layers-1 )], [n_max, n_max, layers-1]) - allocate(w_harvest, mold = identity) - allocate(b_harvest(size(identity,1), size(identity,3))) - call random_number(w_harvest) - call random_number(b_harvest) - - associate( & - w => identity + perturbation_magnitude*(w_harvest-0.5)/0.5, & - b => perturbation_magnitude*(b_harvest-0.5)/0.5 & - ) - trainable_engine = trainable_engine_t( & - nodes = n, weights = w, biases = b, metadata = metadata, input_map = input_map, output_map = output_map & - ) - end associate - end associate - end associate - - contains - - pure function e(j,n) result(unit_vector) - integer, intent(in) :: j, n - integer k - real, allocatable :: unit_vector(:) - unit_vector = real([(merge(1,0,j==k),k=1,n)]) - end function - - end procedure - - module procedure default_real_map_to_training_ranges - associate( & - inputs => input_output_pair%inputs(), & - expected_outputs => input_output_pair%expected_outputs() & - ) - associate( & - normalized_inputs => self%input_map_%map_to_training_range(inputs), & - normalized_outputs => self%output_map_%map_to_training_range(expected_outputs) & - ) - normalized_input_output_pair = input_output_pair_t(normalized_inputs, normalized_outputs) - end associate - end associate - end procedure - - module procedure default_real_map_to_input_training_range - normalized_tensor = self%input_map_%map_to_training_range(tensor) - end procedure - - module procedure default_real_map_from_input_training_range - unnormalized_tensor = self%input_map_%map_from_training_range(tensor) - end procedure - - module procedure default_real_map_to_output_training_range - normalized_tensor = self%output_map_%map_to_training_range(tensor) - end procedure - - module procedure default_real_map_from_output_training_range - unnormalized_tensor = self%output_map_%map_from_training_range(tensor) - end procedure - -end submodule trainable_engine_s diff --git a/src/inference_engine/trainable_network_m.f90 b/src/inference_engine/trainable_network_m.f90 index 3338f0f69..86380eb0f 100644 --- a/src/inference_engine/trainable_network_m.f90 +++ b/src/inference_engine/trainable_network_m.f90 @@ -1,7 +1,11 @@ module trainable_network_m use inference_engine_m_, only : inference_engine_t, workspace_t - use mini_batch_m, only : mini_batch_t + use input_output_pair_m, only : input_output_pair_t + use julienne_m, only : string_t use kind_parameters_m, only : default_real + use mini_batch_m, only : mini_batch_t + use training_configuration_m, only : training_configuration_t + use tensor_map_m, only : tensor_map_t implicit none private @@ -13,7 +17,9 @@ module trainable_network_m type(workspace_t), private :: workspace_ contains generic :: train => default_real_train - procedure, non_overridable :: default_real_train + procedure, private, non_overridable :: default_real_train + generic :: map_to_training_ranges => default_real_map_to_training_ranges + procedure, private, non_overridable :: default_real_map_to_training_ranges end type interface trainable_network_t @@ -23,7 +29,17 @@ pure module function default_real_network(inference_engine) result(trainable_net type(inference_engine_t), intent(in) :: inference_engine type(trainable_network_t) trainable_network end function - + + module function perturbed_identity_network(training_configuration, perturbation_magnitude, metadata, input_map, output_map) & + result(trainable_network) + implicit none + type(training_configuration_t), intent(in) :: training_configuration + type(string_t), intent(in) :: metadata(:) + real, intent(in) :: perturbation_magnitude + type(tensor_map_t) input_map, output_map + type(trainable_network_t) trainable_network + end function + end interface interface @@ -37,6 +53,13 @@ pure module subroutine default_real_train(self, mini_batches_arr, cost, adam, le real, intent(in) :: learning_rate end subroutine + elemental module function default_real_map_to_training_ranges(self, input_output_pair) result(normalized_input_output_pair) + implicit none + class(trainable_network_t), intent(in) :: self + type(input_output_pair_t), intent(in) :: input_output_pair + type(input_output_pair_t) normalized_input_output_pair + end function + end interface end module trainable_network_m diff --git a/src/inference_engine/trainable_network_s.f90 b/src/inference_engine/trainable_network_s.f90 index d6e1d83a6..a4e3caa6c 100644 --- a/src/inference_engine/trainable_network_s.f90 +++ b/src/inference_engine/trainable_network_s.f90 @@ -9,7 +9,53 @@ end procedure module procedure default_real_train - call self%inference_engine_t%default_real_learn(mini_batches_arr, cost, adam, learning_rate, self%workspace_) + call self%learn(mini_batches_arr, cost, adam, learning_rate, self%workspace_) end procedure + module procedure default_real_map_to_training_ranges + associate(inputs => input_output_pair%inputs(), expected_outputs => input_output_pair%expected_outputs()) + associate( & + normalized_inputs => self%input_map_%map_to_training_range(inputs), & + normalized_outputs => self%output_map_%map_to_training_range(expected_outputs) & + ) + normalized_input_output_pair = input_output_pair_t(normalized_inputs, normalized_outputs) + end associate + end associate + end procedure + + module procedure perturbed_identity_network + + integer k, l + real, allocatable :: identity(:,:,:), w_harvest(:,:,:), b_harvest(:,:) + + associate(n=>training_configuration%nodes_per_layer()) + associate(n_max => maxval(n), layers => size(n)) + + identity = reshape( [( [(e(k,n_max), k=1,n_max)], l = 1, layers-1 )], [n_max, n_max, layers-1]) + allocate(w_harvest, mold = identity) + allocate(b_harvest(size(identity,1), size(identity,3))) + call random_number(w_harvest) + call random_number(b_harvest) + + associate( & + w => identity + perturbation_magnitude*(w_harvest-0.5)/0.5, & + b => perturbation_magnitude*(b_harvest-0.5)/0.5 & + ) + trainable_network = trainable_network_t( & + inference_engine_t(nodes=n, weights=w, biases=b, metadata=metadata, input_map=input_map, output_map=output_map) & + ) + end associate + end associate + end associate + + contains + + pure function e(j,n) result(unit_vector) + integer, intent(in) :: j, n + integer k + real, allocatable :: unit_vector(:) + unit_vector = real([(merge(1,0,j==k),k=1,n)]) + end function + + end procedure end submodule trainable_network_s diff --git a/src/inference_engine_m.f90 b/src/inference_engine_m.f90 index 5b6820d1e..45a803ab7 100644 --- a/src/inference_engine_m.f90 +++ b/src/inference_engine_m.f90 @@ -14,7 +14,6 @@ module inference_engine_m use tensor_m, only : tensor_t use tensor_map_m, only : tensor_map_t use trainable_network_m, only : trainable_network_t - use trainable_engine_m, only : trainable_engine_t use training_configuration_m, only : training_configuration_t use ubounds_m, only : ubounds_t implicit none diff --git a/test/main.F90 b/test/main.F90 index 1b7d8d4ec..a21444d24 100644 --- a/test/main.F90 +++ b/test/main.F90 @@ -3,7 +3,7 @@ program main use inference_engine_test_m, only : inference_engine_test_t use asymmetric_engine_test_m, only : asymmetric_engine_test_t - use trainable_engine_test_m, only : trainable_engine_test_t + use trainable_network_test_m, only : trainable_network_test_t use metadata_test_m, only : metadata_test_t use hyperparameters_test_m, only : hyperparameters_test_t use network_configuration_test_m, only : network_configuration_test_t @@ -15,7 +15,7 @@ program main type(inference_engine_test_t) inference_engine_test type(asymmetric_engine_test_t) asymmetric_engine_test - type(trainable_engine_test_t) trainable_engine_test + type(trainable_network_test_t) trainable_network_test type(hyperparameters_test_t) hyperparameters_test type(metadata_test_t) metadata_test type(network_configuration_test_t) network_configuration_test @@ -49,7 +49,7 @@ program main call tensor_test%report(passes, tests) call asymmetric_engine_test%report(passes, tests) call inference_engine_test%report(passes, tests) - call trainable_engine_test%report(passes, tests) + call trainable_network_test%report(passes, tests) call cpu_time(t_finish) print * diff --git a/test/trainable_engine_test_m.F90 b/test/trainable_network_test_m.F90 similarity index 86% rename from test/trainable_engine_test_m.F90 rename to test/trainable_network_test_m.F90 index 929d40ea6..92518d74d 100644 --- a/test/trainable_engine_test_m.F90 +++ b/test/trainable_network_test_m.F90 @@ -1,6 +1,6 @@ ! Copyright (c), The Regents of the University of California ! Terms of use are as specified in LICENSE.txt -module trainable_engine_test_m +module trainable_network_test_m !! Define inference tests and procedures required for reporting results ! External dependencies @@ -12,11 +12,11 @@ module trainable_engine_test_m #endif ! Internal dependencies - use inference_engine_m, only : trainable_engine_t, tensor_t, input_output_pair_t, mini_batch_t, shuffle + use inference_engine_m, only : trainable_network_t, inference_engine_t, tensor_t, input_output_pair_t, mini_batch_t, shuffle implicit none private - public :: trainable_engine_test_t + public :: trainable_network_test_t type, extends(vector_function_strategy_t) :: and_gate_test_function_t contains @@ -38,7 +38,7 @@ module trainable_engine_test_m procedure, nopass :: vector_function => xor_gate_with_random_weights end type - type, extends(test_t) :: trainable_engine_test_t + type, extends(test_t) :: trainable_network_test_t contains procedure, nopass :: subject procedure, nopass :: results @@ -60,7 +60,7 @@ function map_i(inputs) result(expected_outputs) pure function subject() result(specimen) character(len=:), allocatable :: specimen - specimen = "A trainable_engine_t" + specimen = "A trainable_network_t object" end function function results() result(test_results) @@ -163,7 +163,7 @@ subroutine print_truth_table(gate_name, gate_function_ptr, test_inputs, actual_o integer i call assert( size(test_inputs) == size(actual_outputs), & - "trainable_engine_test_m(print_truth_table): size(test_inputs) == size(actual_outputs)") + "trainable_network_test_m(print_truth_table): size(test_inputs) == size(actual_outputs)") print *,"_______" // gate_name // "_______" @@ -173,8 +173,8 @@ subroutine print_truth_table(gate_name, gate_function_ptr, test_inputs, actual_o end do end subroutine - function two_zeroed_hidden_layers() result(trainable_engine) - type(trainable_engine_t) trainable_engine + function two_zeroed_hidden_layers() result(trainable_network) + type(trainable_network_t) trainable_network integer, parameter :: inputs = 2, outputs = 1, hidden = 3 ! number of neurons in input, output, and hidden layers integer, parameter :: neurons(*) = [inputs, hidden, hidden, outputs] ! neurons per layer integer, parameter :: max_neurons = maxval(neurons), layers=size(neurons) ! max layer width, number of layers @@ -183,14 +183,14 @@ function two_zeroed_hidden_layers() result(trainable_engine) w = 0. b = 0. - trainable_engine = trainable_engine_t( & - nodes = neurons, weights = w, biases = b, metadata = & - [string_t("2-hide|3-wide"), string_t("Damian Rouson"), string_t("2023-06-30"), string_t("sigmoid"), string_t("false")] & - ) + trainable_network = trainable_network_t( inference_engine_t( & + nodes = neurons, weights = w, biases = b & + ,metadata = [string_t("2-hide|3-wide"), string_t("Rouson"), string_t("2023-06-30"), string_t("sigmoid"), string_t("false")] & + )) end function - function two_random_hidden_layers() result(trainable_engine) - type(trainable_engine_t) trainable_engine + function two_random_hidden_layers() result(trainable_network) + type(trainable_network_t) trainable_network integer, parameter :: inputs = 2, outputs = 1, hidden = 3 ! number of neurons in input, output, and hidden layers integer, parameter :: neurons(*) = [inputs, hidden, hidden, outputs] ! neurons per layer integer, parameter :: max_neurons = maxval(neurons), layers=size(neurons) ! max layer width, number of layers @@ -201,10 +201,10 @@ function two_random_hidden_layers() result(trainable_engine) b = 2*b w = 2*w - trainable_engine = trainable_engine_t( & - nodes = neurons, weights = w, biases = b, metadata = & - [string_t("2-hide|3-wide"), string_t("Damian Rouson"), string_t("2023-06-30"), string_t("sigmoid"), string_t("false")] & - ) + trainable_network = trainable_network_t( inference_engine_t( & + nodes = neurons, weights = w, biases = b & + ,metadata = [string_t("2-hide|3-wide"), string_t("Rouson"), string_t("2023-06-30"), string_t("sigmoid"), string_t("false")] & + )) end function function and_gate_with_skewed_training_data() result(test_passes) @@ -212,7 +212,7 @@ function and_gate_with_skewed_training_data() result(test_passes) type(mini_batch_t), allocatable :: mini_batches(:) type(tensor_t), allocatable, dimension(:,:) :: training_inputs, training_outputs type(tensor_t), allocatable, dimension(:) :: tmp, tmp2, test_inputs, expected_test_outputs, actual_outputs - type(trainable_engine_t) trainable_engine + type(trainable_network_t) trainable_network real, parameter :: tolerance = 1.E-02 real, allocatable :: harvest(:,:,:) integer, parameter :: num_inputs=2, mini_batch_size = 1, num_iterations=20000 @@ -231,13 +231,13 @@ function and_gate_with_skewed_training_data() result(test_passes) training_outputs = reshape(tmp2, [mini_batch_size, num_iterations]) mini_batches = [(mini_batch_t(input_output_pair_t(training_inputs(:,iter), training_outputs(:,iter))), iter=1, num_iterations)] - trainable_engine = two_zeroed_hidden_layers() + trainable_network = two_zeroed_hidden_layers() - call trainable_engine%train(mini_batches, adam=.false., learning_rate=1.5) + call trainable_network%train(mini_batches, adam=.false., learning_rate=1.5) test_inputs = [tensor_t([true,true]), tensor_t([false,true]), tensor_t([true,false]), tensor_t([false,false])] expected_test_outputs = [(and(test_inputs(i)), i=1, size(test_inputs))] - actual_outputs = trainable_engine%infer(test_inputs) + actual_outputs = trainable_network%infer(test_inputs) test_passes = [(abs(actual_outputs(i)%values() - expected_test_outputs(i)%values()) < tolerance, i=1, size(actual_outputs))] contains @@ -255,7 +255,7 @@ function not_and_gate_with_skewed_training_data() result(test_passes) type(mini_batch_t), allocatable :: mini_batches(:) type(tensor_t), allocatable :: training_inputs(:,:), tmp(:), test_inputs(:) type(tensor_t), allocatable :: training_outputs(:,:), expected_test_outputs(:), tmp2(:) - type(trainable_engine_t) trainable_engine + type(trainable_network_t) trainable_network type(tensor_t), allocatable :: actual_outputs(:) real, parameter :: tolerance = 1.E-02 real, allocatable :: harvest(:,:,:) @@ -275,13 +275,13 @@ function not_and_gate_with_skewed_training_data() result(test_passes) training_outputs = reshape(tmp2, [mini_batch_size, num_iterations]) mini_batches = [(mini_batch_t(input_output_pair_t(training_inputs(:,iter), training_outputs(:,iter))), iter=1, num_iterations)] - trainable_engine = two_zeroed_hidden_layers() + trainable_network = two_zeroed_hidden_layers() - call trainable_engine%train(mini_batches, adam=.false., learning_rate=1.5) + call trainable_network%train(mini_batches, adam=.false., learning_rate=1.5) test_inputs = [tensor_t([true,true]), tensor_t([false,true]), tensor_t([true,false]), tensor_t([false,false])] expected_test_outputs = [(not_and(test_inputs(i)), i=1, size(test_inputs))] - actual_outputs = trainable_engine%infer(test_inputs) + actual_outputs = trainable_network%infer(test_inputs) test_passes = [(abs(actual_outputs(i)%values() - expected_test_outputs(i)%values()) < tolerance, i=1, size(actual_outputs))] contains @@ -299,7 +299,7 @@ function or_gate_with_random_weights() result(test_passes) type(mini_batch_t), allocatable :: mini_batches(:) type(tensor_t), allocatable :: training_inputs(:,:), test_inputs(:), actual_outputs(:) type(tensor_t), allocatable :: training_outputs(:,:), expected_test_outputs(:) - type(trainable_engine_t) trainable_engine + type(trainable_network_t) trainable_network real, parameter :: tolerance = 1.E-02 real, allocatable :: harvest(:,:,:) integer, parameter :: num_inputs=2, mini_batch_size = 1, num_iterations=50000 @@ -320,13 +320,13 @@ function or_gate_with_random_weights() result(test_passes) mini_batches(iter) = mini_batch_t(input_output_pair_t(training_inputs(:,iter), training_outputs(:,iter))) end do - trainable_engine = two_random_hidden_layers() + trainable_network = two_random_hidden_layers() - call trainable_engine%train(mini_batches, adam=.false., learning_rate=1.5) + call trainable_network%train(mini_batches, adam=.false., learning_rate=1.5) test_inputs = [tensor_t([true,true]), tensor_t([false,true]), tensor_t([true,false]), tensor_t([false,false])] expected_test_outputs = [(or(test_inputs(i)), i=1, size(test_inputs))] - actual_outputs = trainable_engine%infer(test_inputs) + actual_outputs = trainable_network%infer(test_inputs) test_passes = [(abs(actual_outputs(i)%values() - expected_test_outputs(i)%values()) < tolerance, i=1, size(actual_outputs))] contains @@ -344,7 +344,7 @@ function xor_gate_with_random_weights() result(test_passes) type(mini_batch_t), allocatable :: mini_batches(:) type(tensor_t), allocatable, dimension(:,:) :: training_inputs, training_outputs type(tensor_t), allocatable, dimension(:) :: actual_outputs, test_inputs, expected_test_outputs - type(trainable_engine_t) trainable_engine + type(trainable_network_t) trainable_network real, parameter :: tolerance = 1.E-02 real, allocatable :: harvest(:,:,:) #ifdef __flang__ @@ -375,16 +375,16 @@ function xor_gate_with_random_weights() result(test_passes) mini_batches(iter) = mini_batch_t(input_output_pair_t(training_inputs(:,iter), training_outputs(:,iter))) end do - trainable_engine = two_random_hidden_layers() + trainable_network = two_random_hidden_layers() - call trainable_engine%train(mini_batches, adam=.true., learning_rate=1.5) + call trainable_network%train(mini_batches, adam=.true., learning_rate=1.5) test_inputs = [tensor_t([true,true]), tensor_t([false,true]), tensor_t([true,false]), tensor_t([false,false])] block integer i expected_test_outputs = [(local_xor(test_inputs(i)), i=1, size(test_inputs))] - actual_outputs = trainable_engine%infer(test_inputs) + actual_outputs = trainable_network%infer(test_inputs) test_passes = [(abs(actual_outputs(i)%values() - expected_test_outputs(i)%values()) < tolerance, i=1, size(actual_outputs))] end block @@ -400,8 +400,8 @@ pure function local_xor(inputs) result(expected_outputs) end function - function perturbed_identity_network(perturbation_magnitude) result(trainable_engine) - type(trainable_engine_t) trainable_engine + function perturbed_identity_network(perturbation_magnitude) result(trainable_network) + type(trainable_network_t) trainable_network real, intent(in) :: perturbation_magnitude integer, parameter :: nodes_per_layer(*) = [2, 2, 2, 2] integer, parameter :: max_n = maxval(nodes_per_layer), layers = size(nodes_per_layer) @@ -420,12 +420,12 @@ function perturbed_identity_network(perturbation_magnitude) result(trainable_eng call random_number(harvest) harvest = perturbation_magnitude*harvest - trainable_engine = trainable_engine_t( & + trainable_network = trainable_network_t( inference_engine_t( & nodes = nodes_per_layer, & weights = identity + harvest , & biases = reshape([real:: [0,0], [0,0], [0,0]], [max_n, layers-1]), & metadata = [string_t("Identity"), string_t("Damian Rouson"), string_t("2023-09-18"), string_t("relu"), string_t("false")] & - ) + )) end function function preserves_identity_mapping() result(test_passes) @@ -433,17 +433,17 @@ function preserves_identity_mapping() result(test_passes) type(mini_batch_t), allocatable :: mini_batches(:) type(input_output_pair_t), allocatable :: input_output_pairs(:) type(tensor_t), allocatable :: inputs(:) - type(trainable_engine_t) trainable_engine + type(trainable_network_t) trainable_network type(bin_t), allocatable :: bins(:) real, allocatable :: cost(:) integer, parameter :: num_pairs = 100, num_epochs = 100, n_bins = 3 integer i, bin, epoch - trainable_engine = perturbed_identity_network(perturbation_magnitude=0.) + trainable_network = perturbed_identity_network(perturbation_magnitude=0.) - associate(num_inputs => trainable_engine%num_inputs(), num_outputs => trainable_engine%num_outputs()) + associate(num_inputs => trainable_network%num_inputs(), num_outputs => trainable_network%num_outputs()) - call assert(num_inputs == num_outputs,"trainable_engine_test_m(identity_mapping): # inputs == # outputs", & + call assert(num_inputs == num_outputs,"trainable_network_test_m(identity_mapping): # inputs == # outputs", & intrinsic_array_t([num_inputs, num_outputs]) & ) #ifdef _CRAYFTN @@ -461,16 +461,16 @@ function preserves_identity_mapping() result(test_passes) do epoch = 1,num_epochs mini_batches = [(mini_batch_t(input_output_pairs(bins(bin)%first():bins(bin)%last())), bin = 1, size(bins))] - call trainable_engine%train(mini_batches, cost, adam=.false., learning_rate=1.5) + call trainable_network%train(mini_batches, cost, adam=.false., learning_rate=1.5) end do block real, parameter :: tolerance = 1.E-06 #if defined _CRAYFTN || __GFORTRAN__ type(tensor_t), allocatable :: network_outputs(:) - network_outputs = trainable_engine%infer(inputs) + network_outputs = trainable_network%infer(inputs) #else - associate(network_outputs => trainable_engine%infer(inputs)) + associate(network_outputs => trainable_network%infer(inputs)) #endif test_passes = maxval(abs([(network_outputs(i)%values() - inputs(i)%values(), i=1,num_pairs)])) < tolerance #if defined _CRAYFTN || __GFORTRAN__ @@ -492,7 +492,7 @@ function perturbed_identity_converges() result(test_passes) type(mini_batch_t), allocatable :: mini_batches(:) type(input_output_pair_t), allocatable :: input_output_pairs(:) type(tensor_t), allocatable :: inputs(:) - type(trainable_engine_t) trainable_engine + type(trainable_network_t) trainable_network type(bin_t), allocatable :: bins(:) real, allocatable :: cost(:) integer, parameter :: num_pairs = 6 @@ -500,11 +500,11 @@ function perturbed_identity_converges() result(test_passes) integer, parameter :: num_bins = 5 integer i, bin, epoch - trainable_engine = perturbed_identity_network(perturbation_magnitude=0.1) + trainable_network = perturbed_identity_network(perturbation_magnitude=0.1) - associate(num_inputs => trainable_engine%num_inputs(), num_outputs => trainable_engine%num_outputs()) + associate(num_inputs => trainable_network%num_inputs(), num_outputs => trainable_network%num_outputs()) - call assert(num_inputs == num_outputs,"trainable_engine_test_m(identity_mapping): # inputs == # outputs", & + call assert(num_inputs == num_outputs,"trainable_network_test_m(identity_mapping): # inputs == # outputs", & intrinsic_array_t([num_inputs, num_outputs]) & ) #ifdef _CRAYFTN @@ -523,16 +523,16 @@ function perturbed_identity_converges() result(test_passes) do epoch = 1,num_epochs call shuffle(input_output_pairs) mini_batches = [(mini_batch_t(input_output_pairs(bins(bin)%first():bins(bin)%last())), bin = 1, size(bins))] - call trainable_engine%train(mini_batches, cost, adam=.true., learning_rate=1.5) + call trainable_network%train(mini_batches, cost, adam=.true., learning_rate=1.5) end do block real, parameter :: tolerance = 1.E-06 #if defined _CRAYFTN || __GFORTRAN__ type(tensor_t), allocatable :: network_outputs(:) - network_outputs = trainable_engine%infer(inputs) + network_outputs = trainable_network%infer(inputs) #else - associate(network_outputs => trainable_engine%infer(inputs)) + associate(network_outputs => trainable_network%infer(inputs)) #endif test_passes = maxval(abs([(network_outputs(i)%values() - inputs(i)%values(), i=1,num_pairs)])) < tolerance #if defined _CRAYFTN || __GFORTRAN__ @@ -545,4 +545,4 @@ function perturbed_identity_converges() result(test_passes) end function -end module trainable_engine_test_m +end module trainable_network_test_m From e89c7b4c6c7e6d278f4b17a52dd306bad84c2b8d Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Mon, 14 Oct 2024 01:12:32 -0700 Subject: [PATCH 087/105] fix(train-cloud-micro):let write_lines() open file --- demo/app/train-cloud-microphysics.F90 | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/demo/app/train-cloud-microphysics.F90 b/demo/app/train-cloud-microphysics.F90 index e96038f5d..9a8c5efcc 100644 --- a/demo/app/train-cloud-microphysics.F90 +++ b/demo/app/train-cloud-microphysics.F90 @@ -428,15 +428,9 @@ subroutine read_train_write(training_configuration, args, plot_file) print *, epoch, average_cost write(plot_file%plot_unit,*) epoch, average_cost - block - integer net_unit - - open(newunit=net_unit, file=network_file, form='formatted', status='unknown', iostat=io_status, action='write') - associate(json_file => trainable_network%to_json()) - call json_file%write_lines(string_t(network_file)) - end associate - close(net_unit) - end block + associate(json_file => trainable_network%to_json()) + call json_file%write_lines(string_t(network_file)) + end associate end if image_1_maybe_writes From b23524df5a4b28bc2ef42345fee91f9c4372dc2c Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Mon, 14 Oct 2024 02:14:25 -0700 Subject: [PATCH 088/105] chore(inference_engine): -> neural_network/fiats This commit 1. Renames the project from inference-engine to fiats. 2. Renames inference_engine_t to neural_network_t. --- demo/app/infer-aerosol.f90 | 8 +- demo/app/tensor-statistics.f90 | 2 +- demo/app/train-cloud-microphysics.F90 | 10 +- demo/fpm.toml | 10 +- demo/include | 1 + demo/setup.sh | 44 +++---- example/concurrent-inferences.f90 | 26 ++--- example/learn-addition.F90 | 14 +-- example/learn-exponentiation.F90 | 13 +-- example/learn-multiplication.F90 | 13 +-- example/learn-power-series.F90 | 13 +-- example/learn-saturated-mixing-ratio.F90 | 13 +-- example/print-training-configuration.F90 | 2 +- example/read-query-infer.f90 | 20 ++-- .../saturated_mixing_ratio_m.f90 | 2 +- example/train-and-write.F90 | 12 +- example/write-read-infer.F90 | 36 +++--- fpm.toml | 4 +- setup.sh | 14 +-- .../activation_m.f90 | 0 .../activation_s.f90 | 0 .../double_precision_file_m.f90 | 0 .../double_precision_file_s.f90 | 0 .../double_precision_string_m.f90 | 0 .../double_precision_string_s.f90 | 0 .../hyperparameters_m.f90 | 0 .../hyperparameters_s.f90 | 0 .../input_output_pair_m.f90 | 0 .../input_output_pair_s.f90 | 0 .../kind_parameters_m.f90 | 0 src/{inference_engine => fiats}/layer_m.f90 | 14 +-- src/{inference_engine => fiats}/layer_s.f90 | 16 +-- .../metadata_m.f90 | 0 .../metadata_s.f90 | 0 .../mini_batch_m.f90 | 0 .../mini_batch_s.f90 | 0 .../network_configuration_m.f90 | 0 .../network_configuration_s.F90 | 0 .../neural_network_m.f90} | 108 +++++++++--------- .../neural_network_s.F90} | 98 ++++++++-------- src/{inference_engine => fiats}/neuron_m.f90 | 0 src/{inference_engine => fiats}/neuron_s.f90 | 0 src/{inference_engine => fiats}/tensor_m.f90 | 0 .../tensor_map_m.f90 | 0 .../tensor_map_s.f90 | 0 src/{inference_engine => fiats}/tensor_s.f90 | 0 .../trainable_network_m.f90 | 8 +- .../trainable_network_s.f90 | 6 +- .../training_configuration_m.f90 | 0 .../training_configuration_s.F90 | 0 src/{inference_engine => fiats}/ubounds_m.f90 | 0 .../unmapped_network_s.f90 | 32 +++--- .../workspace_s.f90 | 58 +++++----- src/{inference_engine_m.f90 => fiats_m.f90} | 6 +- src/inference_engine/tmp | 0 test/asymmetric_engine_test_m.F90 | 12 +- test/hyperparameters_test_m.F90 | 2 +- test/main.F90 | 6 +- test/metadata_test_m.F90 | 2 +- test/network_configuration_test_m.F90 | 2 +- ...e_test_m.F90 => neural_network_test_m.F90} | 88 +++++++------- test/tensor_map_test_m.F90 | 2 +- test/tensor_test_m.f90 | 2 +- test/trainable_network_test_m.F90 | 8 +- test/training_configuration_test_m.F90 | 2 +- 65 files changed, 362 insertions(+), 367 deletions(-) create mode 120000 demo/include rename src/{inference_engine => fiats}/activation_m.f90 (100%) rename src/{inference_engine => fiats}/activation_s.f90 (100%) rename src/{inference_engine => fiats}/double_precision_file_m.f90 (100%) rename src/{inference_engine => fiats}/double_precision_file_s.f90 (100%) rename src/{inference_engine => fiats}/double_precision_string_m.f90 (100%) rename src/{inference_engine => fiats}/double_precision_string_s.f90 (100%) rename src/{inference_engine => fiats}/hyperparameters_m.f90 (100%) rename src/{inference_engine => fiats}/hyperparameters_s.f90 (100%) rename src/{inference_engine => fiats}/input_output_pair_m.f90 (100%) rename src/{inference_engine => fiats}/input_output_pair_s.f90 (100%) rename src/{inference_engine => fiats}/kind_parameters_m.f90 (100%) rename src/{inference_engine => fiats}/layer_m.f90 (89%) rename src/{inference_engine => fiats}/layer_s.f90 (93%) rename src/{inference_engine => fiats}/metadata_m.f90 (100%) rename src/{inference_engine => fiats}/metadata_s.f90 (100%) rename src/{inference_engine => fiats}/mini_batch_m.f90 (100%) rename src/{inference_engine => fiats}/mini_batch_s.f90 (100%) rename src/{inference_engine => fiats}/network_configuration_m.f90 (100%) rename src/{inference_engine => fiats}/network_configuration_s.F90 (100%) rename src/{inference_engine/inference_engine_m_.f90 => fiats/neural_network_m.f90} (81%) rename src/{inference_engine/inference_engine_s.F90 => fiats/neural_network_s.F90} (91%) rename src/{inference_engine => fiats}/neuron_m.f90 (100%) rename src/{inference_engine => fiats}/neuron_s.f90 (100%) rename src/{inference_engine => fiats}/tensor_m.f90 (100%) rename src/{inference_engine => fiats}/tensor_map_m.f90 (100%) rename src/{inference_engine => fiats}/tensor_map_s.f90 (100%) rename src/{inference_engine => fiats}/tensor_s.f90 (100%) rename src/{inference_engine => fiats}/trainable_network_m.f90 (88%) rename src/{inference_engine => fiats}/trainable_network_s.f90 (88%) rename src/{inference_engine => fiats}/training_configuration_m.f90 (100%) rename src/{inference_engine => fiats}/training_configuration_s.F90 (100%) rename src/{inference_engine => fiats}/ubounds_m.f90 (100%) rename src/{inference_engine => fiats}/unmapped_network_s.f90 (66%) rename src/{inference_engine => fiats}/workspace_s.f90 (52%) rename src/{inference_engine_m.f90 => fiats_m.f90} (88%) delete mode 100644 src/inference_engine/tmp rename test/{inference_engine_test_m.F90 => neural_network_test_m.F90} (82%) diff --git a/demo/app/infer-aerosol.f90 b/demo/app/infer-aerosol.f90 index d2211f2e9..d052f8481 100644 --- a/demo/app/infer-aerosol.f90 +++ b/demo/app/infer-aerosol.f90 @@ -7,7 +7,7 @@ program infer_aerosol use iso_fortran_env, only : int64, real64 ! External dependencies: - use inference_engine_m, only : unmapped_network_t, tensor_t, double_precision, double_precision_file_t + use fiats_m, only : unmapped_network_t, tensor_t, double_precision, double_precision_file_t use julienne_m, only : string_t, command_line_t use omp_lib @@ -49,7 +49,7 @@ subroutine read_stats_and_perform_inference(path) double precision cube_root double precision, allocatable, dimension(:,:) :: aerosol_data, input_components, output_components type(tensor_statistics_t) input_stats, output_stats - type(unmapped_network_t(double_precision)) inference_engine + type(unmapped_network_t(double_precision)) neural_network integer i, j input_stats = read_tensor_statistics(path // "meanxp.txt", path // "stdxp.txt", num_inputs) !for pre-processing normalization @@ -76,7 +76,7 @@ subroutine read_stats_and_perform_inference(path) !$omp end parallel do print *, "Reading the neural network from " // network_file_name - inference_engine = unmapped_network_t(double_precision_file_t(path // network_file_name)) + neural_network = unmapped_network_t(double_precision_file_t(path // network_file_name)) time_inference: & block @@ -103,7 +103,7 @@ subroutine read_stats_and_perform_inference(path) !$ start_time = omp_get_wtime() !$omp parallel do shared(inputs,outputs,icc) do i = 1,icc - outputs(i) = inference_engine%infer(inputs(i)) + outputs(i) = neural_network%infer(inputs(i)) end do !$omp end parallel do !$ end_time = omp_get_wtime() diff --git a/demo/app/tensor-statistics.f90 b/demo/app/tensor-statistics.f90 index 3a216205a..8c5cf2d06 100644 --- a/demo/app/tensor-statistics.f90 +++ b/demo/app/tensor-statistics.f90 @@ -9,7 +9,7 @@ program tensor_statistics ! External dependencies: use julienne_m, only : command_line_t, file_t, string_t use assert_m, only : assert, intrinsic_array_t - use inference_engine_m, only : ubounds_t + use fiats_m, only : ubounds_t use ieee_arithmetic, only : ieee_is_nan use iso_fortran_env, only : int64, real64 diff --git a/demo/app/train-cloud-microphysics.F90 b/demo/app/train-cloud-microphysics.F90 index 9a8c5efcc..1187b42e7 100644 --- a/demo/app/train-cloud-microphysics.F90 +++ b/demo/app/train-cloud-microphysics.F90 @@ -12,10 +12,10 @@ program train_on_flat_distribution !! External dependencies: use julienne_m, only : string_t, file_t, command_line_t, bin_t use assert_m, only : assert, intrinsic_array_t - use inference_engine_m, only : & - inference_engine_t, mini_batch_t, input_output_pair_t, tensor_t, trainable_network_t, tensor_map_t, & - training_configuration_t, shuffle - + use fiats_m, only : & + neural_network_t, mini_batch_t, input_output_pair_t, tensor_t, trainable_network_t, tensor_map_t, training_configuration_t, & + shuffle + !! Internal dependencies: use phase_space_bin_m, only : phase_space_bin_t use NetCDF_file_m, only: NetCDF_file_t @@ -319,7 +319,7 @@ subroutine read_train_write(training_configuration, args, plot_file) read_or_initialize_engine: & if (io_status==0) then print *,"Reading network from file " // network_file - trainable_network = trainable_network_t(inference_engine_t(file_t(string_t(network_file)))) + trainable_network = trainable_network_t(neural_network_t(file_t(string_t(network_file)))) close(network_unit) else close(network_unit) diff --git a/demo/fpm.toml b/demo/fpm.toml index eefd579dd..9bed6f611 100644 --- a/demo/fpm.toml +++ b/demo/fpm.toml @@ -1,10 +1,10 @@ -name = "Inference-Engine-Demonstration-Applications" -license = "(Please see inference-engine/LICENSE.txt.)" -author = "(Please see inference-engine/fpm.toml.)" -maintainer = "(Please see inference-engine/fpm.toml.)" +name = "Fiats-Demonstration-Applications" +license = "(Please see fiats/LICENSE.txt.)" +author = "(Please see fiats/fpm.toml.)" +maintainer = "(Please see fiats/fpm.toml.)" [dependencies] assert = {git = "https://github.com/sourceryinstitute/assert", tag = "1.7.0"} julienne = {git = "https://github.com/berkeleylab/julienne", tag = "1.2.2"} -inference-engine = {path = "../"} +fiats = {path = "../"} netcdf-interfaces = {git = "https://github.com/LKedward/netcdf-interfaces.git", rev = "d2bbb71ac52b4e346b62572b1ca1620134481096"} diff --git a/demo/include b/demo/include new file mode 120000 index 000000000..f5030fe88 --- /dev/null +++ b/demo/include @@ -0,0 +1 @@ +../include \ No newline at end of file diff --git a/demo/setup.sh b/demo/setup.sh index 5d848cfe8..46ce5b05d 100755 --- a/demo/setup.sh +++ b/demo/setup.sh @@ -4,7 +4,7 @@ set -e # exit on error usage() { - echo "Inference Engine Setup Script" + echo "Fiats Demonstration Applications Setup Script" echo "" echo "USAGE:" echo "./setup.sh [--help|-h] | [-p|--prefix=PREFIX]" @@ -112,25 +112,25 @@ else PKG_CONFIG_PATH=`realpath "$PKG_CONFIG_PATH"` fi -INFERENCE_ENGINE_VERSION=$(grep version ../fpm.toml | grep -o '".*"' - | sed 's/"//g') +FIATS_VERSION=$(grep version ../fpm.toml | grep -o '".*"' - | sed 's/"//g') -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 +FIATS_PC="$PKG_CONFIG_PATH/fiats.pc" +echo "FIATS_FPM_CC=\"$FPM_CC\"" > $FIATS_PC +echo "FIATS_FPM_FC=\"$FPM_FC\"" >> $FIATS_PC if [[ ! -z ${FPM_RUNNER:-} ]]; then - echo "INFERENCE_ENGINE_FPM_RUNNER=\"$FPM_RUNNER\"" >> $INFERENCE_ENGINE_PC + echo "FIATS_FPM_RUNNER=\"$FPM_RUNNER\"" >> $FIATS_PC fi -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: $INFERENCE_ENGINE_VERSION" >> $INFERENCE_ENGINE_PC +echo "FIATS_FPM_LD_FLAG=\"$FPM_LD_FLAG\"" >> $FIATS_PC +echo "FIATS_FPM_FLAG=\"$FPM_FLAG\"" >> $FIATS_PC +echo "Name: fiats" >> $FIATS_PC +echo "Description: Fiats" >> $FIATS_PC +echo "URL: https://github.com/berkeleylab/fiats " >> $FIATS_PC +echo "Version: $FIATS_VERSION" >> $FIATS_PC if [ $CI = true ]; then echo "---------------" - echo "cat \$INFERENCE_ENGINE_PC" - cat $INFERENCE_ENGINE_PC + echo "cat \$FIATS_PC" + cat $FIATS_PC echo "---------------" fi @@ -139,14 +139,14 @@ cp src/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 "--c-compiler \"`pkg-config fiats --variable=FIATS_FPM_CC`\" \\" >> $RUN_FPM_SH +echo "--compiler \"`pkg-config fiats --variable=FIATS_FPM_FC`\" \\" >> $RUN_FPM_SH if [[ ! -z ${FPM_RUNNER:-} ]]; then - echo "--runner \"`pkg-config inference-engine --variable=INFERENCE_ENGINE_FPM_RUNNER`\" \\" >> $RUN_FPM_SH + echo "--runner \"`pkg-config fiats --variable=FIATS_FPM_RUNNER`\" \\" >> $RUN_FPM_SH fi -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 +echo "--flag \"-cpp `pkg-config fiats --variable=FIATS_FPM_FLAG`\" \\" >> $RUN_FPM_SH +echo "--link-flag \"`pkg-config fiats --variable=FIATS_FPM_LD_FLAG`\" \\" >> $RUN_FPM_SH +echo "\$program_arguments" >> $RUN_FPM_SH chmod u+x $RUN_FPM_SH if [ $CI = true ]; then echo "---------------" @@ -158,10 +158,10 @@ fi $RUN_FPM_SH build echo "" -echo "____________________ The inference-engine demo apps build succeeded! _______________________" +echo "____________________ The fiats demo apps build succeeded! _______________________" echo "" echo "Run the following command to see a list of available apps:" echo "" echo "./build/run-fpm.sh run" echo "" -echo "Append a space followe by an app's name to see basic app usage information." +echo "Append a space followed by an app's name to see basic app usage information." diff --git a/example/concurrent-inferences.f90 b/example/concurrent-inferences.f90 index 5036e67b7..9a50ae249 100644 --- a/example/concurrent-inferences.f90 +++ b/example/concurrent-inferences.f90 @@ -3,7 +3,7 @@ program concurrent_inferences !! This program demonstrates how to read a neural network from a JSON file !! and use the network to perform concurrent inferences. - use inference_engine_m, only : inference_engine_t, tensor_t, double_precision, double_precision_string_t, double_precision_file_t + use fiats_m, only : neural_network_t, tensor_t, double_precision, double_precision_string_t, double_precision_file_t use julienne_m, only : string_t, command_line_t, file_t use assert_m, only : assert use iso_fortran_env, only : int64, real64 @@ -27,17 +27,17 @@ program concurrent_inferences block integer(int64) t_start, t_finish, clock_rate - type(inference_engine_t) inference_engine + type(neural_network_t) neural_network type(tensor_t), allocatable :: inputs(:,:,:), outputs(:,:,:) real, allocatable :: input_components(:,:,:,:) - print *, "Constructing a new inference_engine_t object from the file " // network_file_name%string() - inference_engine = inference_engine_t(file_t(network_file_name)) + print *, "Constructing a new neural_network_t object from the file " // network_file_name%string() + neural_network = neural_network_t(file_t(network_file_name)) print *,"Defining an array of tensor_t input objects with random normalized components" allocate(outputs(lat,lon,lev)) allocate( inputs(lat,lon,lev)) - allocate(input_components(lat,lon,lev,inference_engine%num_inputs())) + allocate(input_components(lat,lon,lev,neural_network%num_inputs())) call random_number(input_components) do concurrent(i=1:lat, j=1:lon, k=1:lev) @@ -47,7 +47,7 @@ program concurrent_inferences print *,"Performing concurrent inference" call system_clock(t_start, clock_rate) do concurrent(i=1:lat, j=1:lon, k=1:lev) - outputs(i,j,k) = inference_engine%infer(inputs(i,j,k)) + outputs(i,j,k) = neural_network%infer(inputs(i,j,k)) end do call system_clock(t_finish) print *,"Concurrent inference time: ", real(t_finish - t_start, real64)/real(clock_rate, real64) @@ -57,7 +57,7 @@ program concurrent_inferences do k=1,lev do j=1,lon do i=1,lat - outputs(i,j,k) = inference_engine%infer(inputs(i,j,k)) + outputs(i,j,k) = neural_network%infer(inputs(i,j,k)) end do end do end do @@ -66,7 +66,7 @@ program concurrent_inferences print *,"Performing elemental inferences" call system_clock(t_start, clock_rate) - outputs = inference_engine%infer(inputs) ! implicit (re)allocation of outputs array only if shape(inputs) /= shape(outputs) + outputs = neural_network%infer(inputs) ! implicit (re)allocation of outputs array only if shape(inputs) /= shape(outputs) call system_clock(t_finish) print *,"Elemental inference time: ", real(t_finish - t_start, real64)/real(clock_rate, real64) @@ -76,17 +76,17 @@ program concurrent_inferences block integer(int64) t_start, t_finish, clock_rate - type(inference_engine_t(double_precision)) inference_engine + type(neural_network_t(double_precision)) neural_network type(tensor_t(double_precision)), allocatable :: inputs(:,:,:), outputs(:,:,:) double precision, allocatable :: input_components(:,:,:,:) - print *, "Constructing a new inference_engine_t object from the file " // network_file_name%string() - inference_engine = inference_engine_t(double_precision_file_t(network_file_name)) + print *, "Constructing a new neural_network_t object from the file " // network_file_name%string() + neural_network = neural_network_t(double_precision_file_t(network_file_name)) print *,"Defining an array of tensor_t input objects with random normalized components" allocate(outputs(lat,lon,lev)) allocate( inputs(lat,lon,lev)) - allocate(input_components(lat,lon,lev,inference_engine%num_inputs())) + allocate(input_components(lat,lon,lev,neural_network%num_inputs())) call random_number(input_components) do concurrent(i=1:lat, j=1:lon, k=1:lev) @@ -96,7 +96,7 @@ program concurrent_inferences print *,"Performing double-precision concurrent inference" call system_clock(t_start, clock_rate) do concurrent(i=1:lat, j=1:lon, k=1:lev) - outputs(i,j,k) = inference_engine%infer(inputs(i,j,k)) + outputs(i,j,k) = neural_network%infer(inputs(i,j,k)) end do call system_clock(t_finish) print *,"Double-precision concurrent inference time: ", real(t_finish - t_start, real64)/real(clock_rate, real64) diff --git a/example/learn-addition.F90 b/example/learn-addition.F90 index b6174650f..a5ca64ffa 100644 --- a/example/learn-addition.F90 +++ b/example/learn-addition.F90 @@ -2,7 +2,7 @@ ! Terms of use are as specified in LICENSE.txt module addition_m !! Define a function that produces the desired network output for a given network input - use inference_engine_m, only : tensor_t + use fiats_m, only : tensor_t use assert_m, only : assert implicit none @@ -20,8 +20,8 @@ elemental function y(x_tensor) result(a_tensor) program learn_addition !! This trains a neural network to learn the following six polynomial functions of its eight inputs. - use inference_engine_m, only : & - inference_engine_t, trainable_network_t, mini_batch_t, tensor_t, input_output_pair_t, shuffle + use fiats_m, only : & + neural_network_t, trainable_network_t, mini_batch_t, tensor_t, input_output_pair_t, shuffle use julienne_m, only : string_t, file_t, command_line_t, bin_t use assert_m, only : assert, intrinsic_array_t use addition_m, only : y @@ -107,11 +107,11 @@ program learn_addition contains - subroutine output(inference_engine, file_name) - class(inference_engine_t), intent(in) :: inference_engine + subroutine output(neural_network, file_name) + class(neural_network_t), intent(in) :: neural_network type(string_t), intent(in) :: file_name type(file_t) json_file - json_file = inference_engine%to_json() + json_file = neural_network%to_json() call json_file%write_lines(file_name) end subroutine @@ -140,7 +140,7 @@ function perturbed_identity_network(perturbation_magnitude) result(trainable_net associate(w => identity + perturbation_magnitude*(w_harvest-0.5)/0.5, b => perturbation_magnitude*(b_harvest-0.5)/0.5) - trainable_network = trainable_network_t( inference_engine_t( & + trainable_network = trainable_network_t( neural_network_t( & nodes = n, weights = w, biases = b, metadata = & [string_t("Perturbed Identity"), string_t("Damian Rouson"), string_t("2023-09-23"), string_t("relu"), string_t("false")] & )) diff --git a/example/learn-exponentiation.F90 b/example/learn-exponentiation.F90 index a09b9359d..cfb549b85 100644 --- a/example/learn-exponentiation.F90 +++ b/example/learn-exponentiation.F90 @@ -2,7 +2,7 @@ ! Terms of use are as specified in LICENSE.txt module exponentiation_m !! Define a function that produces the desired network output for a given network input - use inference_engine_m, only : tensor_t + use fiats_m, only : tensor_t use assert_m, only : assert implicit none @@ -20,8 +20,7 @@ elemental function y(x_tensor) result(a_tensor) program learn_exponentiation !! This trains a neural network to learn the following six polynomial functions of its eight inputs. - use inference_engine_m, only : & - inference_engine_t, trainable_network_t, mini_batch_t, tensor_t, input_output_pair_t, shuffle + use fiats_m, only : neural_network_t, trainable_network_t, mini_batch_t, tensor_t, input_output_pair_t, shuffle use julienne_m, only : string_t, file_t, command_line_t, bin_t use assert_m, only : assert, intrinsic_array_t use exponentiation_m, only : y @@ -107,11 +106,11 @@ program learn_exponentiation contains - subroutine output(inference_engine, file_name) - class(inference_engine_t), intent(in) :: inference_engine + subroutine output(neural_network, file_name) + class(neural_network_t), intent(in) :: neural_network type(string_t), intent(in) :: file_name type(file_t) json_file - json_file = inference_engine%to_json() + json_file = neural_network%to_json() call json_file%write_lines(file_name) end subroutine @@ -140,7 +139,7 @@ function perturbed_identity_network(perturbation_magnitude) result(trainable_net associate(w => identity + perturbation_magnitude*(w_harvest-0.5)/0.5, b => perturbation_magnitude*(b_harvest-0.5)/0.5) - trainable_network = trainable_network_t( inference_engine_t( & + trainable_network = trainable_network_t( neural_network_t( & nodes = n, weights = w, biases = b, metadata = & [string_t("Perturbed Identity"), string_t("Damian Rouson"), string_t("2023-09-23"), string_t("relu"), string_t("false")] & )) diff --git a/example/learn-multiplication.F90 b/example/learn-multiplication.F90 index 81d72e88d..116c961c0 100644 --- a/example/learn-multiplication.F90 +++ b/example/learn-multiplication.F90 @@ -2,7 +2,7 @@ ! Terms of use are as specified in LICENSE.txt module multiply_inputs !! Define a function that produces the desired network output for a given network input - use inference_engine_m, only : tensor_t + use fiats_m, only : tensor_t use assert_m, only : assert implicit none @@ -20,8 +20,7 @@ elemental function y(x_tensor) result(a_tensor) program learn_multiplication !! This trains a neural network to learn the following six polynomial functions of its eight inputs. - use inference_engine_m, only : & - inference_engine_t, trainable_network_t, mini_batch_t, tensor_t, input_output_pair_t, shuffle + use fiats_m, only : neural_network_t, trainable_network_t, mini_batch_t, tensor_t, input_output_pair_t, shuffle use julienne_m, only : string_t, file_t, command_line_t, bin_t use assert_m, only : assert, intrinsic_array_t use multiply_inputs, only : y @@ -107,11 +106,11 @@ program learn_multiplication contains - subroutine output(inference_engine, file_name) - class(inference_engine_t), intent(in) :: inference_engine + subroutine output(neural_network, file_name) + class(neural_network_t), intent(in) :: neural_network type(string_t), intent(in) :: file_name type(file_t) json_file - json_file = inference_engine%to_json() + json_file = neural_network%to_json() call json_file%write_lines(file_name) end subroutine @@ -140,7 +139,7 @@ function perturbed_identity_network(perturbation_magnitude) result(trainable_net associate(w => identity + perturbation_magnitude*(w_harvest-0.5)/0.5, b => perturbation_magnitude*(b_harvest-0.5)/0.5) - trainable_network = trainable_network_t( inference_engine_t( & + trainable_network = trainable_network_t( neural_network_t( & nodes = n, weights = w, biases = b, metadata = & [string_t("Perturbed Identity"), string_t("Damian Rouson"), string_t("2023-09-23"), string_t("relu"), string_t("false")] & )) diff --git a/example/learn-power-series.F90 b/example/learn-power-series.F90 index e8a46e6dc..3cbcc2719 100644 --- a/example/learn-power-series.F90 +++ b/example/learn-power-series.F90 @@ -2,7 +2,7 @@ ! Terms of use are as specified in LICENSE.txt module power_series !! Define a function that produces the desired network output for a given network input - use inference_engine_m, only : tensor_t + use fiats_m, only : tensor_t use assert_m, only : assert implicit none @@ -20,8 +20,7 @@ elemental function y(x_in) result(a) program learn_power_series !! This trains a neural network to learn the following six polynomial functions of its eight inputs. - use inference_engine_m, only : & - inference_engine_t, trainable_network_t, mini_batch_t, tensor_t, input_output_pair_t, shuffle + use fiats_m, only : neural_network_t, trainable_network_t, mini_batch_t, tensor_t, input_output_pair_t, shuffle use julienne_m, only : string_t, file_t, command_line_t, bin_t use assert_m, only : assert, intrinsic_array_t use power_series, only : y @@ -109,11 +108,11 @@ program learn_power_series contains - subroutine output(inference_engine, file_name) - class(inference_engine_t), intent(in) :: inference_engine + subroutine output(neural_network, file_name) + class(neural_network_t), intent(in) :: neural_network type(string_t), intent(in) :: file_name type(file_t) json_file - json_file = inference_engine%to_json() + json_file = neural_network%to_json() call json_file%write_lines(file_name) end subroutine @@ -142,7 +141,7 @@ function perturbed_identity_network(perturbation_magnitude) result(trainable_net associate(w => identity + perturbation_magnitude*(w_harvest-0.5)/0.5, b => perturbation_magnitude*(b_harvest-0.5)/0.5) - trainable_network = trainable_network_t( inference_engine_t( & + trainable_network = trainable_network_t( neural_network_t( & nodes = n, weights = w, biases = b, metadata = & [string_t("Perturbed Identity"), string_t("Damian Rouson"), string_t("2023-09-23"), string_t("relu"), string_t("false")] & )) diff --git a/example/learn-saturated-mixing-ratio.F90 b/example/learn-saturated-mixing-ratio.F90 index bf49da58c..29313e2c7 100644 --- a/example/learn-saturated-mixing-ratio.F90 +++ b/example/learn-saturated-mixing-ratio.F90 @@ -2,8 +2,7 @@ ! Terms of use are as specified in LICENSE.txt program train_saturated_mixture_ratio !! This program trains a neural network to learn the saturated mixing ratio function of ICAR. - use inference_engine_m, only : & - inference_engine_t, trainable_network_t, mini_batch_t, tensor_t, input_output_pair_t, shuffle + use fiats_m, only : neural_network_t, trainable_network_t, mini_batch_t, tensor_t, input_output_pair_t, shuffle use julienne_m, only : string_t, file_t, command_line_t, bin_t, csv use assert_m, only : assert, intrinsic_array_t use saturated_mixing_ratio_m, only : y, T, p @@ -43,7 +42,7 @@ program train_saturated_mixture_ratio if (io_status == io_success) then print *,"Reading network from file " // network_file%string() - trainable_network = trainable_network_t(inference_engine_t(file_t(network_file))) + trainable_network = trainable_network_t(neural_network_t(file_t(network_file))) close(network_unit) else close(network_unit) @@ -155,11 +154,11 @@ subroutine print_diagnostics(plot_file_unit, epoch, cost, clock, nodes) write(unit=plot_file_unit, fmt=csv) nodes end subroutine - subroutine output(inference_engine, file_name) - class(inference_engine_t), intent(in) :: inference_engine + subroutine output(neural_network, file_name) + class(neural_network_t), intent(in) :: neural_network type(string_t), intent(in) :: file_name type(file_t) json_file - json_file = inference_engine%to_json() + json_file = neural_network%to_json() call json_file%write_lines(file_name) end subroutine @@ -187,7 +186,7 @@ function perturbed_identity_network(perturbation_magnitude, n) result(trainable_ call random_number(b_harvest) associate(w => identity + perturbation_magnitude*(w_harvest-0.5)/0.5, b => perturbation_magnitude*(b_harvest-0.5)/0.5) - trainable_network = trainable_network_t( inference_engine_t(nodes=n, weights=w, biases=b, & + trainable_network = trainable_network_t( neural_network_t(nodes=n, weights=w, biases=b, & metadata=[string_t("Saturated Mixing Ratio"),string_t("Rouson"),string_t("20241013"),string_t("relu"),string_t("false")] & )) end associate diff --git a/example/print-training-configuration.F90 b/example/print-training-configuration.F90 index cf6f4996b..36ed0983f 100644 --- a/example/print-training-configuration.F90 +++ b/example/print-training-configuration.F90 @@ -2,7 +2,7 @@ ! Terms of use are as specified in LICENSE.txt program print_training_configuration !! Demonstrate how to construct and print a training_configuration_t object - use inference_engine_m, only : training_configuration_t, hyperparameters_t, network_configuration_t + use fiats_m, only : training_configuration_t, hyperparameters_t, network_configuration_t use julienne_m, only : file_t implicit none #ifdef _CRAYFTN diff --git a/example/read-query-infer.f90 b/example/read-query-infer.f90 index 86889192b..66bb7c7b0 100644 --- a/example/read-query-infer.f90 +++ b/example/read-query-infer.f90 @@ -4,7 +4,7 @@ program read_query_infer !! This program demonstrates how to read a neural network from a JSON file, !! query the network object for some of its properties, print those properties, !! and use the network to perform inference. - use inference_engine_m, only : inference_engine_t, tensor_t + use fiats_m, only : neural_network_t, tensor_t use julienne_m, only : string_t, command_line_t, file_t implicit none @@ -17,16 +17,16 @@ program read_query_infer 'Usage: fpm run --example read-query -- --input-file ""' end if - print *, "Reading an inference_engine_t object from the same JSON file '"//file_name%string()//"'." - associate(inference_engine => inference_engine_t(file_t(file_name))) + print *, "Reading an neural_network_t object from the same JSON file '"//file_name%string()//"'." + associate(neural_network => neural_network_t(file_t(file_name))) - print *, "Querying the new inference_engine_t object for several properties:" - associate(activation_name => inference_engine%activation_function_name()) + print *, "Querying the new neural_network_t object for several properties:" + associate(activation_name => neural_network%activation_function_name()) print *, "Activation function: ", activation_name%string() end associate - print *, "Number of outputs:", inference_engine%num_outputs() - print *, "Number of inputs:", inference_engine%num_inputs() - print *, "Nodes per layer:", inference_engine%nodes_per_layer() + print *, "Number of outputs:", neural_network%num_outputs() + print *, "Number of inputs:", neural_network%num_inputs() + print *, "Nodes per layer:", neural_network%nodes_per_layer() print *, "Performing inference:" block @@ -41,13 +41,13 @@ program read_query_infer do i = 1, num_tests call random_number(harvest) associate(inputs => tensor_t(tensor_min + (tensor_max-tensor_min)*harvest)) - associate(outputs => inference_engine%infer(inputs)) + associate(outputs => neural_network%infer(inputs)) print '(2(2g12.5,a,2x))', inputs%values(), "|", outputs%values() end associate end associate end do end block - end associate ! associate(inference_engine => ...) + end associate ! associate(neural_network => ...) end associate ! associate(file_name => ...) end program diff --git a/example/supporting-modules/saturated_mixing_ratio_m.f90 b/example/supporting-modules/saturated_mixing_ratio_m.f90 index e888f652d..88067358d 100644 --- a/example/supporting-modules/saturated_mixing_ratio_m.f90 +++ b/example/supporting-modules/saturated_mixing_ratio_m.f90 @@ -27,7 +27,7 @@ module saturated_mixing_ratio_m !! The saturated_mixing_ratio function in this module resulted from refactoring the sat_mr function !! in the Intermediate Complexity Atmospheric Research (ICAR) model file src/physics/mp_simple.f90. !! ICAR is distributed under the above MIT license. See https://github.com/ncar/icar. - use inference_engine_m, only : tensor_t + use fiats_m, only : tensor_t use assert_m, only : assert implicit none diff --git a/example/train-and-write.F90 b/example/train-and-write.F90 index 7105afde0..3594b1007 100644 --- a/example/train-and-write.F90 +++ b/example/train-and-write.F90 @@ -8,8 +8,8 @@ program train_and_write !! the desired network therefore contains weights corresponding to identity matrices and biases that vanish everywhere. !! The initial condition corresponds to the desired network with all weights and biases perturbed by a random variable !! that is uniformly distributed on the range [0,0.1]. - use inference_engine_m, only : & - inference_engine_t, trainable_network_t, mini_batch_t, tensor_t, input_output_pair_t, shuffle + use fiats_m, only : & + neural_network_t, trainable_network_t, mini_batch_t, tensor_t, input_output_pair_t, shuffle use julienne_m, only : string_t, file_t, command_line_t, bin_t use assert_m, only : assert, intrinsic_array_t implicit none @@ -98,11 +98,11 @@ program train_and_write contains - subroutine output(inference_engine, file_name) - class(inference_engine_t), intent(in) :: inference_engine + subroutine output(neural_network, file_name) + class(neural_network_t), intent(in) :: neural_network type(string_t), intent(in) :: file_name type(file_t) json_file - json_file = inference_engine%to_json() + json_file = neural_network%to_json() call json_file%write_lines(file_name) end subroutine @@ -130,7 +130,7 @@ function perturbed_identity_network(perturbation_magnitude) result(trainable_net associate(w => identity + perturbation_magnitude*(w_harvest-0.5)/0.5, b => perturbation_magnitude*(b_harvest-0.5)/0.5) - trainable_network = trainable_network_t( inference_engine_t( & + trainable_network = trainable_network_t( neural_network_t( & nodes = nodes_per_layer, weights = w, biases = b, metadata = & [string_t("Perturbed Identity"), string_t("Damian Rouson"), string_t("2024-10-13"), string_t("relu"), string_t("false")] & )) diff --git a/example/write-read-infer.F90 b/example/write-read-infer.F90 index 8786d4b24..1f0cbed31 100644 --- a/example/write-read-infer.F90 +++ b/example/write-read-infer.F90 @@ -7,7 +7,7 @@ program write_read_infer !! perform inference. The network performs an identity mapping from any !! non-negative inputs to the corresponding outputs using a RELU activation !! function. - use inference_engine_m, only : inference_engine_t, tensor_t + use fiats_m, only : neural_network_t, tensor_t use julienne_m, only : string_t, command_line_t, file_t implicit none @@ -25,21 +25,21 @@ program write_read_infer contains - function identity_network() result(inference_engine) - type(inference_engine_t) inference_engine + function identity_network() result(neural_network) + type(neural_network_t) neural_network integer, parameter :: nodes_per_layer(*) = [2, 2, 2] integer, parameter :: max_n = maxval(nodes_per_layer), layers = size(nodes_per_layer) #ifdef _CRAYFTN real, allocatable :: weights(:,:,:) weights = reshape([real :: [1,0, 0,1], [1,0, 0,1]], [max_n, max_n, layers-1]) - inference_engine = inference_engine_t( & + neural_network = neural_network_t( & metadata = [string_t("Identity"), string_t("Damian Rouson"), string_t("2023-09-18"), string_t("relu"), string_t("false")], & weights = weights, & biases = reshape([real:: [0,0], [0,0]], [max_n, layers-1]), & nodes = nodes_per_layer & ) #else - inference_engine = inference_engine_t( & + neural_network = neural_network_t( & metadata = [string_t("Identity"), string_t("Damian Rouson"), string_t("2023-09-18"), string_t("relu"), string_t("false")], & weights = reshape([real :: [1,0, 0,1], [1,0, 0,1]], [max_n, max_n, layers-1]), & biases = reshape([real :: [0,0], [0,0]], [max_n, layers-1]), & @@ -53,35 +53,35 @@ subroutine write_read_query_infer(output_file_name) type(string_t) activation_name integer i, j integer, parameter :: num_neurons = 3, num_hidden_layers = 2 - type(inference_engine_t) network, inference_engine + type(neural_network_t) network, neural_network type(file_t) json_output_file, json_input_file type(tensor_t) inputs, outputs - print *, "Constructing an inference_engine_t neural-network object from scratch." + print *, "Constructing an neural_network_t neural-network object from scratch." network = identity_network() - print *, "Converting an inference_engine_t object to a file_t object." + print *, "Converting an neural_network_t object to a file_t object." json_output_file = network%to_json() - print *, "Writing an inference_engine_t object to the file '"//output_file_name%string()//"' in JSON format." + print *, "Writing an neural_network_t object to the file '"//output_file_name%string()//"' in JSON format." call json_output_file%write_lines(output_file_name) - print *, "Reading an inference_engine_t object from the same JSON file '"//output_file_name%string()//"'." + print *, "Reading an neural_network_t object from the same JSON file '"//output_file_name%string()//"'." json_input_file = file_t(output_file_name) - print *, "Constructing a new inference_engine_t object from the parameters read." - inference_engine = inference_engine_t(json_input_file) + print *, "Constructing a new neural_network_t object from the parameters read." + neural_network = neural_network_t(json_input_file) - print *, "Querying the new inference_engine_t object for several properties:" - print *, "Number of outputs:", inference_engine%num_outputs() - print *, "Number of inputs:", inference_engine%num_inputs() - print *, "Nodes per layer:", inference_engine%nodes_per_layer() - activation_name = inference_engine%activation_function_name() + print *, "Querying the new neural_network_t object for several properties:" + print *, "Number of outputs:", neural_network%num_outputs() + print *, "Number of inputs:", neural_network%num_inputs() + print *, "Nodes per layer:", neural_network%nodes_per_layer() + activation_name = neural_network%activation_function_name() print *, "Activation function: ", activation_name%string() print *, "Performing inference:" inputs = tensor_t([2.,3.]) print *, "Inputs: ", inputs%values() - outputs = inference_engine%infer(inputs) + outputs = neural_network%infer(inputs) print *, "Actual outputs: ", outputs%values() print *, "Correct outputs: ", inputs%values() end subroutine write_read_query_infer diff --git a/fpm.toml b/fpm.toml index 92ba67a53..fbf885f9c 100644 --- a/fpm.toml +++ b/fpm.toml @@ -1,4 +1,4 @@ -name = "inference-engine" +name = "fiats" version = "0.13.1" license = "see LICENSE.txt" author = "Damian Rouson, Tan Nguyen, Jordan Welsman, David Torres, Brad Richardson, Katherine Rasmussen, Federica Villani, Dan Bonachea" @@ -6,6 +6,4 @@ maintainer = "rouson@lbl.gov" [dependencies] assert = {git = "https://github.com/sourceryinstitute/assert", tag = "1.7.0"} - -[dev-dependencies] julienne = {git = "https://github.com/berkeleylab/julienne", tag = "1.3.1"} diff --git a/setup.sh b/setup.sh index 5908ce704..455007afa 100755 --- a/setup.sh +++ b/setup.sh @@ -4,13 +4,13 @@ set -e # exit on error usage() { - echo "Inference Engine Setup Script" + echo "Fiats Setup Script" echo "" echo "USAGE:" echo "./setup.sh [--help|-h]" echo "" echo " --help Display this help text" - echo " --prefix=PREFIX Install any binaries needed to build inference-engine in 'PREFIX/bin'" + echo " --prefix=PREFIX Install any binaries needed to build fiats in 'PREFIX/bin'" echo " Default prefix='\$HOME/.local/bin'" echo "" } @@ -48,10 +48,10 @@ install_fpm_from_source() echo "Please install curl and then rerun ./setup.sh" exit 1 fi - 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 + mkdir temp-dir-to-build-fpm-for-fiats-installation + curl -L -o temp-dir-to-build-fpm-for-fiats-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-fiats-installation temp-dir-to-build-fpm-for-fiats-installation/fpm.F90 + rm -rf temp-dir-to-build-fpm-for-fiats-installation if command -v fpm > /dev/null ; then echo "fpm installed" else @@ -80,7 +80,7 @@ FPM_CC=${CC:-"gcc-14"} fpm test --profile release --flag "-fopenmp" echo "" -echo "____________________ Inference-Engine has been set up! _______________________" +echo "____________________ Fiats has been set up! _______________________" echo "" echo "Enter the command below to the see names of example use cases that you can run:" echo "" diff --git a/src/inference_engine/activation_m.f90 b/src/fiats/activation_m.f90 similarity index 100% rename from src/inference_engine/activation_m.f90 rename to src/fiats/activation_m.f90 diff --git a/src/inference_engine/activation_s.f90 b/src/fiats/activation_s.f90 similarity index 100% rename from src/inference_engine/activation_s.f90 rename to src/fiats/activation_s.f90 diff --git a/src/inference_engine/double_precision_file_m.f90 b/src/fiats/double_precision_file_m.f90 similarity index 100% rename from src/inference_engine/double_precision_file_m.f90 rename to src/fiats/double_precision_file_m.f90 diff --git a/src/inference_engine/double_precision_file_s.f90 b/src/fiats/double_precision_file_s.f90 similarity index 100% rename from src/inference_engine/double_precision_file_s.f90 rename to src/fiats/double_precision_file_s.f90 diff --git a/src/inference_engine/double_precision_string_m.f90 b/src/fiats/double_precision_string_m.f90 similarity index 100% rename from src/inference_engine/double_precision_string_m.f90 rename to src/fiats/double_precision_string_m.f90 diff --git a/src/inference_engine/double_precision_string_s.f90 b/src/fiats/double_precision_string_s.f90 similarity index 100% rename from src/inference_engine/double_precision_string_s.f90 rename to src/fiats/double_precision_string_s.f90 diff --git a/src/inference_engine/hyperparameters_m.f90 b/src/fiats/hyperparameters_m.f90 similarity index 100% rename from src/inference_engine/hyperparameters_m.f90 rename to src/fiats/hyperparameters_m.f90 diff --git a/src/inference_engine/hyperparameters_s.f90 b/src/fiats/hyperparameters_s.f90 similarity index 100% rename from src/inference_engine/hyperparameters_s.f90 rename to src/fiats/hyperparameters_s.f90 diff --git a/src/inference_engine/input_output_pair_m.f90 b/src/fiats/input_output_pair_m.f90 similarity index 100% rename from src/inference_engine/input_output_pair_m.f90 rename to src/fiats/input_output_pair_m.f90 diff --git a/src/inference_engine/input_output_pair_s.f90 b/src/fiats/input_output_pair_s.f90 similarity index 100% rename from src/inference_engine/input_output_pair_s.f90 rename to src/fiats/input_output_pair_s.f90 diff --git a/src/inference_engine/kind_parameters_m.f90 b/src/fiats/kind_parameters_m.f90 similarity index 100% rename from src/inference_engine/kind_parameters_m.f90 rename to src/fiats/kind_parameters_m.f90 diff --git a/src/inference_engine/layer_m.f90 b/src/fiats/layer_m.f90 similarity index 89% rename from src/inference_engine/layer_m.f90 rename to src/fiats/layer_m.f90 index cc418e8d6..390f3bfa6 100644 --- a/src/inference_engine/layer_m.f90 +++ b/src/fiats/layer_m.f90 @@ -4,7 +4,7 @@ module layer_m use double_precision_string_m, only : double_precision_string_t use kind_parameters_m, only : default_real, double_precision use julienne_string_m, only : string_t - use inference_engine_m_, only : inference_engine_t + use neural_network_m, only : neural_network_t use metadata_m, only : metadata_t use neuron_m, only : neuron_t use tensor_map_m, only : tensor_map_t @@ -19,8 +19,8 @@ module layer_m type(neuron_t(k)), private :: neuron !! linked list of this layer's neurons type(layer_t(k)), allocatable, private :: next !! next layer contains - generic :: inference_engine => default_real_inference_engine, double_precision_inference_engine - procedure, private :: default_real_inference_engine, double_precision_inference_engine + generic :: neural_network => default_real_neural_network, double_precision_neural_network + procedure, private :: default_real_neural_network, double_precision_neural_network generic :: count_layers => default_real_count_layers, double_precision_count_layers procedure, private :: default_real_count_layers, double_precision_count_layers generic :: count_neurons => default_real_count_neurons, double_precision_count_neurons @@ -57,22 +57,22 @@ recursive module function double_precision_construct_layer(layer_lines, start) r interface - module function default_real_inference_engine(hidden_layers, metadata, output_layer, input_map, output_map) result(inference_engine_) + module function default_real_neural_network(hidden_layers, metadata, output_layer, input_map, output_map) result(neural_network_) implicit none class(layer_t), intent(in), target :: hidden_layers type(layer_t), intent(in), target :: output_layer type(string_t), intent(in) :: metadata(:) type(tensor_map_t), intent(in) :: input_map, output_map - type(inference_engine_t) inference_engine_ + type(neural_network_t) neural_network_ end function - module function double_precision_inference_engine(hidden_layers, metadata, output_layer, input_map, output_map) result(inference_engine_) + module function double_precision_neural_network(hidden_layers, metadata, output_layer, input_map, output_map) result(neural_network_) implicit none class(layer_t(double_precision)), intent(in), target :: hidden_layers type(layer_t(double_precision)), intent(in), target :: output_layer type(metadata_t), intent(in) :: metadata type(tensor_map_t(double_precision)), intent(in) :: input_map, output_map - type(inference_engine_t(double_precision)) inference_engine_ + type(neural_network_t(double_precision)) neural_network_ end function module function default_real_count_layers(layer) result(num_layers) diff --git a/src/inference_engine/layer_s.f90 b/src/fiats/layer_s.f90 similarity index 93% rename from src/inference_engine/layer_s.f90 rename to src/fiats/layer_s.f90 index 231514e2f..589740b36 100644 --- a/src/inference_engine/layer_s.f90 +++ b/src/fiats/layer_s.f90 @@ -68,7 +68,7 @@ end procedure - module procedure default_real_inference_engine + module procedure default_real_neural_network associate( & num_inputs => hidden_layers%count_inputs(), & @@ -77,7 +77,7 @@ num_hidden_layers => hidden_layers%count_layers(), & num_output_layers => output_layer%count_layers() & ) - call assert(num_output_layers==1, "inference_engine_s(default_real_inference_engine): 1 output layer", num_output_layers) + call assert(num_output_layers==1, "neural_network_s(default_real_neural_network): 1 output layer", num_output_layers) associate(nodes => [num_inputs, neurons_per_hidden_layer, num_outputs]) associate(n_max => maxval(nodes)) @@ -132,15 +132,15 @@ end do loop_over_output_neurons - inference_engine_ = inference_engine_t(metadata, weights, biases, nodes, input_map, output_map) + neural_network_ = neural_network_t(metadata, weights, biases, nodes, input_map, output_map) end block end associate end associate end associate - end procedure default_real_inference_engine + end procedure default_real_neural_network - module procedure double_precision_inference_engine + module procedure double_precision_neural_network associate( & num_inputs => hidden_layers%count_inputs(), & @@ -149,7 +149,7 @@ num_hidden_layers => hidden_layers%count_layers(), & num_output_layers => output_layer%count_layers() & ) - call assert(num_output_layers==1, "inference_engine_s(double_precision_inference_engine): 1 output layer", num_output_layers) + call assert(num_output_layers==1, "neural_network_s(double_precision_neural_network): 1 output layer", num_output_layers) associate(nodes => [num_inputs, neurons_per_hidden_layer, num_outputs]) associate(n_max => maxval(nodes)) @@ -204,13 +204,13 @@ end do loop_over_output_neurons - inference_engine_ = inference_engine_t(metadata, weights, biases, nodes, input_map, output_map) + neural_network_ = neural_network_t(metadata, weights, biases, nodes, input_map, output_map) end block end associate end associate end associate - end procedure double_precision_inference_engine + end procedure double_precision_neural_network module procedure default_real_count_layers diff --git a/src/inference_engine/metadata_m.f90 b/src/fiats/metadata_m.f90 similarity index 100% rename from src/inference_engine/metadata_m.f90 rename to src/fiats/metadata_m.f90 diff --git a/src/inference_engine/metadata_s.f90 b/src/fiats/metadata_s.f90 similarity index 100% rename from src/inference_engine/metadata_s.f90 rename to src/fiats/metadata_s.f90 diff --git a/src/inference_engine/mini_batch_m.f90 b/src/fiats/mini_batch_m.f90 similarity index 100% rename from src/inference_engine/mini_batch_m.f90 rename to src/fiats/mini_batch_m.f90 diff --git a/src/inference_engine/mini_batch_s.f90 b/src/fiats/mini_batch_s.f90 similarity index 100% rename from src/inference_engine/mini_batch_s.f90 rename to src/fiats/mini_batch_s.f90 diff --git a/src/inference_engine/network_configuration_m.f90 b/src/fiats/network_configuration_m.f90 similarity index 100% rename from src/inference_engine/network_configuration_m.f90 rename to src/fiats/network_configuration_m.f90 diff --git a/src/inference_engine/network_configuration_s.F90 b/src/fiats/network_configuration_s.F90 similarity index 100% rename from src/inference_engine/network_configuration_s.F90 rename to src/fiats/network_configuration_s.F90 diff --git a/src/inference_engine/inference_engine_m_.f90 b/src/fiats/neural_network_m.f90 similarity index 81% rename from src/inference_engine/inference_engine_m_.f90 rename to src/fiats/neural_network_m.f90 index 40121b4cf..eb72c1524 100644 --- a/src/inference_engine/inference_engine_m_.f90 +++ b/src/fiats/neural_network_m.f90 @@ -1,7 +1,7 @@ ! Copyright (c), The Regents of the University of California ! Terms of use are as specified in LICENSE.txt -module inference_engine_m_ - !! Define an abstraction that supports inference operationsn on a neural network +module neural_network_m + !! Define an abstraction that supports inference operations on a neural network use activation_m, only : activation_t use double_precision_file_m, only : double_precision_file_t use kind_parameters_m, only : default_real, double_precision @@ -13,13 +13,13 @@ module inference_engine_m_ implicit none private - public :: inference_engine_t + public :: neural_network_t public :: unmapped_network_t public :: exchange_t public :: workspace_t - type inference_engine_t(k) - !! Encapsulate the minimal information needed to perform inference + type neural_network_t(k) + !! Encapsulate the information needed to perform inference integer, kind :: k = default_real type(tensor_map_t(k)), private :: input_map_, output_map_ type(metadata_t), private :: metadata_ @@ -73,9 +73,9 @@ module inference_engine_m_ interface workspace_t - pure module function default_real_workspace(inference_engine) result(workspace) + pure module function default_real_workspace(neural_network) result(workspace) implicit none - type(inference_engine_t), intent(in) :: inference_engine + type(neural_network_t), intent(in) :: neural_network type(workspace_t) workspace end function @@ -83,10 +83,10 @@ pure module function default_real_workspace(inference_engine) result(workspace) interface - module subroutine default_real_allocate(self, inference_engine) + module subroutine default_real_allocate(self, neural_network) implicit none class(workspace_t), intent(inout) :: self - type(inference_engine_t), intent(in) :: inference_engine + type(neural_network_t), intent(in) :: neural_network end subroutine pure module function default_real_allocated(self) result(all_allocated) @@ -110,50 +110,50 @@ pure module function default_real_allocated(self) result(all_allocated) module function default_real_to_exchange(self) result(exchange) implicit none - class(inference_engine_t), intent(in) :: self + class(neural_network_t), intent(in) :: self type(exchange_t) exchange end function module function double_precision_to_exchange(self) result(exchange) implicit none - class(inference_engine_t(double_precision)), intent(in) :: self + class(neural_network_t(double_precision)), intent(in) :: self type(exchange_t(double_precision)) exchange end function end interface - interface inference_engine_t + interface neural_network_t module function default_real_construct_from_components(metadata, weights, biases, nodes, input_map, output_map) & - result(inference_engine) + result(neural_network) implicit none type(string_t), intent(in) :: metadata(:) real, intent(in) :: weights(:,:,:), biases(:,:) integer, intent(in) :: nodes(0:) type(tensor_map_t), intent(in), optional :: input_map, output_map - type(inference_engine_t) inference_engine + type(neural_network_t) neural_network end function module function double_precision_construct_from_components(metadata, weights, biases, nodes, input_map, output_map) & - result(inference_engine) + result(neural_network) implicit none type(metadata_t), intent(in) :: metadata double precision, intent(in) :: weights(:,:,:), biases(:,:) integer, intent(in) :: nodes(0:) type(tensor_map_t(double_precision)), intent(in), optional :: input_map, output_map - type(inference_engine_t(double_precision)) inference_engine + type(neural_network_t(double_precision)) neural_network end function - impure elemental module function default_real_from_json(file_) result(inference_engine) + impure elemental module function default_real_from_json(file_) result(neural_network) implicit none type(file_t), intent(in) :: file_ - type(inference_engine_t) inference_engine + type(neural_network_t) neural_network end function - impure elemental module function double_precision_from_json(file) result(inference_engine) + impure elemental module function double_precision_from_json(file) result(neural_network) implicit none type(double_precision_file_t), intent(in) :: file - type(inference_engine_t(double_precision)) inference_engine + type(neural_network_t(double_precision)) neural_network end function end interface @@ -161,26 +161,26 @@ impure elemental module function double_precision_from_json(file) result(inferen - interface ! inference_engine_t type-bound procedures + interface ! neural_network_t type-bound procedures elemental module function default_real_approximately_equal(lhs, rhs) result(lhs_eq_rhs) !! The result is true if lhs and rhs are the same to within a tolerance implicit none - class(inference_engine_t), intent(in) :: lhs, rhs + class(neural_network_t), intent(in) :: lhs, rhs logical lhs_eq_rhs end function elemental module function double_precision_approximately_equal(lhs, rhs) result(lhs_eq_rhs) !! The result is true if lhs and rhs are the same to within a tolerance implicit none - class(inference_engine_t(double_precision)), intent(in) :: lhs, rhs + class(neural_network_t(double_precision)), intent(in) :: lhs, rhs logical lhs_eq_rhs end function elemental module function default_real_map_to_input_range(self, tensor) result(normalized_tensor) !! The result contains the input tensor values normalized to fall on the range used during training implicit none - class(inference_engine_t), intent(in) :: self + class(neural_network_t), intent(in) :: self type(tensor_t), intent(in) :: tensor type(tensor_t) normalized_tensor end function @@ -188,7 +188,7 @@ elemental module function default_real_map_to_input_range(self, tensor) result(n elemental module function double_precision_map_to_input_range(self, tensor) result(normalized_tensor) !! The result contains the input tensor values normalized to fall on the range used during training implicit none - class(inference_engine_t(double_precision)), intent(in) :: self + class(neural_network_t(double_precision)), intent(in) :: self type(tensor_t(double_precision)), intent(in) :: tensor type(tensor_t(double_precision)) normalized_tensor end function @@ -196,7 +196,7 @@ elemental module function double_precision_map_to_input_range(self, tensor) resu elemental module function default_real_map_from_output_range(self, normalized_tensor) result(tensor) !! The result contains the output tensor values unmapped via the inverse of the mapping used in training implicit none - class(inference_engine_t), intent(in) :: self + class(neural_network_t), intent(in) :: self type(tensor_t), intent(in) :: normalized_tensor type(tensor_t) tensor end function @@ -204,124 +204,124 @@ elemental module function default_real_map_from_output_range(self, normalized_te elemental module function double_precision_map_from_output_range(self, normalized_tensor) result(tensor) !! The result contains the output tensor values unmapped via the inverse of the mapping used in training implicit none - class(inference_engine_t(double_precision)), intent(in) :: self + class(neural_network_t(double_precision)), intent(in) :: self type(tensor_t(double_precision)), intent(in) :: normalized_tensor type(tensor_t(double_precision)) tensor end function impure elemental module function default_real_to_json(self) result(json_file) implicit none - class(inference_engine_t), intent(in) :: self + class(neural_network_t), intent(in) :: self type(file_t) json_file end function impure elemental module function double_precision_to_json(self) result(json_file) implicit none - class(inference_engine_t(double_precision)), intent(in) :: self + class(neural_network_t(double_precision)), intent(in) :: self type(file_t) json_file end function - elemental module subroutine default_real_assert_conformable_with(self, inference_engine) + elemental module subroutine default_real_assert_conformable_with(self, neural_network) implicit none - class(inference_engine_t), intent(in) :: self - type(inference_engine_t), intent(in) :: inference_engine + class(neural_network_t), intent(in) :: self + type(neural_network_t), intent(in) :: neural_network end subroutine - elemental module subroutine double_precision_assert_conformable_with(self, inference_engine) + elemental module subroutine double_precision_assert_conformable_with(self, neural_network) implicit none - class(inference_engine_t(double_precision)), intent(in) :: self - type(inference_engine_t(double_precision)), intent(in) :: inference_engine + class(neural_network_t(double_precision)), intent(in) :: self + type(neural_network_t(double_precision)), intent(in) :: neural_network end subroutine elemental module function default_real_infer(self, inputs) result(outputs) implicit none - class(inference_engine_t), intent(in) :: self + class(neural_network_t), intent(in) :: self type(tensor_t), intent(in) :: inputs type(tensor_t) outputs end function elemental module function double_precision_infer(self, inputs) result(outputs) implicit none - class(inference_engine_t(double_precision)), intent(in) :: self + class(neural_network_t(double_precision)), intent(in) :: self type(tensor_t(double_precision)), intent(in) :: inputs type(tensor_t(double_precision)) outputs end function elemental module function default_real_num_outputs(self) result(output_count) implicit none - class(inference_engine_t), intent(in) :: self + class(neural_network_t), intent(in) :: self integer output_count end function elemental module function double_precision_num_outputs(self) result(output_count) implicit none - class(inference_engine_t(double_precision)), intent(in) :: self + class(neural_network_t(double_precision)), intent(in) :: self integer output_count end function elemental module function default_real_num_hidden_layers(self) result(hidden_layer_count) implicit none - class(inference_engine_t), intent(in) :: self + class(neural_network_t), intent(in) :: self integer hidden_layer_count end function elemental module function double_precision_num_hidden_layers(self) result(hidden_layer_count) implicit none - class(inference_engine_t(double_precision)), intent(in) :: self + class(neural_network_t(double_precision)), intent(in) :: self integer hidden_layer_count end function elemental module function default_real_num_inputs(self) result(input_count) implicit none - class(inference_engine_t), intent(in) :: self + class(neural_network_t), intent(in) :: self integer input_count end function elemental module function double_precision_num_inputs(self) result(input_count) implicit none - class(inference_engine_t(double_precision)), intent(in) :: self + class(neural_network_t(double_precision)), intent(in) :: self integer input_count end function pure module function default_real_nodes_per_layer(self) result(node_count) implicit none - class(inference_engine_t), intent(in) :: self + class(neural_network_t), intent(in) :: self integer, allocatable :: node_count(:) end function pure module function double_precision_nodes_per_layer(self) result(node_count) implicit none - class(inference_engine_t(double_precision)), intent(in) :: self + class(neural_network_t(double_precision)), intent(in) :: self integer, allocatable :: node_count(:) end function elemental module function default_real_activation_name(self) result(activation_name) implicit none - class(inference_engine_t), intent(in) :: self + class(neural_network_t), intent(in) :: self type(string_t) activation_name end function elemental module function double_precision_activation_name(self) result(activation_name) implicit none - class(inference_engine_t(double_precision)), intent(in) :: self + class(neural_network_t(double_precision)), intent(in) :: self type(string_t) activation_name end function pure module function default_real_skip(self) result(use_skip_connections) implicit none - class(inference_engine_t), intent(in) :: self + class(neural_network_t), intent(in) :: self logical use_skip_connections end function pure module function double_precision_skip(self) result(use_skip_connections) implicit none - class(inference_engine_t(double_precision)), intent(in) :: self + class(neural_network_t(double_precision)), intent(in) :: self logical use_skip_connections end function pure module subroutine default_real_learn(self, mini_batches_arr, cost, adam, learning_rate, workspace) implicit none - class(inference_engine_t), intent(inout) :: self + class(neural_network_t), intent(inout) :: self type(mini_batch_t), intent(in) :: mini_batches_arr(:) real, intent(out), allocatable, optional :: cost(:) logical, intent(in) :: adam @@ -331,12 +331,12 @@ pure module subroutine default_real_learn(self, mini_batches_arr, cost, adam, le pure module subroutine default_real_consistency(self) implicit none - class(inference_engine_t), intent(in) :: self + class(neural_network_t), intent(in) :: self end subroutine pure module subroutine double_precision_consistency(self) implicit none - class(inference_engine_t(double_precision)), intent(in) :: self + class(neural_network_t(double_precision)), intent(in) :: self end subroutine end interface @@ -344,7 +344,7 @@ pure module subroutine double_precision_consistency(self) type unmapped_network_t(k) integer, kind :: k = default_real private - type(inference_engine_t(k)) inference_engine_ + type(neural_network_t(k)) neural_network_ contains generic :: infer => default_real_infer_unmapped, double_precision_infer_unmapped procedure, private, non_overridable :: default_real_infer_unmapped, double_precision_infer_unmapped @@ -378,4 +378,4 @@ elemental module function double_precision_infer_unmapped(self, inputs) result(o end interface -end module inference_engine_m_ +end module neural_network_m diff --git a/src/inference_engine/inference_engine_s.F90 b/src/fiats/neural_network_s.F90 similarity index 91% rename from src/inference_engine/inference_engine_s.F90 rename to src/fiats/neural_network_s.F90 index ac31642f6..c652f6f2c 100644 --- a/src/inference_engine/inference_engine_s.F90 +++ b/src/fiats/neural_network_s.F90 @@ -1,6 +1,6 @@ ! Copyright (c), The Regents of the University of California ! Terms of use are as specified in LICENSE.txt -submodule(inference_engine_m_) inference_engine_s +submodule(neural_network_m) neural_network_s use assert_m, only : assert, intrinsic_array_t use double_precision_string_m, only : double_precision_string_t use kind_parameters_m, only : double_precision @@ -152,17 +152,17 @@ associate( & all_allocated=>[allocated(self%weights_),allocated(self%biases_),allocated(self%nodes_)]& ) - call assert(all(all_allocated),"inference_engine_s(default_real_consistency): fully_allocated", & + call assert(all(all_allocated),"neural_network_s(default_real_consistency): fully_allocated", & intrinsic_array_t(all_allocated)) end associate associate(max_width=>maxval(self%nodes_), component_dims=>[size(self%biases_,1), size(self%weights_,1), size(self%weights_,2)]) - call assert(all(component_dims == max_width), "inference_engine_s(default_real_consistency): conformable arrays", & + call assert(all(component_dims == max_width), "neural_network_s(default_real_consistency): conformable arrays", & intrinsic_array_t([max_width,component_dims])) end associate associate(input_subscript => lbound(self%nodes_,1)) - call assert(input_subscript == input_layer, "inference_engine_s(default_real_consistency): n base subsscript", & + call assert(input_subscript == input_layer, "neural_network_s(default_real_consistency): n base subsscript", & input_subscript) end associate @@ -173,17 +173,17 @@ associate( & all_allocated=>[allocated(self%weights_),allocated(self%biases_),allocated(self%nodes_)]& ) - call assert(all(all_allocated),"inference_engine_s(default_real_consistency): fully_allocated", & + call assert(all(all_allocated),"neural_network_s(default_real_consistency): fully_allocated", & intrinsic_array_t(all_allocated)) end associate associate(max_width=>maxval(self%nodes_), component_dims=>[size(self%biases_,1), size(self%weights_,1), size(self%weights_,2)]) - call assert(all(component_dims == max_width), "inference_engine_s(default_real_consistency): conformable arrays", & + call assert(all(component_dims == max_width), "neural_network_s(default_real_consistency): conformable arrays", & intrinsic_array_t([max_width,component_dims])) end associate associate(input_subscript => lbound(self%nodes_,1)) - call assert(input_subscript == input_layer, "inference_engine_s(default_real_consistency): n base subsscript", & + call assert(input_subscript == input_layer, "neural_network_s(default_real_consistency): n base subsscript", & input_subscript) end associate @@ -191,77 +191,77 @@ module procedure default_real_construct_from_components - inference_engine%metadata_ = metadata_t(metadata(1),metadata(2),metadata(3),metadata(4),metadata(5)) - inference_engine%weights_ = weights - inference_engine%biases_ = biases - inference_engine%nodes_ = nodes + neural_network%metadata_ = metadata_t(metadata(1),metadata(2),metadata(3),metadata(4),metadata(5)) + neural_network%weights_ = weights + neural_network%biases_ = biases + neural_network%nodes_ = nodes block integer i if (present(input_map)) then - inference_engine%input_map_ = input_map + neural_network%input_map_ = input_map else associate(num_inputs => nodes(lbound(nodes,1))) associate(default_minima => [(0., i=1,num_inputs)], default_maxima => [(1., i=1,num_inputs)]) - inference_engine%input_map_ = tensor_map_t("inputs", default_minima, default_maxima) + neural_network%input_map_ = tensor_map_t("inputs", default_minima, default_maxima) end associate end associate end if if (present(output_map)) then - inference_engine%output_map_ = output_map + neural_network%output_map_ = output_map else associate(num_outputs => nodes(ubound(nodes,1))) associate(default_minima => [(0., i=1,num_outputs)], default_maxima => [(1., i=1,num_outputs)]) - inference_engine%output_map_ = tensor_map_t("outputs", default_minima, default_maxima) + neural_network%output_map_ = tensor_map_t("outputs", default_minima, default_maxima) end associate end associate end if end block - inference_engine%activation_ = activation_t(metadata(4)%string()) + neural_network%activation_ = activation_t(metadata(4)%string()) - call inference_engine%assert_consistency() + call neural_network%assert_consistency() end procedure default_real_construct_from_components module procedure double_precision_construct_from_components - inference_engine%metadata_ = metadata - inference_engine%weights_ = weights - inference_engine%biases_ = biases - inference_engine%nodes_ = nodes + neural_network%metadata_ = metadata + neural_network%weights_ = weights + neural_network%biases_ = biases + neural_network%nodes_ = nodes block integer i if (present(input_map)) then - inference_engine%input_map_ = input_map + neural_network%input_map_ = input_map else associate(num_inputs => nodes(lbound(nodes,1))) associate(default_intercept => [(0D0, i=1,num_inputs)], default_slope => [(1D0, i=1,num_inputs)]) - inference_engine%input_map_ = tensor_map_t("inputs", default_intercept, default_slope) + neural_network%input_map_ = tensor_map_t("inputs", default_intercept, default_slope) end associate end associate end if if (present(output_map)) then - inference_engine%output_map_ = output_map + neural_network%output_map_ = output_map else associate(num_outputs => nodes(ubound(nodes,1))) associate(default_intercept => [(0D0, i=1,num_outputs)], default_slope => [(1D0, i=1,num_outputs)]) - inference_engine%output_map_ = tensor_map_t("outputs", default_intercept, default_slope) + neural_network%output_map_ = tensor_map_t("outputs", default_intercept, default_slope) end associate end associate end if end block associate(function_name => metadata%activation_name()) - inference_engine%activation_ = activation_t(function_name%string()) + neural_network%activation_ = activation_t(function_name%string()) end associate - call inference_engine%assert_consistency() + call neural_network%assert_consistency() end procedure double_precision_construct_from_components @@ -373,7 +373,7 @@ lines(line) = string_t(' ]') line = line + 1 lines(line) = string_t('}') - call assert(line == json_lines, "inference_engine_t%to_json: all lines defined", intrinsic_array_t([json_lines, line])) + call assert(line == json_lines, "neural_network_t%to_json: all lines defined", intrinsic_array_t([json_lines, line])) end associate json_file = file_t(lines) end block @@ -489,7 +489,7 @@ lines(line) = string_t(' ]') line = line + 1 lines(line) = string_t('}') - call assert(line == json_lines, "inference_engine_t%to_json: all lines defined", intrinsic_array_t([json_lines, line])) + call assert(line == json_lines, "neural_network_t%to_json: all lines defined", intrinsic_array_t([json_lines, line])) end associate json_file = file_t(lines) end block @@ -506,7 +506,7 @@ type(layer_t) hidden_layers, output_layer lines = file_%lines() - call assert(adjustl(lines(1)%string())=="{", "inference_engine_s(default_real_from_json): expected outermost object '{'") + call assert(adjustl(lines(1)%string())=="{", "neural_network_s(default_real_from_json): expected outermost object '{'") check_git_tag: & block @@ -515,7 +515,7 @@ tag = lines(2)%get_json_value("acceptable_engine_tag", mold="") call assert( & tag == acceptable_engine_tag & - ,"inference_engine_s(default_real_from_json): acceptable_engine_tag" & + ,"neural_network_s(default_real_from_json): acceptable_engine_tag" & ,tag //"(expected " //acceptable_engine_tag // ")" & ) end block check_git_tag @@ -587,15 +587,15 @@ associate(proto_meta => metadata_t(string_t(""),string_t(""),string_t(""),string_t(""),string_t(""))) associate(metadata => metadata_t(lines(l : l + size(proto_meta%to_json()) - 1))) associate(metadata_strings => metadata%strings()) - inference_engine = hidden_layers%inference_engine(metadata_strings, output_layer, input_map, output_map) + neural_network = hidden_layers%neural_network(metadata_strings, output_layer, input_map, output_map) associate(function_name => metadata%activation_name()) - inference_engine%activation_ = activation_t(function_name%string()) + neural_network%activation_ = activation_t(function_name%string()) end associate end associate end associate end associate read_metadata - call inference_engine%assert_consistency() + call neural_network%assert_consistency() end procedure default_real_from_json @@ -608,7 +608,7 @@ type(layer_t(double_precision)) hidden_layers, output_layer lines = file%double_precision_lines() - call assert(adjustl(lines(1)%string())=="{", "inference_engine_s(double_precision_from_json): expected outermost object '{'") + call assert(adjustl(lines(1)%string())=="{", "neural_network_s(double_precision_from_json): expected outermost object '{'") check_git_tag: & block @@ -617,7 +617,7 @@ tag = lines(2)%get_json_value("acceptable_engine_tag", mold="") call assert( & tag == acceptable_engine_tag & - ,"inference_engine_s(double_precision_from_json): acceptable_engine_tag" & + ,"neural_network_s(double_precision_from_json): acceptable_engine_tag" & ,tag //"(expected " //acceptable_engine_tag // ")" & ) end block check_git_tag @@ -688,14 +688,14 @@ read_metadata: & associate(proto_meta => metadata_t(string_t(""),string_t(""),string_t(""),string_t(""),string_t(""))) associate(metadata => metadata_t(lines(l : l + size(proto_meta%to_json()) - 1))) - inference_engine = hidden_layers%inference_engine(metadata, output_layer, input_map, output_map) + neural_network = hidden_layers%neural_network(metadata, output_layer, input_map, output_map) associate(function_name => metadata%activation_name()) - inference_engine%activation_ = activation_t(function_name%string()) + neural_network%activation_ = activation_t(function_name%string()) end associate end associate end associate read_metadata - call inference_engine%assert_consistency() + call neural_network%assert_consistency() end procedure double_precision_from_json @@ -704,14 +704,14 @@ call self%assert_consistency() associate(equal_shapes => [ & - shape(self%weights_) == shape(inference_engine%weights_), & - shape(self%biases_) == shape(inference_engine%biases_), & - shape(self%nodes_) == shape(inference_engine%nodes_) & + shape(self%weights_) == shape(neural_network%weights_), & + shape(self%biases_) == shape(neural_network%biases_), & + shape(self%nodes_) == shape(neural_network%nodes_) & ]) call assert(all(equal_shapes), "assert_conformable_with: all(equal_shapes)", intrinsic_array_t(equal_shapes)) end associate - call assert(self%activation_ == inference_engine%activation_, "assert_conformable_with: activation_") + call assert(self%activation_ == neural_network%activation_, "assert_conformable_with: activation_") end procedure @@ -720,14 +720,14 @@ call self%assert_consistency() associate(equal_shapes => [ & - shape(self%weights_) == shape(inference_engine%weights_), & - shape(self%biases_) == shape(inference_engine%biases_), & - shape(self%nodes_) == shape(inference_engine%nodes_) & + shape(self%weights_) == shape(neural_network%weights_), & + shape(self%biases_) == shape(neural_network%biases_), & + shape(self%nodes_) == shape(neural_network%nodes_) & ]) call assert(all(equal_shapes), "assert_conformable_with: all(equal_shapes)", intrinsic_array_t(equal_shapes)) end associate - call assert(self%activation_ == inference_engine%activation_, "assert_conformable_with: activation_") + call assert(self%activation_ == neural_network%activation_, "assert_conformable_with: activation_") end procedure @@ -888,7 +888,7 @@ type(tensor_t), allocatable :: inputs(:), expected_outputs(:) call self%assert_consistency() - call assert(workspace%fully_allocated(), "inference_engine_s(default_real_learn): workspace%fully_allocated()") + call assert(workspace%fully_allocated(), "neural_network_s(default_real_learn): workspace%fully_allocated()") associate(output_layer => ubound(self%nodes_,1)) @@ -1072,4 +1072,4 @@ end associate end procedure default_real_learn -end submodule inference_engine_s +end submodule neural_network_s diff --git a/src/inference_engine/neuron_m.f90 b/src/fiats/neuron_m.f90 similarity index 100% rename from src/inference_engine/neuron_m.f90 rename to src/fiats/neuron_m.f90 diff --git a/src/inference_engine/neuron_s.f90 b/src/fiats/neuron_s.f90 similarity index 100% rename from src/inference_engine/neuron_s.f90 rename to src/fiats/neuron_s.f90 diff --git a/src/inference_engine/tensor_m.f90 b/src/fiats/tensor_m.f90 similarity index 100% rename from src/inference_engine/tensor_m.f90 rename to src/fiats/tensor_m.f90 diff --git a/src/inference_engine/tensor_map_m.f90 b/src/fiats/tensor_map_m.f90 similarity index 100% rename from src/inference_engine/tensor_map_m.f90 rename to src/fiats/tensor_map_m.f90 diff --git a/src/inference_engine/tensor_map_s.f90 b/src/fiats/tensor_map_s.f90 similarity index 100% rename from src/inference_engine/tensor_map_s.f90 rename to src/fiats/tensor_map_s.f90 diff --git a/src/inference_engine/tensor_s.f90 b/src/fiats/tensor_s.f90 similarity index 100% rename from src/inference_engine/tensor_s.f90 rename to src/fiats/tensor_s.f90 diff --git a/src/inference_engine/trainable_network_m.f90 b/src/fiats/trainable_network_m.f90 similarity index 88% rename from src/inference_engine/trainable_network_m.f90 rename to src/fiats/trainable_network_m.f90 index 86380eb0f..0cb2cfc50 100644 --- a/src/inference_engine/trainable_network_m.f90 +++ b/src/fiats/trainable_network_m.f90 @@ -1,5 +1,5 @@ module trainable_network_m - use inference_engine_m_, only : inference_engine_t, workspace_t + use neural_network_m, only : neural_network_t, workspace_t use input_output_pair_m, only : input_output_pair_t use julienne_m, only : string_t use kind_parameters_m, only : default_real @@ -11,7 +11,7 @@ module trainable_network_m private public :: trainable_network_t - type, extends(inference_engine_t) :: trainable_network_t(k) + type, extends(neural_network_t) :: trainable_network_t(k) integer, kind :: k = default_real private type(workspace_t), private :: workspace_ @@ -24,9 +24,9 @@ module trainable_network_m interface trainable_network_t - pure module function default_real_network(inference_engine) result(trainable_network) + pure module function default_real_network(neural_network) result(trainable_network) implicit none - type(inference_engine_t), intent(in) :: inference_engine + type(neural_network_t), intent(in) :: neural_network type(trainable_network_t) trainable_network end function diff --git a/src/inference_engine/trainable_network_s.f90 b/src/fiats/trainable_network_s.f90 similarity index 88% rename from src/inference_engine/trainable_network_s.f90 rename to src/fiats/trainable_network_s.f90 index a4e3caa6c..12c025661 100644 --- a/src/inference_engine/trainable_network_s.f90 +++ b/src/fiats/trainable_network_s.f90 @@ -4,8 +4,8 @@ contains module procedure default_real_network - trainable_network%inference_engine_t = inference_engine - trainable_network%workspace_ = workspace_t(inference_engine) + trainable_network%neural_network_t = neural_network + trainable_network%workspace_ = workspace_t(neural_network) end procedure module procedure default_real_train @@ -42,7 +42,7 @@ b => perturbation_magnitude*(b_harvest-0.5)/0.5 & ) trainable_network = trainable_network_t( & - inference_engine_t(nodes=n, weights=w, biases=b, metadata=metadata, input_map=input_map, output_map=output_map) & + neural_network_t(nodes=n, weights=w, biases=b, metadata=metadata, input_map=input_map, output_map=output_map) & ) end associate end associate diff --git a/src/inference_engine/training_configuration_m.f90 b/src/fiats/training_configuration_m.f90 similarity index 100% rename from src/inference_engine/training_configuration_m.f90 rename to src/fiats/training_configuration_m.f90 diff --git a/src/inference_engine/training_configuration_s.F90 b/src/fiats/training_configuration_s.F90 similarity index 100% rename from src/inference_engine/training_configuration_s.F90 rename to src/fiats/training_configuration_s.F90 diff --git a/src/inference_engine/ubounds_m.f90 b/src/fiats/ubounds_m.f90 similarity index 100% rename from src/inference_engine/ubounds_m.f90 rename to src/fiats/ubounds_m.f90 diff --git a/src/inference_engine/unmapped_network_s.f90 b/src/fiats/unmapped_network_s.f90 similarity index 66% rename from src/inference_engine/unmapped_network_s.f90 rename to src/fiats/unmapped_network_s.f90 index 9ced289e5..4a2cf6e43 100644 --- a/src/inference_engine/unmapped_network_s.f90 +++ b/src/fiats/unmapped_network_s.f90 @@ -1,6 +1,6 @@ ! Copyright (c), The Regents of the University of California ! Terms of use are as specified in LICENSE.txt -submodule(inference_engine_m_) unmapped_network_s +submodule(neural_network_m) unmapped_network_s implicit none integer, parameter :: input_layer = 0 @@ -8,7 +8,7 @@ contains module procedure double_precision_unmapped_from_json - unmapped_network%inference_engine_ = double_precision_from_json(file) + unmapped_network%neural_network_ = double_precision_from_json(file) end procedure module procedure default_real_infer_unmapped @@ -16,15 +16,15 @@ real, allocatable :: a(:,:) integer l - associate(inference_engine => self%inference_engine_) + associate(neural_network => self%neural_network_) - call inference_engine%assert_consistency() + call neural_network%assert_consistency() associate( & - w => inference_engine%weights_ & - ,b => inference_engine%biases_ & - ,n => inference_engine%nodes_ & - ,output_layer => ubound(inference_engine%nodes_,1) & + w => neural_network%weights_ & + ,b => neural_network%biases_ & + ,n => neural_network%nodes_ & + ,output_layer => ubound(neural_network%nodes_,1) & ) allocate(a(maxval(n), input_layer:output_layer)) @@ -34,7 +34,7 @@ do l = input_layer+1, output_layer associate(z => matmul(w(1:n(l),1:n(l-1),l), a(1:n(l-1),l-1)) + b(1:n(l),l)) if (l .lt. output_layer) then - a(1:n(l),l) = inference_engine%activation_%evaluate(z) + a(1:n(l),l) = neural_network%activation_%evaluate(z) else a(1:n(l),l) = z(1:n(l)) end if @@ -53,15 +53,15 @@ double precision, allocatable :: a(:,:) integer l - associate(inference_engine => self%inference_engine_) + associate(neural_network => self%neural_network_) - call inference_engine%assert_consistency() + call neural_network%assert_consistency() associate( & - w => inference_engine%weights_ & - ,b => inference_engine%biases_ & - ,n => inference_engine%nodes_ & - ,output_layer => ubound(inference_engine%nodes_,1) & + w => neural_network%weights_ & + ,b => neural_network%biases_ & + ,n => neural_network%nodes_ & + ,output_layer => ubound(neural_network%nodes_,1) & ) allocate(a(maxval(n), input_layer:output_layer)) @@ -72,7 +72,7 @@ do l = input_layer+1, output_layer associate(z => matmul(w(1:n(l),1:n(l-1),l), a(1:n(l-1),l-1)) + b(1:n(l),l)) if (l .lt. output_layer) then - a(1:n(l),l) = inference_engine%activation_%evaluate(z) + a(1:n(l),l) = neural_network%activation_%evaluate(z) else a(1:n(l),l) = z(1:n(l)) end if diff --git a/src/inference_engine/workspace_s.f90 b/src/fiats/workspace_s.f90 similarity index 52% rename from src/inference_engine/workspace_s.f90 rename to src/fiats/workspace_s.f90 index f3d80139c..f3e1209d6 100644 --- a/src/inference_engine/workspace_s.f90 +++ b/src/fiats/workspace_s.f90 @@ -1,4 +1,4 @@ -submodule(inference_engine_m_) workspace_s +submodule(neural_network_m) workspace_s use assert_m, only : assert implicit none @@ -8,26 +8,26 @@ module procedure default_real_workspace - allocate(workspace%dcdw, mold=inference_engine%weights_) ! Gradient of cost function with respect to weights - allocate(workspace%vdw , mold=inference_engine%weights_) - allocate(workspace%sdw , mold=inference_engine%weights_) - allocate(workspace%vdwc, mold=inference_engine%weights_) - allocate(workspace%sdwc, mold=inference_engine%weights_) + allocate(workspace%dcdw, mold=neural_network%weights_) ! Gradient of cost function with respect to weights + allocate(workspace%vdw , mold=neural_network%weights_) + allocate(workspace%sdw , mold=neural_network%weights_) + allocate(workspace%vdwc, mold=neural_network%weights_) + allocate(workspace%sdwc, mold=neural_network%weights_) - allocate(workspace%dcdb, mold=inference_engine%biases_ ) ! Gradient of cost function with respect with biases - allocate(workspace%vdb , mold=inference_engine%biases_ ) - allocate(workspace%sdb , mold=inference_engine%biases_ ) - allocate(workspace%vdbc, mold=inference_engine%biases_ ) - allocate(workspace%sdbc, mold=inference_engine%biases_ ) + allocate(workspace%dcdb, mold=neural_network%biases_ ) ! Gradient of cost function with respect with biases + allocate(workspace%vdb , mold=neural_network%biases_ ) + allocate(workspace%sdb , mold=neural_network%biases_ ) + allocate(workspace%vdbc, mold=neural_network%biases_ ) + allocate(workspace%sdbc, mold=neural_network%biases_ ) ! TODO: #if ! (F2023_LOCALITY || F2018_LOCALITY) ! then don't allocate a, z, and delta - allocate(workspace%z , mold=inference_engine%biases_) - allocate(workspace%delta, mold=inference_engine%biases_) + allocate(workspace%z , mold=neural_network%biases_) + allocate(workspace%delta, mold=neural_network%biases_) - associate(output_layer => ubound(inference_engine%nodes_,1)) - allocate(workspace%a(maxval(inference_engine%nodes_), input_layer:output_layer)) ! Activations + associate(output_layer => ubound(neural_network%nodes_,1)) + allocate(workspace%a(maxval(neural_network%nodes_), input_layer:output_layer)) ! Activations end associate call assert(workspace%fully_allocated(), "workspace_s(defalt_real_workspace): workspace allocated") @@ -49,26 +49,26 @@ module procedure default_real_allocate - call allocate_if_necessary(self%dcdw, mold=inference_engine%weights_) ! Gradient of cost function with respect to weights - call allocate_if_necessary(self%vdw , mold=inference_engine%weights_) - call allocate_if_necessary(self%sdw , mold=inference_engine%weights_) - call allocate_if_necessary(self%vdwc, mold=inference_engine%weights_) - call allocate_if_necessary(self%sdwc, mold=inference_engine%weights_) + call allocate_if_necessary(self%dcdw, mold=neural_network%weights_) ! Gradient of cost function with respect to weights + call allocate_if_necessary(self%vdw , mold=neural_network%weights_) + call allocate_if_necessary(self%sdw , mold=neural_network%weights_) + call allocate_if_necessary(self%vdwc, mold=neural_network%weights_) + call allocate_if_necessary(self%sdwc, mold=neural_network%weights_) - call allocate_if_necessary(self%dcdb , mold=inference_engine%biases_) ! Gradient of cost function with respect with biases - call allocate_if_necessary(self%vdb , mold=inference_engine%biases_) - call allocate_if_necessary(self%sdb , mold=inference_engine%biases_) - call allocate_if_necessary(self%vdbc , mold=inference_engine%biases_) - call allocate_if_necessary(self%sdbc , mold=inference_engine%biases_) + call allocate_if_necessary(self%dcdb , mold=neural_network%biases_) ! Gradient of cost function with respect with biases + call allocate_if_necessary(self%vdb , mold=neural_network%biases_) + call allocate_if_necessary(self%sdb , mold=neural_network%biases_) + call allocate_if_necessary(self%vdbc , mold=neural_network%biases_) + call allocate_if_necessary(self%sdbc , mold=neural_network%biases_) ! TODO: #if ! (F2023_LOCALITY || F2018_LOCALITY) ! then don't allocate a, z, and delta - call allocate_if_necessary(self%z , mold=inference_engine%biases_) - call allocate_if_necessary(self%delta, mold=inference_engine%biases_) + call allocate_if_necessary(self%z , mold=neural_network%biases_) + call allocate_if_necessary(self%delta, mold=neural_network%biases_) - associate(output_layer => ubound(inference_engine%nodes_,1)) - allocate(self%a(maxval(inference_engine%nodes_), input_layer:output_layer)) ! Activations + associate(output_layer => ubound(neural_network%nodes_,1)) + allocate(self%a(maxval(neural_network%nodes_), input_layer:output_layer)) ! Activations end associate contains diff --git a/src/inference_engine_m.f90 b/src/fiats_m.f90 similarity index 88% rename from src/inference_engine_m.f90 rename to src/fiats_m.f90 index 45a803ab7..4d09e40c7 100644 --- a/src/inference_engine_m.f90 +++ b/src/fiats_m.f90 @@ -1,15 +1,15 @@ ! Copyright (c), The Regents of the University of California ! Terms of use are as specified in LICENSE.txt -module inference_engine_m +module fiats_m !! Specify the user-facing modules, derived types, and type parameters use double_precision_file_m, only : double_precision_file_t use double_precision_string_m, only : double_precision_string_t use hyperparameters_m, only : hyperparameters_t use input_output_pair_m, only : input_output_pair_t, shuffle, write_to_stdout - use inference_engine_m_, only : inference_engine_t, unmapped_network_t use kind_parameters_m, only : default_real, double_precision use metadata_m, only : metadata_t use mini_batch_m, only : mini_batch_t + use neural_network_m, only : neural_network_t, unmapped_network_t use network_configuration_m, only : network_configuration_t use tensor_m, only : tensor_t use tensor_map_m, only : tensor_map_t @@ -17,4 +17,4 @@ module inference_engine_m use training_configuration_m, only : training_configuration_t use ubounds_m, only : ubounds_t implicit none -end module +end module fiats_m diff --git a/src/inference_engine/tmp b/src/inference_engine/tmp deleted file mode 100644 index e69de29bb..000000000 diff --git a/test/asymmetric_engine_test_m.F90 b/test/asymmetric_engine_test_m.F90 index 522bff898..fe69fa759 100644 --- a/test/asymmetric_engine_test_m.F90 +++ b/test/asymmetric_engine_test_m.F90 @@ -9,7 +9,7 @@ module asymmetric_engine_test_m test_t, test_result_t, vector_test_description_t, test_description_substring, string_t, vector_function_strategy_t ! Internal dependencies - use inference_engine_m, only : inference_engine_t, tensor_t + use fiats_m, only : neural_network_t, tensor_t implicit none @@ -31,7 +31,7 @@ module asymmetric_engine_test_m pure function subject() result(specimen) character(len=:), allocatable :: specimen - specimen = "An inference_engine_t object encoding an asymmetric XOR-AND-the-2nd-input network" + specimen = "An neural_network_t object encoding an asymmetric XOR-AND-the-2nd-input network" end function function results() result(test_results) @@ -60,8 +60,8 @@ function results() result(test_results) end associate end function - function xor_and_2nd_input_network() result(inference_engine) - type(inference_engine_t) inference_engine + function xor_and_2nd_input_network() result(neural_network) + type(neural_network_t) neural_network real, allocatable :: biases(:,:), weights(:,:,:) type(string_t), allocatable :: metadata(:) integer, parameter :: n(0:*) = [2,4,4,1] @@ -107,14 +107,14 @@ function xor_and_2nd_input_network() result(inference_engine) biases(1:n(l),l) = [-1] call assert(j == n(l), "l=3; j=1: j == n(l)") - inference_engine = inference_engine_t(metadata, weights, biases, nodes = n) + neural_network = neural_network_t(metadata, weights, biases, nodes = n) end function function xor_and_2nd_input_truth_table() result(test_passes) logical, allocatable :: test_passes(:) - type(inference_engine_t) asymmetric + type(neural_network_t) asymmetric asymmetric = xor_and_2nd_input_network() diff --git a/test/hyperparameters_test_m.F90 b/test/hyperparameters_test_m.F90 index 2313c1c91..07d1140ab 100644 --- a/test/hyperparameters_test_m.F90 +++ b/test/hyperparameters_test_m.F90 @@ -4,7 +4,7 @@ module hyperparameters_test_m !! Test hyperparameters_t object I/O and construction ! External dependencies - use inference_engine_m, only : hyperparameters_t + use fiats_m, only : hyperparameters_t use julienne_m, only : test_t, test_result_t, test_description_t, test_description_substring, string_t #ifdef __GFORTRAN__ use julienne_m, only : test_function_i diff --git a/test/main.F90 b/test/main.F90 index a21444d24..a280ae011 100644 --- a/test/main.F90 +++ b/test/main.F90 @@ -1,7 +1,7 @@ ! Copyright (c), The Regents of the University of California ! Terms of use are as specified in LICENSE.txt program main - use inference_engine_test_m, only : inference_engine_test_t + use neural_network_test_m, only : neural_network_test_t use asymmetric_engine_test_m, only : asymmetric_engine_test_t use trainable_network_test_m, only : trainable_network_test_t use metadata_test_m, only : metadata_test_t @@ -13,7 +13,7 @@ program main use julienne_m, only : command_line_t implicit none - type(inference_engine_test_t) inference_engine_test + type(neural_network_test_t) neural_network_test type(asymmetric_engine_test_t) asymmetric_engine_test type(trainable_network_test_t) trainable_network_test type(hyperparameters_test_t) hyperparameters_test @@ -48,7 +48,7 @@ program main call tensor_map_test%report(passes, tests) call tensor_test%report(passes, tests) call asymmetric_engine_test%report(passes, tests) - call inference_engine_test%report(passes, tests) + call neural_network_test%report(passes, tests) call trainable_network_test%report(passes, tests) call cpu_time(t_finish) diff --git a/test/metadata_test_m.F90 b/test/metadata_test_m.F90 index b87b499b3..5c3448a70 100644 --- a/test/metadata_test_m.F90 +++ b/test/metadata_test_m.F90 @@ -4,7 +4,7 @@ module metadata_test_m !! Test metadata_t object I/O and construction ! External dependencies - use inference_engine_m, only : metadata_t + use fiats_m, only : metadata_t use julienne_m, only : test_t, test_result_t, test_description_t, test_description_substring, string_t #ifdef __GFORTRAN__ use julienne_m, only : test_function_i diff --git a/test/network_configuration_test_m.F90 b/test/network_configuration_test_m.F90 index 97e4e8a64..5ba09fc97 100644 --- a/test/network_configuration_test_m.F90 +++ b/test/network_configuration_test_m.F90 @@ -4,7 +4,7 @@ module network_configuration_test_m !! Test network_configuration_t object I/O and construction ! External dependencies - use inference_engine_m, only : network_configuration_t + use fiats_m, only : network_configuration_t use julienne_m, only : test_t, test_result_t, test_description_t, test_description_substring, string_t, file_t #ifdef __GFORTRAN__ use julienne_m, only : test_function_i diff --git a/test/inference_engine_test_m.F90 b/test/neural_network_test_m.F90 similarity index 82% rename from test/inference_engine_test_m.F90 rename to test/neural_network_test_m.F90 index 2d7d3071c..0207ca7d1 100644 --- a/test/inference_engine_test_m.F90 +++ b/test/neural_network_test_m.F90 @@ -1,6 +1,6 @@ ! Copyright (c), The Regents of the University of California ! Terms of use are as specified in LICENSE.txt -module inference_engine_test_m +module neural_network_test_m !! Define inference tests and procedures required for reporting results ! External dependencies @@ -12,14 +12,14 @@ module inference_engine_test_m #endif ! Internal dependencies - use inference_engine_m, only : inference_engine_t, tensor_t, metadata_t + use fiats_m, only : neural_network_t, tensor_t, metadata_t implicit none private - public :: inference_engine_test_t + public :: neural_network_test_t - type, extends(test_t) :: inference_engine_test_t + type, extends(test_t) :: neural_network_test_t contains procedure, nopass :: subject procedure, nopass :: results @@ -29,7 +29,7 @@ module inference_engine_test_m pure function subject() result(specimen) character(len=:), allocatable :: specimen - specimen = "An inference_engine_t that encodes an XOR gate" + specimen = "An neural_network_t that encodes an XOR gate" end function function results() result(test_results) @@ -74,12 +74,12 @@ function results() result(test_results) test_results = test_descriptions%run() end function - function single_hidden_layer_xor_network() result(inference_engine) - type(inference_engine_t) inference_engine + function single_hidden_layer_xor_network() result(neural_network) + type(neural_network_t) neural_network integer, parameter :: nodes_per_layer(*) = [2, 3, 1] integer, parameter :: max_n = maxval(nodes_per_layer), layers = size(nodes_per_layer) - inference_engine = inference_engine_t( & + neural_network = neural_network_t( & metadata = [string_t("XOR"), string_t("Damian Rouson"), string_t("2023-07-02"), string_t("step"), string_t("false")], & weights = reshape([real:: [1,1,0, 0,1,1, 0,0,0], [1,0,0, -2,0,0, 1,0,0]], [max_n, max_n, layers-1]), & biases = reshape([[0.,-1.99,0.], [0., 0., 0.]], [max_n, layers-1]), & @@ -87,12 +87,12 @@ function single_hidden_layer_xor_network() result(inference_engine) ) end function - function multi_layer_xor_network() result(inference_engine) - type(inference_engine_t) inference_engine + function multi_layer_xor_network() result(neural_network) + type(neural_network_t) neural_network integer, parameter :: nodes_per_layer(*) = [2, 3, 3, 1] integer, parameter :: max_n = maxval(nodes_per_layer), layers = size(nodes_per_layer) - inference_engine = inference_engine_t( & + neural_network = neural_network_t( & metadata = [string_t("XOR"), string_t("Damian Rouson"), string_t("2023-07-02"), string_t("step"), string_t("false")], & weights = reshape([real:: [1,1,0, 0,1,1, 1,0,0, 1,0,0, 0,1,0, 0,0,1], [1,0,0, -2,0,0, 1,0,0]], & [max_n, max_n, layers-1]), & @@ -101,13 +101,13 @@ function multi_layer_xor_network() result(inference_engine) ) end function - function decrement_split_combine_increment() result(inference_engine) + function decrement_split_combine_increment() result(neural_network) !! Define a network that produces outputs identical to the 2 inputs for any input greater than or equal to 1 !! based on the following algorithm: !! 1. A 1st hidden layer that forwards input 1 unmolested and decrements input 2 by 1, !! 2. A 2nd hidden layer that forwards input 1 unmolested and splits input 2 into two halves, !! 3. An output layer that recombines those two halves and increments the result by 1. - type(inference_engine_t) inference_engine + type(neural_network_t) neural_network integer, parameter :: inputs = 2, hidden(*) = [2,3], outputs = 2 ! number of neurons in input, output, and hidden layers integer, parameter :: n(*) = [inputs, hidden(1), hidden(2), outputs] ! nodes per layer integer, parameter :: n_max = maxval(n), layers=size(n) ! max layer width, number of layers @@ -116,7 +116,7 @@ function decrement_split_combine_increment() result(inference_engine) w(*,*,*) = reshape( [1.,0.,0., 0.,1.,0., 0.,0.,0., 1.,0.,0., 0.,.5,.5, 0.,0.,0., 1.,0.,0., 0.,1.,0., 0.,1.,1.], w_shape), & b(*,*) = reshape( [0.,-1.,0., 0.,0.,0., 0.,1.,0.], b_shape) - inference_engine = inference_engine_t( & + neural_network = neural_network_t( & metadata = [ & string_t("Decrement/Split/Combine/Increment => Identity") & ,string_t("Damian Rouson") & @@ -127,9 +127,9 @@ function decrement_split_combine_increment() result(inference_engine) ) end function - function double_precision_network() result(inference_engine) + function double_precision_network() result(neural_network) !! The result is a double-precision version of the this network defined in the decrement_split_combine_increment function - type(inference_engine_t(double_precision)) inference_engine + type(neural_network_t(double_precision)) neural_network integer, parameter :: inputs = 2, hidden(*) = [2,3], outputs = 2 ! number of neurons in input, output, and hidden layers integer, parameter :: n(*) = [inputs, hidden(1), hidden(2), outputs] ! nodes per layer integer, parameter :: n_max = maxval(n), layers=size(n) ! max layer width, number of layers @@ -139,15 +139,15 @@ function double_precision_network() result(inference_engine) [double precision :: 1.,0.,0., 0.,1.,0., 0.,0.,0., 1.,0.,0., 0.,.5,.5, 0.,0.,0., 1.,0.,0., 0.,1.,0., 0.,1.,1.], w_shape & ), b(*,*) = reshape( [double precision :: 0.,-1.,0., 0.,0.,0., 0.,1.,0.], b_shape) - inference_engine = inference_engine_t( & + neural_network = neural_network_t( & metadata = metadata_t( & string_t("Double-Precision"), string_t("Damian Rouson"), string_t("2024-09-02"), string_t("relu"), string_t("false") & ), weights = w, biases = b, nodes = n & ) end function - function varying_width() result(inference_engine) - type(inference_engine_t) inference_engine + function varying_width() result(neural_network) + type(neural_network_t) neural_network integer, parameter :: inputs = 2, hidden(*) = [2,3], outputs = 2 ! number of neurons in input, output, and hidden layers integer, parameter :: n(*) = [inputs, hidden(1), hidden(2), outputs] ! nodes per layer integer, parameter :: n_max = maxval(n), layers=size(n) ! max layer width, number of layers @@ -158,14 +158,14 @@ function varying_width() result(inference_engine) w = reshape( [(i, i=1,product(w_shape))], w_shape) b = reshape( [(maxval(w) + i, i=1,product(b_shape))], b_shape) - inference_engine = inference_engine_t( & + neural_network = neural_network_t( & metadata = [string_t("random"), string_t("Damian Rouson"), string_t("2024-07-03"), string_t("sigmoid"), string_t("false")], & weights = w, biases = b, nodes = n & ) end function - function distinct_parameters() result(inference_engine) - type(inference_engine_t) inference_engine + function distinct_parameters() result(neural_network) + type(neural_network_t) neural_network integer, parameter :: inputs = 2, hidden = 3, outputs = 1 ! number of neurons in input, output, and hidden layers integer, parameter :: n(*) = [inputs, hidden, hidden, outputs] ! nodes per layer integer, parameter :: n_max = maxval(n), layers=size(n) ! max layer width, number of layers @@ -176,7 +176,7 @@ function distinct_parameters() result(inference_engine) w = reshape( [(i, i=1,product(w_shape))], w_shape) b = reshape( [(maxval(w) + i, i=1,product(b_shape))], b_shape) - inference_engine = inference_engine_t( & + neural_network = neural_network_t( & metadata = [string_t("random"), string_t("Damian Rouson"), string_t("2023-07-15"), string_t("sigmoid"), string_t("false")], & weights = w, biases = b, nodes = n & ) @@ -184,54 +184,54 @@ function distinct_parameters() result(inference_engine) function multi_hidden_layer_net_to_from_json() result(test_passes) logical test_passes - type(inference_engine_t) inference_engine, from_json + type(neural_network_t) neural_network, from_json type(file_t) json_file - inference_engine = distinct_parameters() - json_file = inference_engine%to_json() - from_json = inference_engine_t(json_file) - test_passes = inference_engine == from_json + neural_network = distinct_parameters() + json_file = neural_network%to_json() + from_json = neural_network_t(json_file) + test_passes = neural_network == from_json end function function varying_width_net_to_from_json() result(test_passes) logical test_passes - associate(inference_engine => varying_width()) - associate(from_json => inference_engine_t( inference_engine%to_json() )) - test_passes = inference_engine == from_json + associate(neural_network => varying_width()) + associate(from_json => neural_network_t( neural_network%to_json() )) + test_passes = neural_network == from_json end associate end associate end function function infer_with_varying_width_net() result(test_passes) logical test_passes - type(inference_engine_t) inference_engine + type(neural_network_t) neural_network type(tensor_t) inputs, outputs real, parameter :: tolerance = 1.E-08 - inference_engine = decrement_split_combine_increment() + neural_network = decrement_split_combine_increment() inputs = tensor_t([1.1, 2.7]) - outputs = inference_engine%infer(inputs) + outputs = neural_network%infer(inputs) test_passes = all(abs(inputs%values() - outputs%values()) < tolerance) end function function double_precision_inference() result(test_passes) logical test_passes - type(inference_engine_t(double_precision)) inference_engine + type(neural_network_t(double_precision)) neural_network type(tensor_t(double_precision)) inputs, outputs real, parameter :: tolerance = 1.D-08 - inference_engine = double_precision_network() + neural_network = double_precision_network() inputs = tensor_t([1.1D0, 2.7D0]) - outputs = inference_engine%infer(inputs) + outputs = neural_network%infer(inputs) test_passes = all(abs(inputs%values() - outputs%values()) < tolerance) end function function elemental_infer_with_1_hidden_layer_xor_net() result(test_passes) logical test_passes - type(inference_engine_t) inference_engine + type(neural_network_t) neural_network - inference_engine = single_hidden_layer_xor_network() + neural_network = single_hidden_layer_xor_network() block type(tensor_t), allocatable :: truth_table(:) @@ -239,7 +239,7 @@ function elemental_infer_with_1_hidden_layer_xor_net() result(test_passes) integer i associate(array_of_inputs => [tensor_t([true,true]), tensor_t([true,false]), tensor_t([false,true]), tensor_t([false,false])]) - truth_table = inference_engine%infer(array_of_inputs) + truth_table = neural_network%infer(array_of_inputs) end associate test_passes = all( & abs(truth_table(1)%values() - false) < tolerance .and. abs(truth_table(2)%values() - true) < tolerance .and. & @@ -250,9 +250,9 @@ function elemental_infer_with_1_hidden_layer_xor_net() result(test_passes) function elemental_infer_with_2_hidden_layer_xor_net() result(test_passes) logical test_passes - type(inference_engine_t) inference_engine + type(neural_network_t) neural_network - inference_engine = multi_layer_xor_network() + neural_network = multi_layer_xor_network() block type(tensor_t), allocatable :: truth_table(:) @@ -260,7 +260,7 @@ function elemental_infer_with_2_hidden_layer_xor_net() result(test_passes) integer i associate(array_of_inputs => [tensor_t([true,true]), tensor_t([true,false]), tensor_t([false,true]), tensor_t([false,false])]) - truth_table = inference_engine%infer(array_of_inputs) + truth_table = neural_network%infer(array_of_inputs) end associate test_passes = all( & abs(truth_table(1)%values() - false) < tolerance .and. abs(truth_table(2)%values() - true) < tolerance .and. & @@ -269,4 +269,4 @@ function elemental_infer_with_2_hidden_layer_xor_net() result(test_passes) end block end function -end module inference_engine_test_m +end module neural_network_test_m diff --git a/test/tensor_map_test_m.F90 b/test/tensor_map_test_m.F90 index b99c31d3b..cdc961411 100644 --- a/test/tensor_map_test_m.F90 +++ b/test/tensor_map_test_m.F90 @@ -6,7 +6,7 @@ module tensor_map_test_m ! External dependencies use assert_m, only : assert use julienne_m, only : string_t, test_t, test_result_t, test_description_t, test_description_substring, file_t - use inference_engine_m, only : tensor_map_t, tensor_t + use fiats_m, only : tensor_map_t, tensor_t #ifdef __GFORTRAN__ use julienne_m, only : test_function_i #endif diff --git a/test/tensor_test_m.f90 b/test/tensor_test_m.f90 index f7e321504..7d4ac7c21 100644 --- a/test/tensor_test_m.f90 +++ b/test/tensor_test_m.f90 @@ -11,7 +11,7 @@ module tensor_test_m #endif ! Internal dependencies - use inference_engine_m, only : tensor_t + use fiats_m, only : tensor_t implicit none diff --git a/test/trainable_network_test_m.F90 b/test/trainable_network_test_m.F90 index 92518d74d..f6c88d910 100644 --- a/test/trainable_network_test_m.F90 +++ b/test/trainable_network_test_m.F90 @@ -12,7 +12,7 @@ module trainable_network_test_m #endif ! Internal dependencies - use inference_engine_m, only : trainable_network_t, inference_engine_t, tensor_t, input_output_pair_t, mini_batch_t, shuffle + use fiats_m, only : trainable_network_t, neural_network_t, tensor_t, input_output_pair_t, mini_batch_t, shuffle implicit none private @@ -183,7 +183,7 @@ function two_zeroed_hidden_layers() result(trainable_network) w = 0. b = 0. - trainable_network = trainable_network_t( inference_engine_t( & + trainable_network = trainable_network_t( neural_network_t( & nodes = neurons, weights = w, biases = b & ,metadata = [string_t("2-hide|3-wide"), string_t("Rouson"), string_t("2023-06-30"), string_t("sigmoid"), string_t("false")] & )) @@ -201,7 +201,7 @@ function two_random_hidden_layers() result(trainable_network) b = 2*b w = 2*w - trainable_network = trainable_network_t( inference_engine_t( & + trainable_network = trainable_network_t( neural_network_t( & nodes = neurons, weights = w, biases = b & ,metadata = [string_t("2-hide|3-wide"), string_t("Rouson"), string_t("2023-06-30"), string_t("sigmoid"), string_t("false")] & )) @@ -420,7 +420,7 @@ function perturbed_identity_network(perturbation_magnitude) result(trainable_net call random_number(harvest) harvest = perturbation_magnitude*harvest - trainable_network = trainable_network_t( inference_engine_t( & + trainable_network = trainable_network_t( neural_network_t( & nodes = nodes_per_layer, & weights = identity + harvest , & biases = reshape([real:: [0,0], [0,0], [0,0]], [max_n, layers-1]), & diff --git a/test/training_configuration_test_m.F90 b/test/training_configuration_test_m.F90 index 13ed5ba2d..a09dc0c09 100644 --- a/test/training_configuration_test_m.F90 +++ b/test/training_configuration_test_m.F90 @@ -4,7 +4,7 @@ module training_configuration_test_m !! Test training_configuration_t object I/O and construction ! External dependencies - use inference_engine_m, only : training_configuration_t, hyperparameters_t, network_configuration_t + use fiats_m, only : training_configuration_t, hyperparameters_t, network_configuration_t use julienne_m, only : test_t, test_result_t, test_description_t, test_description_substring, string_t, file_t #ifdef __GFORTRAN__ use julienne_m, only : test_function_i From 2fbb16184cb5457f68e71cf97b5cd29b91340f02 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Mon, 14 Oct 2024 02:40:06 -0700 Subject: [PATCH 089/105] doc(ford): update project name & summary --- README.md | 72 ++++++++++++++++++++++++++++++------------------------- ford.md | 10 ++++---- 2 files changed, 44 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index f6c6656f2..aba5ca419 100644 --- a/README.md +++ b/README.md @@ -1,31 +1,28 @@ - ```ascii - _ __ _ - (_) / _| (_) - _ _ __ | |_ ___ _ __ ___ _ __ ___ ___ ___ _ __ __ _ _ _ __ ___ - | | '_ \| _/ _ \ '__/ _ \ '_ \ / __/ _ \ __ / _ \ '_ \ / _` | | '_ \ / _ \ - | | | | | || __/ | | __/ | | | (_| __/ |__| | __/ | | | (_| | | | | | __/ - |_|_| |_|_| \___|_| \___|_| |_|\___\___| \___|_| |_|\__, |_|_| |_|\___| - __/ | - |___/ -``` -Inference-Engine -================ +___________.__ __ +\_ _____/|__|____ _/ |_ ______ + | __) | \__ \\ __\/ ___/ + | \ | |/ __ \| | \___ \ + \___ / |__(____ /__| /____ > + \/ \/ \/ +``` +Fiats: Functional inference and training for surrogates +======================================================= [Overview](#overview) | [Getting Started](#getting-started) | [Documentation](#documentation) Overview -------- -Inference-Engine supports research in the training and deployment of neural-network surrogate models for computational science. -Inference-Engine also provides a platform for exploring and advancing the native parallel programming features of Fortran 2023 in the context of deep learning. -The language features of interest facilitate loop-level parallelism via the `do concurrent` construct and Single-Program, Multiple Data (SMPD) parallelism via "multi-image" (e.g., multithreaded or multiprocess) execution. -Toward these ends, +Fiats supports research on the training and deployment of neural-network surrogate models for computational science. +Fiats also provides a platform for exploring and advancing the native parallel programming features of Fortran 2023 in the context of deep learning. +Toward these ends, the design of Fiats centers around functional programming patterns that facilitate concurrent execution: +The language features of interest facilitate loop-level parallelism via the `do concurrent` construct and Single-Program, Multiple Data (SMPD) parallelism via "multi-image" (e.g., multithreaded or multiprocess) execution: -* Most Inference-Engine procedures are `pure` and thus satisfy a language requirement for invocation inside `do concurrent`, +* Most Fiats procedures are `pure` and thus satisfy a language requirement for invocation inside `do concurrent`, * The network training procedure uses `do concurrent` to expose automatic parallelization opportunities to compilers, and * Exploiting multi-image execution to speedup training is under investigation. -To broaden support for the native parallel features, Inference-Engine's contributors also write compiler tests, bug reports, and patches; develop a parallel runtime library ([Caffeine]); participate in the language standardization process; and provide example inference and training code for exercising and evaluating compilers' automatic parallelization capabilities on processors and accelerators, including Graphics Processing Units (GPUs). +To broaden support for the native parallel features, the Fiats contributors also write compiler tests, bug reports, and patches; develop a parallel runtime library ([Caffeine]); participate in the language standardization process; and provide example inference and training code for exercising and evaluating compilers' automatic parallelization capabilities on processors and accelerators, including Graphics Processing Units (GPUs). Available optimizers: * Stochastic gradient descent and @@ -51,7 +48,7 @@ Getting Started The [example] subdirectory contains demonstrations of several relatively simple use cases. We recommend reviewing the examples to see how to handle basic tasks such as configuring a network training run or reading a neural network and using it to perform inference. -The [demo] subdirectory contains demonstration applications that depend on Inference-Engine but build separately due to requiring additional prerequisites such as NetCDF and HDF5. +The [demo] subdirectory contains demonstration applications that depend on Fiats but build separately due to requiring additional prerequisites such as NetCDF and HDF5. The demonstration applications - Train a cloud microphysics model surrogate for the Intermediate Complexity Atmospheric Research ([ICAR]) package, - Perform inference using a pretrained model for aerosol dynamics in the Energy Exascale Earth System ([E3SM]) package, and @@ -61,20 +58,18 @@ The demonstration applications Because this repository supports programming language research, the code exercises new language features in novel ways. We recommend using any compiler's latest release or even building open-source compilers from source. The [handy-dandy] repository contains scripts capturing steps for building the [LLVM] compiler suite. -The remainder of this section contains commands for building Inference-Engine with a recent Fortran compiler and the Fortran Package Manager ([`fpm`]) in your `PATH`. +The remainder of this section contains commands for building Fiats with a recent Fortran compiler and the Fortran Package Manager ([`fpm`]) in your `PATH`. -#### GNU (`gfortran`) 13 or higher required -``` -fpm test --compiler gfortran --profile release -``` -#### NAG (`nagfor`) + +#### LLVM (`flang-new`): supported +With LLVM `flang` 20 installed and in your `PATH`, build and test Fiats with the installed `flang-new` symlink: ``` -fpm test --compiler nagfor --flag -fpp --profile release +fpm test --compiler flang-new --flag "-O3" ``` -#### LLVM (`flang-new`) -Building with `flang-new` requires passing flags to enable the compiler's experimental support for assumed-rank entities: +With LLVM flang 19, enable the compiler's experimental support for assumed-rank entities: + ``` fpm test --compiler flang-new --flag "-mmlir -allow-assumed-rank -O3" ``` @@ -89,9 +84,20 @@ fpm run \ -- --network model.json ``` -where `model.json` must be a neural network in the [JSON] format used by Inference-Engine and the companion [nexport] package. +where `model.json` must be a neural network in the [JSON] format used by Fiats and the companion [nexport] package. Automatic parallelization for training is under development. +#### NAG (`nagfor`): +``` +fpm test --compiler nagfor --flag -fpp --profile release +``` + +#### GNU (`gfortran`): + +``` +fpm test --compiler gfortran --profile release +``` + #### Intel (`ifx`) ``` fpm test --compiler ifx --profile release --flag -O3 @@ -119,7 +125,7 @@ fpm test --compiler crayftn.sh ``` ### Configuring a training run -Inference-Engine imports hyperparameters and network configurations to and from JSON files. +Fiats imports hyperparameters and network configurations to and from JSON files. To see the expected file format, run the [print-training-configuration] example as follows: ``` % fpm run --example print-training-configuration --compiler gfortran @@ -141,7 +147,7 @@ Project is up to date } } ``` -Inference-Engine's JSON file format is fragile: splitting or combining lines breaks the file reader. +The Fiats JSON file format is fragile: splitting or combining lines breaks the file reader. Files with added or removed white space or reordered whole objects ("hyperparameters" or "network configuration") should work. A future release will leverage the [rojff] JSON interface to allow for more flexible file formatting. @@ -163,7 +169,7 @@ Before halting, the program will print a table of expected and predicted saturat The program also writes the neural network initial condition to `initial-network.json` and the final (trained) network to the file specified in the above command: `sat-mix-rat.json`. ### Performing inference -Users with a PyTorch model may use [nexport] to export the model to JSON files that Inference-Engine can read. +Users with a PyTorch model may use [nexport] to export the model to JSON files that Fiats can read. Examples of performing inference using a neural-network JSON file are in [example/concurrent-inferences]. Documentation @@ -182,7 +188,7 @@ Please see our [GitHub Pages site] for HTML documentation generated by [`ford`] [`ford`]: https://github.com/Fortran-FOSS-Programmers/ford [`fpm`]: https://github.com/fortran-lang/fpm [Getting Started]: #getting-started -[GitHub Pages site]: https://berkeleylab.github.io/inference-engine/ +[GitHub Pages site]: https://berkeleylab.github.io/fiats/ [handy-dandy]: https://github.com/rouson/handy-dandy/blob/main/src [ICAR]: https://github.com/BerkeleyLab/icar/tree/neural-net [JSON]: https://www.json.org/json-en.html diff --git a/ford.md b/ford.md index 2e8c5c03e..35de3c8ef 100644 --- a/ford.md +++ b/ford.md @@ -1,5 +1,5 @@ -project: Inference-Engine -summary: A deep learning library targeting high-performance computing (HPC) applications with performance-critical inference and training needs. +project: Fiats +summary: Functional inference and training of surrogate models for computational science. src_dir: src/ src_dir: example exclude_dir: doc @@ -17,12 +17,12 @@ coloured_edges: true sort: permission-alpha extra_mods: iso_fortran_env:https://gcc.gnu.org/onlinedocs/gfortran/ISO_005fFORTRAN_005fENV.html iso_c_binding:https://gcc.gnu.org/onlinedocs/gfortran/ISO_005fC_005fBINDING.html#ISO_005fC_005fBINDING -project_github: https://github.com/berkeleylab/inference-engine +project_github: https://github.com/berkeleylab/fiats author: Berkeley Lab print_creation_date: true creation_date: %Y-%m-%d %H:%M %z -project_github: https://github.com/berkeleylab/inference-engine -project_download: https://github.com/berkeleylab/inference-engine/releases +project_github: https://github.com/berkeleylab/fiats +project_download: https://github.com/berkeleylab/fiats/releases github: https://github.com/berkeleylab predocmark_alt: > predocmark: < From bada395389762737fe9ae1d0d974d49cbf69a763 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Mon, 14 Oct 2024 21:21:55 -0700 Subject: [PATCH 090/105] chore: rename asymmetric_{engine,network}_test --- ...engine_test_m.F90 => asymmetric_network_test_m.F90} | 10 +++++----- test/main.F90 | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) rename test/{asymmetric_engine_test_m.F90 => asymmetric_network_test_m.F90} (95%) diff --git a/test/asymmetric_engine_test_m.F90 b/test/asymmetric_network_test_m.F90 similarity index 95% rename from test/asymmetric_engine_test_m.F90 rename to test/asymmetric_network_test_m.F90 index fe69fa759..f25dbfdda 100644 --- a/test/asymmetric_engine_test_m.F90 +++ b/test/asymmetric_network_test_m.F90 @@ -1,7 +1,7 @@ ! Copyright (c), The Regents of the University of California ! Terms of use are as specified in LICENSE.txt -module asymmetric_engine_test_m - !! Define asymmetric tests and procedures required for reporting results +module asymmetric_network_test_m + !! Define tests in which the desired output depends asymmetrically on the inputs ! External dependencies use assert_m, only : assert @@ -14,9 +14,9 @@ module asymmetric_engine_test_m implicit none private - public :: asymmetric_engine_test_t + public :: asymmetric_network_test_t - type, extends(test_t) :: asymmetric_engine_test_t + type, extends(test_t) :: asymmetric_network_test_t contains procedure, nopass :: subject procedure, nopass :: results @@ -144,4 +144,4 @@ function xor_and_2nd_input_truth_table() result(test_passes) end function -end module asymmetric_engine_test_m +end module asymmetric_network_test_m diff --git a/test/main.F90 b/test/main.F90 index a280ae011..40026b524 100644 --- a/test/main.F90 +++ b/test/main.F90 @@ -2,7 +2,7 @@ ! Terms of use are as specified in LICENSE.txt program main use neural_network_test_m, only : neural_network_test_t - use asymmetric_engine_test_m, only : asymmetric_engine_test_t + use asymmetric_network_test_m, only : asymmetric_network_test_t use trainable_network_test_m, only : trainable_network_test_t use metadata_test_m, only : metadata_test_t use hyperparameters_test_m, only : hyperparameters_test_t @@ -14,7 +14,7 @@ program main implicit none type(neural_network_test_t) neural_network_test - type(asymmetric_engine_test_t) asymmetric_engine_test + type(asymmetric_network_test_t) asymmetric_network_test type(trainable_network_test_t) trainable_network_test type(hyperparameters_test_t) hyperparameters_test type(metadata_test_t) metadata_test @@ -47,7 +47,7 @@ program main call training_configuration_test%report(passes, tests) call tensor_map_test%report(passes, tests) call tensor_test%report(passes, tests) - call asymmetric_engine_test%report(passes, tests) + call asymmetric_network_test%report(passes, tests) call neural_network_test%report(passes, tests) call trainable_network_test%report(passes, tests) call cpu_time(t_finish) From be7960ef1cc59afe40b2be20dafef886180dc7ee Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Mon, 14 Oct 2024 22:02:08 -0700 Subject: [PATCH 091/105] doc(README): update compiler status --- README.md | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index aba5ca419..096add8c2 100644 --- a/README.md +++ b/README.md @@ -63,19 +63,18 @@ The remainder of this section contains commands for building Fiats with a recent #### LLVM (`flang-new`): supported -With LLVM `flang` 20 installed and in your `PATH`, build and test Fiats with the installed `flang-new` symlink: +With LLVM `flang` 20 installed in your `PATH`, build and test Fiats with the installed `flang-new` symlink in order for `fpm` to correctly identify the compiler: ``` fpm test --compiler flang-new --flag "-O3" ``` - -With LLVM flang 19, enable the compiler's experimental support for assumed-rank entities: +With LLVM `flang` 19, enable the compiler's experimental support for assumed-rank entities: ``` fpm test --compiler flang-new --flag "-mmlir -allow-assumed-rank -O3" ``` ##### _Experimental:_ Automatic parallelization of `do concurrent` on CPUs -With the `amd_trunk_dev` branch of the [ROCm fork] fork of LLVM, automatic parallelization currently works for inference, e.g. +With the `amd_trunk_dev` branch of the [ROCm fork] fork of LLVM, automatically parallelize inference calculations inside `do concurrent` constructs: ``` fpm run \ --example concurrent-inferences \ @@ -85,7 +84,8 @@ fpm run \ ``` where `model.json` must be a neural network in the [JSON] format used by Fiats and the companion [nexport] package. -Automatic parallelization for training is under development. + +Automatic parallelization for training neural networks is under development. #### NAG (`nagfor`): ``` @@ -93,12 +93,15 @@ fpm test --compiler nagfor --flag -fpp --profile release ``` #### GNU (`gfortran`): - +Compiler bugs related to parameterized derived types currently prevent `gfortran` from building Fiats versions 0.15.0 or later. +Test and build earlier versions of Fiats build with the following command: ``` fpm test --compiler gfortran --profile release ``` #### Intel (`ifx`) +Compiler bugs related to parameterized derived types currently prevent `gfortran` from building Fiats versions 0.15.0 or later. +Test and build earlier versions of Fiats build with the following command: ``` fpm test --compiler ifx --profile release --flag -O3 ``` @@ -106,7 +109,7 @@ fpm test --compiler ifx --profile release --flag -O3 ##### _Experimental:_ Automatic offloading of `do concurrent` to GPUs This capability is under development with the goal to facilitate automatic GPU offloading via the following command: ``` -fpm test --compiler ifx --profile releae --flag "-fopenmp-target-do-concurrent -qopenmp -fopenmp-targets=spir64 -O3" +fpm test --compiler ifx --profile release --flag "-fopenmp-target-do-concurrent -qopenmp -fopenmp-targets=spir64 -O3" ``` #### HPE (`crayftn.sh`) -- under development @@ -164,7 +167,7 @@ The following is representative output after 3000 epochs: 2000 0.61259E-04 9.8345 2,4,72,2,1 3000 0.45270E-04 14.864 2,4,72,2,1 ``` -The example program halts execution after reaching a cost-function threshold (which requires millions of epochws) or a maximum number of iterations or if the program detects a file named `stop` in the source-tree root directory. +The example program halts execution after reaching a cost-function threshold (which requires millions of epochs) or a maximum number of iterations or if the program detects a file named `stop` in the source-tree root directory. Before halting, the program will print a table of expected and predicted saturated mixing ratio values across a range of input pressures and temperatures, wherein two the inputs have each been mapped to the unit interval [0,1]. The program also writes the neural network initial condition to `initial-network.json` and the final (trained) network to the file specified in the above command: `sat-mix-rat.json`. @@ -174,7 +177,7 @@ Examples of performing inference using a neural-network JSON file are in [exampl Documentation ------------- -Please see our [GitHub Pages site] for HTML documentation generated by [`ford`] or generate documentaiton locally by installing `ford` and executing `ford ford.md`. +Please see our [GitHub Pages site] for HTML documentation generated by [`ford`] or generate documentation locally by installing `ford` and executing `ford ford.md`. [Building and testing]: #building-and-testing From 4b09cdc9f86d38ccab37982bc6ce8dd186234295 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Mon, 14 Oct 2024 22:10:58 -0700 Subject: [PATCH 092/105] refac(json): rename acceptable_engine_tag This commit renames "acceptable_engine_tag" to "minimum_acceptable_tag" in the network JSON file format. --- fpm.toml | 2 +- src/fiats/neural_network_s.F90 | 26 +++++++++++++------------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/fpm.toml b/fpm.toml index fbf885f9c..815ffa443 100644 --- a/fpm.toml +++ b/fpm.toml @@ -1,5 +1,5 @@ name = "fiats" -version = "0.13.1" +version = "0.15.0" license = "see LICENSE.txt" author = "Damian Rouson, Tan Nguyen, Jordan Welsman, David Torres, Brad Richardson, Katherine Rasmussen, Federica Villani, Dan Bonachea" maintainer = "rouson@lbl.gov" diff --git a/src/fiats/neural_network_s.F90 b/src/fiats/neural_network_s.F90 index c652f6f2c..f1ee235ca 100644 --- a/src/fiats/neural_network_s.F90 +++ b/src/fiats/neural_network_s.F90 @@ -8,7 +8,7 @@ use neuron_m, only : neuron_t implicit none - character(len=*), parameter :: acceptable_engine_tag = "0.13.0" ! git tag capable of reading the current json file format + character(len=*), parameter :: minimum_acceptable_tag = "0.15.0" ! git tag capable of reading the current json file format integer, parameter :: input_layer = 0 contains @@ -303,7 +303,7 @@ associate( json_lines => & brace + & ! { - file_version_lines + & ! "acceptable_engine_tag": ... + file_version_lines + & ! "minimum_acceptable_tag": ... metadata_lines + & ! "metadata": ... tensor_map_lines + & ! "inputs_tensor_map": ... tensor_map_lines + & ! "outputs_tensor_map": ... @@ -319,7 +319,7 @@ ) allocate(lines(json_lines)) lines(brace) = string_t('{') - lines(brace+1:brace+file_version_lines)= string_t(' "acceptable_engine_tag": "')//acceptable_engine_tag//'",' + lines(brace+1:brace+file_version_lines)= string_t(' "minimum_acceptable_tag": "')//minimum_acceptable_tag//'",' associate(meta_start => brace + file_version_lines + 1) associate(meta_end => meta_start + metadata_lines - 1) lines(meta_start:meta_end) = self%metadata_%to_json() @@ -419,7 +419,7 @@ associate( json_lines => & brace + & ! { - file_version_lines + & ! "acceptable_engine_tag": ... + file_version_lines + & ! "minimum_acceptable_tag": ... metadata_lines + & ! "metadata": ... tensor_map_lines + & ! "inputs_tensor_map": ... tensor_map_lines + & ! "outputs_tensor_map": ... @@ -435,7 +435,7 @@ ) allocate(lines(json_lines)) lines(brace) = string_t('{') - lines(brace+1:brace+file_version_lines)= string_t(' "acceptable_engine_tag": "')//acceptable_engine_tag//'",' + lines(brace+1:brace+file_version_lines)= string_t(' "minimum_acceptable_tag": "')//minimum_acceptable_tag//'",' associate(meta_start => brace + file_version_lines + 1) associate(meta_end => meta_start + metadata_lines - 1) lines(meta_start:meta_end) = self%metadata_%to_json() @@ -512,11 +512,11 @@ block character(len=:), allocatable :: tag - tag = lines(2)%get_json_value("acceptable_engine_tag", mold="") + tag = lines(2)%get_json_value("minimum_acceptable_tag", mold="") call assert( & - tag == acceptable_engine_tag & - ,"neural_network_s(default_real_from_json): acceptable_engine_tag" & - ,tag //"(expected " //acceptable_engine_tag // ")" & + tag == minimum_acceptable_tag & + ,"neural_network_s(default_real_from_json): minimum_acceptable_tag" & + ,tag //"(expected " //minimum_acceptable_tag // ")" & ) end block check_git_tag @@ -614,11 +614,11 @@ block character(len=:), allocatable :: tag - tag = lines(2)%get_json_value("acceptable_engine_tag", mold="") + tag = lines(2)%get_json_value("minimum_acceptable_tag", mold="") call assert( & - tag == acceptable_engine_tag & - ,"neural_network_s(double_precision_from_json): acceptable_engine_tag" & - ,tag //"(expected " //acceptable_engine_tag // ")" & + tag == minimum_acceptable_tag & + ,"neural_network_s(double_precision_from_json): minimum_acceptable_tag" & + ,tag //"(expected " //minimum_acceptable_tag // ")" & ) end block check_git_tag From d69324dfc862378d4e7a88a662f3149d029f2636 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Mon, 14 Oct 2024 22:17:31 -0700 Subject: [PATCH 093/105] chore: rm unused exhcnage_t type --- src/fiats/neural_network_m.f90 | 28 ---------------------------- src/fiats/neural_network_s.F90 | 24 ------------------------ 2 files changed, 52 deletions(-) diff --git a/src/fiats/neural_network_m.f90 b/src/fiats/neural_network_m.f90 index eb72c1524..06e5737e0 100644 --- a/src/fiats/neural_network_m.f90 +++ b/src/fiats/neural_network_m.f90 @@ -15,7 +15,6 @@ module neural_network_m private public :: neural_network_t public :: unmapped_network_t - public :: exchange_t public :: workspace_t type neural_network_t(k) @@ -39,7 +38,6 @@ module neural_network_m generic :: assert_conformable_with => default_real_assert_conformable_with, double_precision_assert_conformable_with generic :: skip => default_real_skip, double_precision_skip generic :: activation_function_name => default_real_activation_name, double_precision_activation_name - generic :: to_exchange => default_real_to_exchange, double_precision_to_exchange generic :: learn => default_real_learn generic :: assert_consistency => default_real_consistency, double_precision_consistency procedure, private, non_overridable :: default_real_consistency, double_precision_consistency @@ -56,7 +54,6 @@ module neural_network_m procedure, private, non_overridable :: default_real_assert_conformable_with, double_precision_assert_conformable_with procedure, private, non_overridable :: default_real_skip, double_precision_skip procedure, private, non_overridable :: default_real_activation_name, double_precision_activation_name - procedure, private, non_overridable :: default_real_to_exchange, double_precision_to_exchange end type type workspace_t(k) @@ -97,31 +94,6 @@ pure module function default_real_allocated(self) result(all_allocated) end interface - type exchange_t(k) - integer, kind :: k = default_real - type(tensor_map_t(k)) input_map_, output_map_ - type(metadata_t) metadata_ - real(k), allocatable :: weights_(:,:,:), biases_(:,:) - integer, allocatable :: nodes_(:) - type(activation_t) activation_ - end type - - interface - - module function default_real_to_exchange(self) result(exchange) - implicit none - class(neural_network_t), intent(in) :: self - type(exchange_t) exchange - end function - - module function double_precision_to_exchange(self) result(exchange) - implicit none - class(neural_network_t(double_precision)), intent(in) :: self - type(exchange_t(double_precision)) exchange - end function - - end interface - interface neural_network_t module function default_real_construct_from_components(metadata, weights, biases, nodes, input_map, output_map) & diff --git a/src/fiats/neural_network_s.F90 b/src/fiats/neural_network_s.F90 index f1ee235ca..6f3334c80 100644 --- a/src/fiats/neural_network_s.F90 +++ b/src/fiats/neural_network_s.F90 @@ -29,30 +29,6 @@ tensor = self%output_map_%map_from_training_range(normalized_tensor) end procedure - module procedure default_real_to_exchange - exchange%input_map_ = self%input_map_ - exchange%output_map_ = self%output_map_ - associate(strings => self%metadata_%strings()) - exchange%metadata_ = metadata_t(strings(1),strings(2),strings(3),strings(4),strings(5)) - end associate - exchange%weights_ = self%weights_ - exchange%biases_ = self%biases_ - exchange%nodes_ = self%nodes_ - exchange%activation_ = self%activation_ - end procedure - - module procedure double_precision_to_exchange - exchange%input_map_ = self%input_map_ - exchange%output_map_ = self%output_map_ - associate(strings => self%metadata_%strings()) - exchange%metadata_ = metadata_t(strings(1),strings(2),strings(3),strings(4),strings(5)) - end associate - exchange%weights_ = self%weights_ - exchange%biases_ = self%biases_ - exchange%nodes_ = self%nodes_ - exchange%activation_ = self%activation_ - end procedure - module procedure default_real_infer real, allocatable :: a(:,:) From b8473d67151b731b3d2a8b79d5fb4c2e7f16ec0d Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Mon, 14 Oct 2024 22:42:30 -0700 Subject: [PATCH 094/105] doc(README.md): update --- README.md | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 096add8c2..a02840c94 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ ___________.__ __ ``` Fiats: Functional inference and training for surrogates ======================================================= +Alternatively, _Fortran inference and training for science_. [Overview](#overview) | [Getting Started](#getting-started) | [Documentation](#documentation) @@ -15,11 +16,11 @@ Overview -------- Fiats supports research on the training and deployment of neural-network surrogate models for computational science. Fiats also provides a platform for exploring and advancing the native parallel programming features of Fortran 2023 in the context of deep learning. -Toward these ends, the design of Fiats centers around functional programming patterns that facilitate concurrent execution: -The language features of interest facilitate loop-level parallelism via the `do concurrent` construct and Single-Program, Multiple Data (SMPD) parallelism via "multi-image" (e.g., multithreaded or multiprocess) execution: +The design of Fiats centers around functional programming patterns that facilitate concurrency, including loop-level parallelism via the `do concurrent` construct and Single-Program, Multiple Data (SMPD) parallelism via "multi-image" (e.g., multithreaded or multiprocess) execution. +Towards these ends, * Most Fiats procedures are `pure` and thus satisfy a language requirement for invocation inside `do concurrent`, -* The network training procedure uses `do concurrent` to expose automatic parallelization opportunities to compilers, and +* The network training procedure use `do concurrent` to expose automatic parallelization opportunities to compilers, and * Exploiting multi-image execution to speedup training is under investigation. To broaden support for the native parallel features, the Fiats contributors also write compiler tests, bug reports, and patches; develop a parallel runtime library ([Caffeine]); participate in the language standardization process; and provide example inference and training code for exercising and evaluating compilers' automatic parallelization capabilities on processors and accelerators, including Graphics Processing Units (GPUs). @@ -48,7 +49,7 @@ Getting Started The [example] subdirectory contains demonstrations of several relatively simple use cases. We recommend reviewing the examples to see how to handle basic tasks such as configuring a network training run or reading a neural network and using it to perform inference. -The [demo] subdirectory contains demonstration applications that depend on Fiats but build separately due to requiring additional prerequisites such as NetCDF and HDF5. +The [demo] subdirectory contains demonstration applications that depend on Fiats but build separately due to requiring additional prerequisites such as [NetCDF] and [HDF5]. The demonstration applications - Train a cloud microphysics model surrogate for the Intermediate Complexity Atmospheric Research ([ICAR]) package, - Perform inference using a pretrained model for aerosol dynamics in the Energy Exascale Earth System ([E3SM]) package, and @@ -58,11 +59,9 @@ The demonstration applications Because this repository supports programming language research, the code exercises new language features in novel ways. We recommend using any compiler's latest release or even building open-source compilers from source. The [handy-dandy] repository contains scripts capturing steps for building the [LLVM] compiler suite. -The remainder of this section contains commands for building Fiats with a recent Fortran compiler and the Fortran Package Manager ([`fpm`]) in your `PATH`. +The remainder of this section contains commands for building Fiats with a recent Fortran compiler and the Fortran Package Manager ([`fpm`]). - - -#### LLVM (`flang-new`): supported +#### LLVM (`flang-new`) With LLVM `flang` 20 installed in your `PATH`, build and test Fiats with the installed `flang-new` symlink in order for `fpm` to correctly identify the compiler: ``` fpm test --compiler flang-new --flag "-O3" @@ -87,12 +86,12 @@ where `model.json` must be a neural network in the [JSON] format used by Fiats a Automatic parallelization for training neural networks is under development. -#### NAG (`nagfor`): +#### NAG (`nagfor`) ``` fpm test --compiler nagfor --flag -fpp --profile release ``` -#### GNU (`gfortran`): +#### GNU (`gfortran`) Compiler bugs related to parameterized derived types currently prevent `gfortran` from building Fiats versions 0.15.0 or later. Test and build earlier versions of Fiats build with the following command: ``` @@ -112,7 +111,7 @@ This capability is under development with the goal to facilitate automatic GPU o fpm test --compiler ifx --profile release --flag "-fopenmp-target-do-concurrent -qopenmp -fopenmp-targets=spir64 -O3" ``` -#### HPE (`crayftn.sh`) -- under development +#### HPE (`crayftn.sh`) Support for the Cray Compiler Environment (CCE) Fortran compiler is under development. Building with the CCE `ftn` compiler wrapper requires an additional trivial wrapper shell script. For example, create a file `crayftn.sh` with the following contents and @@ -193,8 +192,10 @@ Please see our [GitHub Pages site] for HTML documentation generated by [`ford`] [Getting Started]: #getting-started [GitHub Pages site]: https://berkeleylab.github.io/fiats/ [handy-dandy]: https://github.com/rouson/handy-dandy/blob/main/src +[HDF5]: https://www.hdfgroup.org/solutions/hdf5/ [ICAR]: https://github.com/BerkeleyLab/icar/tree/neural-net [JSON]: https://www.json.org/json-en.html +[NetCDF]: https://www.unidata.ucar.edu/software/netcdf/ [LLVM]: https://github.com/llvm/llvm-project [nexport]: https://go.lbl.gov/nexport [Overview]: #overview From 6f03491a3aa427a4b710112c521e174fe4dc4efe Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Tue, 15 Oct 2024 17:34:01 -0700 Subject: [PATCH 095/105] chore: white space edits --- src/fiats/activation_m.f90 | 8 ++++---- src/fiats/layer_m.f90 | 26 +++++++++++++------------- src/fiats/neural_network_m.f90 | 3 --- 3 files changed, 17 insertions(+), 20 deletions(-) diff --git a/src/fiats/activation_m.f90 b/src/fiats/activation_m.f90 index 966f6beea..9450a069c 100644 --- a/src/fiats/activation_m.f90 +++ b/src/fiats/activation_m.f90 @@ -20,10 +20,10 @@ module activation_m procedure, non_overridable :: function_name generic :: operator(==) => equals procedure, private :: equals - generic :: evaluate => default_real_evaluate , double_precision_evaluate - procedure, non_overridable :: default_real_evaluate , double_precision_evaluate - generic :: differentiate => default_real_differentiate, double_precision_differentiate - procedure, non_overridable :: default_real_differentiate, double_precision_differentiate + generic :: evaluate => default_real_evaluate , double_precision_evaluate + procedure, non_overridable, private :: default_real_evaluate , double_precision_evaluate + generic :: differentiate => default_real_differentiate, double_precision_differentiate + procedure, non_overridable, private :: default_real_differentiate, double_precision_differentiate end type interface activation_t diff --git a/src/fiats/layer_m.f90 b/src/fiats/layer_m.f90 index 390f3bfa6..e877d397a 100644 --- a/src/fiats/layer_m.f90 +++ b/src/fiats/layer_m.f90 @@ -19,20 +19,20 @@ module layer_m type(neuron_t(k)), private :: neuron !! linked list of this layer's neurons type(layer_t(k)), allocatable, private :: next !! next layer contains - generic :: neural_network => default_real_neural_network, double_precision_neural_network - procedure, private :: default_real_neural_network, double_precision_neural_network - generic :: count_layers => default_real_count_layers, double_precision_count_layers - procedure, private :: default_real_count_layers, double_precision_count_layers - generic :: count_neurons => default_real_count_neurons, double_precision_count_neurons - procedure, private :: default_real_count_neurons, double_precision_count_neurons - generic :: count_inputs => default_real_count_inputs, double_precision_count_inputs - procedure, private :: default_real_count_inputs, double_precision_count_inputs + generic :: neural_network => default_real_neural_network , double_precision_neural_network + procedure, private :: default_real_neural_network , double_precision_neural_network + generic :: count_layers => default_real_count_layers , double_precision_count_layers + procedure, private :: default_real_count_layers , double_precision_count_layers + generic :: count_neurons => default_real_count_neurons , double_precision_count_neurons + procedure, private :: default_real_count_neurons , double_precision_count_neurons + generic :: count_inputs => default_real_count_inputs , double_precision_count_inputs + procedure, private :: default_real_count_inputs , double_precision_count_inputs generic :: neurons_per_layer => default_real_neurons_per_layer, double_precision_neurons_per_layer - procedure, private :: default_real_neurons_per_layer, double_precision_neurons_per_layer - generic :: next_allocated => default_real_next_allocated, double_precision_next_allocated - procedure, private :: default_real_next_allocated, double_precision_next_allocated - generic :: next_pointer => default_real_next_pointer, double_precision_next_pointer - procedure, private :: default_real_next_pointer, double_precision_next_pointer + procedure, private :: default_real_neurons_per_layer, double_precision_neurons_per_layer + generic :: next_allocated => default_real_next_allocated , double_precision_next_allocated + procedure, private :: default_real_next_allocated , double_precision_next_allocated + generic :: next_pointer => default_real_next_pointer , double_precision_next_pointer + procedure, private :: default_real_next_pointer , double_precision_next_pointer end type interface layer_t diff --git a/src/fiats/neural_network_m.f90 b/src/fiats/neural_network_m.f90 index 06e5737e0..50bdd7654 100644 --- a/src/fiats/neural_network_m.f90 +++ b/src/fiats/neural_network_m.f90 @@ -130,9 +130,6 @@ impure elemental module function double_precision_from_json(file) result(neural_ end interface - - - interface ! neural_network_t type-bound procedures elemental module function default_real_approximately_equal(lhs, rhs) result(lhs_eq_rhs) From 31ecd597c14e0a14084ee02f028f07884f6e3a84 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Tue, 15 Oct 2024 17:36:07 -0700 Subject: [PATCH 096/105] chore(training_configuration): rm unused function --- src/fiats/training_configuration_m.f90 | 14 -------- src/fiats/training_configuration_s.F90 | 50 -------------------------- 2 files changed, 64 deletions(-) diff --git a/src/fiats/training_configuration_m.f90 b/src/fiats/training_configuration_m.f90 index eb20a30f0..be693220d 100644 --- a/src/fiats/training_configuration_m.f90 +++ b/src/fiats/training_configuration_m.f90 @@ -28,8 +28,6 @@ module training_configuration_m procedure, private :: default_real_optimizer_name, double_precision_optimizer_name generic :: learning_rate => default_real_learning_rate, double_precision_learning_rate procedure, private :: default_real_learning_rate, double_precision_learning_rate - generic :: differentiable_activation => default_real_differentiable_activation, double_precision_differentiable_activation - procedure, private :: default_real_differentiable_activation, double_precision_differentiable_activation generic :: nodes_per_layer => default_real_nodes_per_layer, double_precision_nodes_per_layer procedure, private :: default_real_nodes_per_layer, double_precision_nodes_per_layer generic :: skip_connections => default_real_skip_connections, double_precision_skip_connections @@ -128,18 +126,6 @@ elemental module function double_precision_learning_rate(self) result(rate) double precision rate end function - module function default_real_differentiable_activation(self) result(activation) - implicit none - class(training_configuration_t), intent(in) :: self - type(activation_t) activation - end function - - module function double_precision_differentiable_activation(self) result(activation) - implicit none - class(training_configuration_t(double_precision)), intent(in) :: self - type(activation_t) activation - end function - pure module function default_real_nodes_per_layer(self) result(nodes) implicit none class(training_configuration_t), intent(in) :: self diff --git a/src/fiats/training_configuration_s.F90 b/src/fiats/training_configuration_s.F90 index 3b982a202..bdb57932e 100644 --- a/src/fiats/training_configuration_s.F90 +++ b/src/fiats/training_configuration_s.F90 @@ -181,54 +181,4 @@ using_skip = self%network_configuration_%skip_connections() end procedure - module procedure default_real_differentiable_activation -#if defined __INTEL_COMPILER || _CRAYFTN - type(string_t) :: activation_name - activation_name = self%network_configuration_%activation_name() -#else - associate(activation_name => self%network_configuration_%activation_name()) -#endif - select case(activation_name%string()) - case ("gelu") - activation = activation_t(gelu) - case ("relu") - activation = activation_t(relu) - case ("sigmoid") - activation = activation_t(sigmoid) - case ("swish") - activation = activation_t(swish) - case default - error stop 'activation_factory_s(factory): unrecognized activation name "' // activation_name%string() // '"' - end select -#if defined __INTEL_COMPILER || _CRAYFTN -#else - end associate -#endif - end procedure - - module procedure double_precision_differentiable_activation -#if defined __INTEL_COMPILER || _CRAYFTN - type(string_t) :: activation_name - activation_name = self%network_configuration_%activation_name() -#else - associate(activation_name => self%network_configuration_%activation_name()) -#endif - select case(activation_name%string()) - case ("gelu") - activation = activation_t(gelu) - case ("relu") - activation = activation_t(relu) - case ("sigmoid") - activation = activation_t(sigmoid) - case ("swish") - activation = activation_t(swish) - case default - error stop 'activation_factory_s(factory): unrecognized activation name "' // activation_name%string() // '"' - end select -#if defined __INTEL_COMPILER || _CRAYFTN -#else - end associate -#endif - end procedure - end submodule training_configuration_s From c574fd030c74af5aa263edc1977f09f67f1fc885 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Tue, 15 Oct 2024 12:59:01 -0700 Subject: [PATCH 097/105] doc(UML): update class diagram --- doc/uml_diagrams/inference_engine.md | 59 +++++++++++++--------------- 1 file changed, 27 insertions(+), 32 deletions(-) diff --git a/doc/uml_diagrams/inference_engine.md b/doc/uml_diagrams/inference_engine.md index 6d5b7bd39..9e4880892 100644 --- a/doc/uml_diagrams/inference_engine.md +++ b/doc/uml_diagrams/inference_engine.md @@ -3,39 +3,38 @@ inference engine ```mermaid classDiagram -class activation_strategy_t - -activation_strategy_t <|-- differentiable_activation_strategy_t - -class expected_outputs_t - -class inference_engine_t - -inference_engine_t o-- string_t - -input_output_pair_t o-- inputs_t -input_output_pair_t o-- expected_outputs_t +class neural_network_t +class trainable_network_t +class tensor_map_t +class activation_t +class metadata_t +class mini_batch_t +class tensor_t + +neural_network_t <|-- trainable_network_t +neural_network_t o-- metadata_t +neural_network_t o-- tensor_map_t +neural_network_t o-- activation_t + + +class neuron_t +class layer_t +class hyperparameters_t +class network_configuration_t +class file_t +class double_precision_file_t + +mini_batch_t o--"1..*" input_output_pair_t +input_output_pair_t o-- "2" tensor_t +file_t <|-- double_precision_file_t layer_t o-- neuron_t layer_t o--"0..*" layer_t - -mini_batch_t o--"0..*" input_output_pair_t - -class netCDF_file_t - neuron_t o--"0..*" neuron_t -class outputs_t - - -differentiable_activation_strategy_t <|--sigmoid_t -<> differentiable_activation_strategy_t -activation_strategy_t <|-- step_t - -differentiable_activation_strategy_t <|-- swish_t - -class trainable_engine_t{ - -metadata_:string +class trainable_network_t{ + -neural_network_t : neural_network_t + -metadata_t : metadata -w : real -b : real -n : integer @@ -45,8 +44,4 @@ class trainable_engine_t{ +infer() +num_layers() +num_inputs() - +to_inference_engine() - differentiable_activation_strategy_t() - } -trainable_engine_t o--"0..*" string_t From 7f606a4562375b8cd0108862d80e2eae9338f332 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Tue, 15 Oct 2024 14:50:43 -0700 Subject: [PATCH 098/105] doc(uml): add components & type-bound procedures --- doc/uml_diagrams/inference_engine.md | 38 ++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/doc/uml_diagrams/inference_engine.md b/doc/uml_diagrams/inference_engine.md index 9e4880892..31f202f7e 100644 --- a/doc/uml_diagrams/inference_engine.md +++ b/doc/uml_diagrams/inference_engine.md @@ -10,12 +10,13 @@ class activation_t class metadata_t class mini_batch_t class tensor_t +class workspace_t neural_network_t <|-- trainable_network_t neural_network_t o-- metadata_t neural_network_t o-- tensor_map_t neural_network_t o-- activation_t - +trainable_network_t o-- workspace_t class neuron_t class layer_t @@ -32,16 +33,33 @@ layer_t o-- neuron_t layer_t o--"0..*" layer_t neuron_t o--"0..*" neuron_t + +class neural_network_t{ + -input_map_ : tensor_map_t + -output_map_ : tensor_map_t + -activation_ : activation_t + -metadata_ : metadata_t + -weights_ : real + -bisases_ : real + -nodes_ : integer ++operator(==) ++infer() ++to_json() ++map_to_input_range() ++map_from_output_range() ++num_hidden_layers() ++num_inputs() ++num_outputs() ++nodes_per_layer() ++assert_conformable_with () ++skip() ++activation_function_name() ++learn() ++assert_consistency() +} class trainable_network_t{ -neural_network_t : neural_network_t - -metadata_t : metadata - -w : real - -b : real - -n : integer - - +assert_consistent() + -workspace_ : workspace_t +train() - +infer() - +num_layers() - +num_inputs() + +map_to_training_ranges() } From 5f163341588c136d2b805913556870f58361e3d3 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Tue, 15 Oct 2024 18:06:34 -0700 Subject: [PATCH 099/105] doc(README/uml): add link to UML diagram --- README.md | 13 +++++++++---- .../inference_engine.md => uml/class-diagram.md} | 0 2 files changed, 9 insertions(+), 4 deletions(-) rename doc/{uml_diagrams/inference_engine.md => uml/class-diagram.md} (100%) diff --git a/README.md b/README.md index a02840c94..a98a7d8fa 100644 --- a/README.md +++ b/README.md @@ -176,15 +176,19 @@ Examples of performing inference using a neural-network JSON file are in [exampl Documentation ------------- -Please see our [GitHub Pages site] for HTML documentation generated by [`ford`] or generate documentation locally by installing `ford` and executing `ford ford.md`. +### HTML +Please see our [GitHub Pages site] for Hypertext Markup Languge (HTML) documentation generated by [`ford`] or generate documentation locally by installing `ford` and executing `ford ford.md`. +### UML +Please see the `doc/uml` subdirectory for Unified Modeling Language (UML) diagrams such as a comprehensive Fiats [class diagram] with human-readable [Mermaid] source that renders graphically when opened by browsing to the document on GitHub. [Building and testing]: #building-and-testing [Caffeine]: https://go.lbl.gov/caffeine +[class diagram]: ./doc/uml/class-diagram.md +[Documentation]: #documentation +[demo]: demo [E3SM]: https://e3sm.org [example]: example -[demo]: demo -[Documentation]: #documentation [example/print-training-configuration.F90]: example/print-training-configuration.F90 [example/concurrent-inferences]: example/concurrent-inferences.f90 [`ford`]: https://github.com/Fortran-FOSS-Programmers/ford @@ -195,8 +199,9 @@ Please see our [GitHub Pages site] for HTML documentation generated by [`ford`] [HDF5]: https://www.hdfgroup.org/solutions/hdf5/ [ICAR]: https://github.com/BerkeleyLab/icar/tree/neural-net [JSON]: https://www.json.org/json-en.html -[NetCDF]: https://www.unidata.ucar.edu/software/netcdf/ [LLVM]: https://github.com/llvm/llvm-project +[Mermaid]: https://mermaid.js.org +[NetCDF]: https://www.unidata.ucar.edu/software/netcdf/ [nexport]: https://go.lbl.gov/nexport [Overview]: #overview [ROCm fork]: https://github.com/ROCm/llvm-project diff --git a/doc/uml_diagrams/inference_engine.md b/doc/uml/class-diagram.md similarity index 100% rename from doc/uml_diagrams/inference_engine.md rename to doc/uml/class-diagram.md From 5292c9ba91e2d53ba6d9505d2d77fa79b2a50e7c Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Tue, 15 Oct 2024 17:47:16 -0700 Subject: [PATCH 100/105] doc(uml): add classes & relationships --- doc/uml/class-diagram.md | 194 +++++++++++++++++++++++++++++---------- 1 file changed, 148 insertions(+), 46 deletions(-) diff --git a/doc/uml/class-diagram.md b/doc/uml/class-diagram.md index 31f202f7e..2402c09ba 100644 --- a/doc/uml/class-diagram.md +++ b/doc/uml/class-diagram.md @@ -1,65 +1,167 @@ -inference engine ---- +Fiats Class Diagram +------------------- ```mermaid classDiagram -class neural_network_t -class trainable_network_t -class tensor_map_t class activation_t +class double_precision_string_t class metadata_t class mini_batch_t +class neural_network_t +class string_t class tensor_t +class tensor_map_t +class trainable_network_t class workspace_t -neural_network_t <|-- trainable_network_t -neural_network_t o-- metadata_t -neural_network_t o-- tensor_map_t -neural_network_t o-- activation_t -trainable_network_t o-- workspace_t +neural_network_t <|-- trainable_network_t +neural_network_t o-- metadata_t +neural_network_t o-- tensor_map_t +neural_network_t o-- activation_t +trainable_network_t o-- workspace_t +string_t <|-- double_precision_string_t -class neuron_t -class layer_t +class double_precision_file_t +class file_t class hyperparameters_t +class layer_t class network_configuration_t -class file_t -class double_precision_file_t - -mini_batch_t o--"1..*" input_output_pair_t -input_output_pair_t o-- "2" tensor_t -file_t <|-- double_precision_file_t +class neuron_t -layer_t o-- neuron_t -layer_t o--"0..*" layer_t -neuron_t o--"0..*" neuron_t +mini_batch_t o--"1..*" input_output_pair_t +input_output_pair_t o-- "2" tensor_t +file_t <|-- double_precision_file_t +layer_t o-- neuron_t +layer_t o--"0..*" layer_t +neuron_t o--"0..*" neuron_t +training_configuration_t o-- hyperparameters_t +training_configuration_t o-- network_configuration_t class neural_network_t{ - -input_map_ : tensor_map_t - -output_map_ : tensor_map_t - -activation_ : activation_t - -metadata_ : metadata_t - -weights_ : real - -bisases_ : real - -nodes_ : integer -+operator(==) -+infer() -+to_json() -+map_to_input_range() -+map_from_output_range() -+num_hidden_layers() -+num_inputs() -+num_outputs() -+nodes_per_layer() -+assert_conformable_with () -+skip() -+activation_function_name() -+learn() -+assert_consistency() + - input_map_ : tensor_map_t + - output_map_ : tensor_map_t + - activation_ : activation_t + - metadata_ : metadata_t + - weights_ : real + - bisases_ : real + - nodes_ : integer + + operator(==) logical + + infer(tensor_t) tensor_t + + to_json() file_t + + map_to_input_range(tensor_t) tensor_t + + map_from_output_range(tensor_t) tensor_t + + num_hidden_layers() integer + + num_inputs() integer + + num_outputs() integer + + nodes_per_layer() integer + + assert_conformable_with() + + skip() logical + + activation_function_name() string_t + + learn(mini_batch_t, real, logical, real, workspace_t) + + assert_consistency() } class trainable_network_t{ - -neural_network_t : neural_network_t - -workspace_ : workspace_t - +train() - +map_to_training_ranges() + - workspace_ : workspace_t + + train(mini_batch_t, real, logical, real) + + map_to_training_ranges() : input_output_pair_t +} +class tensor_map_t{ + - layer_ : character + - intercept_ : real + - slope_ : real + + to_json() : file_t + + operator(==) logical +} +class tensor_t{ + - values_ : real + + values() real + - num_components() integer +} +class metadata_t{ + - modelName_ : string_t + - modelAuthor_ : string_t + - compilationDate_ : string_t + - activationFunction_: string_t + - usingSkipConnections_ : string_t + + strings() string_t + + to_json() file_t + + activation_name() string_t + + operator(==) logical +} +class mini_batch_t{ + - input_output_pairs_ : input_output_pair_t + + input_output_pairs() input_output_pair_t +} +class activation_t{ + - selection_ : integer + + function_name() string_t + + evaluate() real + + differentiate() real +} +class input_output_pair_t{ + - inputs_ : tensor_t + - expected_outputs_ : tensor_t + + inputs() tensor_t + + expected_outputs() tensor_t + + shuffle() + + write_to_stdout() +} +class file_t{ + - lines_ : string_t + + lines() : string_t + + write_lines(string_t) +} +class double_precision_file_t{ + + double_precision_lines() double_precision_string_t +} +class hyperparameters_t{ + - mini_batches_ : integer + - learning_rate_ : real + - optimizer_ : character + + to_json() file_t + + mini_batches() : integer + + learning_rate() : real + + optimizer_name() : character +} +class network_configuration_t{ + - skip_connections_ : logical + - nodes_per_layer_: integer + - activation_name_ : character + + to_json() file_t + + operator(==) logical + + activation_name() string_t + + nodes_per_layer() integer + + skip_connections() logical +} +class layer_t{ + - neuron_ : neuron_t + - next_ : layer_t + + neural_network() : neural_network_t + + count_layers() integer + + count_neurons() integer + + count_inputs() integer + + neurons_per_layer() integer + + next_allocated() logical + + next_pointer() layer_t +} +class neuron_t{ + - weights_ : real + - bias_ : next + + to_json() file_t + + weights() real + + bias() real + + next_allocated() logical + + next_pointer() neuron_t + + num_inputs() integer +} +class training_configuration_t{ + - hyperparameters_ : hyperparameters_t + - network_configuration : nework_configuration_t + + operator(==) logical + + to_json() file_t + + mini_batches() integer + + optimizer_name() string_t + + nodes_per_layer() integer + + skip_connections() logical } From 48f22680918777a922cb1f9f50739efc61b415cd Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Tue, 15 Oct 2024 20:03:20 -0700 Subject: [PATCH 101/105] Revert "chore(training_configuration): rm unused function" This reverts commit 31ecd597c14e0a14084ee02f028f07884f6e3a84. --- src/fiats/training_configuration_m.f90 | 14 ++++++++ src/fiats/training_configuration_s.F90 | 50 ++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/src/fiats/training_configuration_m.f90 b/src/fiats/training_configuration_m.f90 index be693220d..eb20a30f0 100644 --- a/src/fiats/training_configuration_m.f90 +++ b/src/fiats/training_configuration_m.f90 @@ -28,6 +28,8 @@ module training_configuration_m procedure, private :: default_real_optimizer_name, double_precision_optimizer_name generic :: learning_rate => default_real_learning_rate, double_precision_learning_rate procedure, private :: default_real_learning_rate, double_precision_learning_rate + generic :: differentiable_activation => default_real_differentiable_activation, double_precision_differentiable_activation + procedure, private :: default_real_differentiable_activation, double_precision_differentiable_activation generic :: nodes_per_layer => default_real_nodes_per_layer, double_precision_nodes_per_layer procedure, private :: default_real_nodes_per_layer, double_precision_nodes_per_layer generic :: skip_connections => default_real_skip_connections, double_precision_skip_connections @@ -126,6 +128,18 @@ elemental module function double_precision_learning_rate(self) result(rate) double precision rate end function + module function default_real_differentiable_activation(self) result(activation) + implicit none + class(training_configuration_t), intent(in) :: self + type(activation_t) activation + end function + + module function double_precision_differentiable_activation(self) result(activation) + implicit none + class(training_configuration_t(double_precision)), intent(in) :: self + type(activation_t) activation + end function + pure module function default_real_nodes_per_layer(self) result(nodes) implicit none class(training_configuration_t), intent(in) :: self diff --git a/src/fiats/training_configuration_s.F90 b/src/fiats/training_configuration_s.F90 index bdb57932e..3b982a202 100644 --- a/src/fiats/training_configuration_s.F90 +++ b/src/fiats/training_configuration_s.F90 @@ -181,4 +181,54 @@ using_skip = self%network_configuration_%skip_connections() end procedure + module procedure default_real_differentiable_activation +#if defined __INTEL_COMPILER || _CRAYFTN + type(string_t) :: activation_name + activation_name = self%network_configuration_%activation_name() +#else + associate(activation_name => self%network_configuration_%activation_name()) +#endif + select case(activation_name%string()) + case ("gelu") + activation = activation_t(gelu) + case ("relu") + activation = activation_t(relu) + case ("sigmoid") + activation = activation_t(sigmoid) + case ("swish") + activation = activation_t(swish) + case default + error stop 'activation_factory_s(factory): unrecognized activation name "' // activation_name%string() // '"' + end select +#if defined __INTEL_COMPILER || _CRAYFTN +#else + end associate +#endif + end procedure + + module procedure double_precision_differentiable_activation +#if defined __INTEL_COMPILER || _CRAYFTN + type(string_t) :: activation_name + activation_name = self%network_configuration_%activation_name() +#else + associate(activation_name => self%network_configuration_%activation_name()) +#endif + select case(activation_name%string()) + case ("gelu") + activation = activation_t(gelu) + case ("relu") + activation = activation_t(relu) + case ("sigmoid") + activation = activation_t(sigmoid) + case ("swish") + activation = activation_t(swish) + case default + error stop 'activation_factory_s(factory): unrecognized activation name "' // activation_name%string() // '"' + end select +#if defined __INTEL_COMPILER || _CRAYFTN +#else + end associate +#endif + end procedure + end submodule training_configuration_s From 289dc4a86f201538f265b54cae41a440f122dbc6 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Wed, 16 Oct 2024 15:40:20 -0700 Subject: [PATCH 102/105] doc(README): edit compiler status --- README.md | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index a98a7d8fa..a644424ff 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,8 @@ We recommend using any compiler's latest release or even building open-source co The [handy-dandy] repository contains scripts capturing steps for building the [LLVM] compiler suite. The remainder of this section contains commands for building Fiats with a recent Fortran compiler and the Fortran Package Manager ([`fpm`]). -#### LLVM (`flang-new`) +#### Supported Compilers +##### LLVM (`flang-new`) With LLVM `flang` 20 installed in your `PATH`, build and test Fiats with the installed `flang-new` symlink in order for `fpm` to correctly identify the compiler: ``` fpm test --compiler flang-new --flag "-O3" @@ -72,7 +73,7 @@ With LLVM `flang` 19, enable the compiler's experimental support for assumed-ran fpm test --compiler flang-new --flag "-mmlir -allow-assumed-rank -O3" ``` -##### _Experimental:_ Automatic parallelization of `do concurrent` on CPUs +###### _Experimental:_ Automatic parallelization of `do concurrent` on CPUs With the `amd_trunk_dev` branch of the [ROCm fork] fork of LLVM, automatically parallelize inference calculations inside `do concurrent` constructs: ``` fpm run \ @@ -86,19 +87,24 @@ where `model.json` must be a neural network in the [JSON] format used by Fiats a Automatic parallelization for training neural networks is under development. -#### NAG (`nagfor`) +#### Partially Supported Compilers + +Fiats release 0.14.0 and earlier support the use of the NAG, GNU, and Intel Fortran compilers. +We are corresponding with these compilers' developers about addressing the compiler issues preventing building newer Fiats releases. + +##### NAG (`nagfor`) ``` fpm test --compiler nagfor --flag -fpp --profile release ``` -#### GNU (`gfortran`) +##### GNU (`gfortran`) Compiler bugs related to parameterized derived types currently prevent `gfortran` from building Fiats versions 0.15.0 or later. Test and build earlier versions of Fiats build with the following command: ``` fpm test --compiler gfortran --profile release ``` -#### Intel (`ifx`) +##### Intel (`ifx`) Compiler bugs related to parameterized derived types currently prevent `gfortran` from building Fiats versions 0.15.0 or later. Test and build earlier versions of Fiats build with the following command: ``` @@ -111,11 +117,12 @@ This capability is under development with the goal to facilitate automatic GPU o fpm test --compiler ifx --profile release --flag "-fopenmp-target-do-concurrent -qopenmp -fopenmp-targets=spir64 -O3" ``` -#### HPE (`crayftn.sh`) -Support for the Cray Compiler Environment (CCE) Fortran compiler is under development. -Building with the CCE `ftn` compiler wrapper requires an additional trivial wrapper -shell script. For example, create a file `crayftn.sh` with the following contents and -place this file's location in your `PATH`: +#### Under Development +We are corresponding with the developers of the compiler(s) below developers about addressing the compiler issues preventing building newer Fiats releases. + +#### HPE Cray Compiler Environment (CCE) (`crayftn.sh`) +Building with the CCE `ftn` compiler wrapper requires an additional trivial wrapper. +For example, create a file `crayftn.sh` with the following contents and place this file's location in your `PATH`: ``` #!/bin/bash From dcc407ef5e489458efd0d78a586bed8e6c9e9aaa Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Wed, 16 Oct 2024 15:53:25 -0700 Subject: [PATCH 103/105] doc(README): fix typo in branch name Co-authored-by: Katherine Rasmussen --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a644424ff..16625640a 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ fpm test --compiler flang-new --flag "-mmlir -allow-assumed-rank -O3" ``` ###### _Experimental:_ Automatic parallelization of `do concurrent` on CPUs -With the `amd_trunk_dev` branch of the [ROCm fork] fork of LLVM, automatically parallelize inference calculations inside `do concurrent` constructs: +With the `amd-trunk-dev` branch of the [ROCm fork] of LLVM, automatically parallelize inference calculations inside `do concurrent` constructs: ``` fpm run \ --example concurrent-inferences \ From 55c377b1bc2756b231d8a0ab223b6166fef20ef3 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Wed, 16 Oct 2024 16:55:45 -0700 Subject: [PATCH 104/105] doc(README): specify compiler issue --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 16625640a..c620ebd1d 100644 --- a/README.md +++ b/README.md @@ -105,7 +105,7 @@ fpm test --compiler gfortran --profile release ``` ##### Intel (`ifx`) -Compiler bugs related to parameterized derived types currently prevent `gfortran` from building Fiats versions 0.15.0 or later. +Compiler bugs related to generic name resolution currently prevent `ifx` from building Fiats versions 0.15.0 or later. Test and build earlier versions of Fiats build with the following command: ``` fpm test --compiler ifx --profile release --flag -O3 From 53c8550f2ec66cc81b6911152d25acce77a273f5 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Wed, 16 Oct 2024 16:58:03 -0700 Subject: [PATCH 105/105] doc(README): fix CCE compiler status text Co-authored-by: Katherine Rasmussen --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c620ebd1d..2e07f20bf 100644 --- a/README.md +++ b/README.md @@ -118,7 +118,7 @@ fpm test --compiler ifx --profile release --flag "-fopenmp-target-do-concurrent ``` #### Under Development -We are corresponding with the developers of the compiler(s) below developers about addressing the compiler issues preventing building newer Fiats releases. +We are corresponding with the developers of the compiler(s) below about addressing the compiler issues preventing building Fiats. #### HPE Cray Compiler Environment (CCE) (`crayftn.sh`) Building with the CCE `ftn` compiler wrapper requires an additional trivial wrapper.