From 02e450a7ed8d70c38d5f5c0f430515a42b348ced Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Sun, 22 Oct 2023 21:55:22 -0700 Subject: [PATCH 01/11] doc(test): fix netCDF_file test comment --- test/netCDF_file_test_m.f90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/netCDF_file_test_m.f90 b/test/netCDF_file_test_m.f90 index 56035d396..f01c1b75c 100644 --- a/test/netCDF_file_test_m.f90 +++ b/test/netCDF_file_test_m.f90 @@ -4,7 +4,7 @@ !! Due to a suspected bug in the Intel ifx compiler, the above C preprocessor macro !! effectively eliminates this file's source code when building with an Intel compiler. module NetCDF_file_test_m - !! Define asymmetric tests and procedures required for reporting results + !! Define asymmetric tests for the NetCDF file interface ! External dependencies use assert_m, only : assert From b024b703b41c649858888e2493f65c5aaae8c6ac Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Wed, 25 Oct 2023 12:08:01 -0700 Subject: [PATCH 02/11] feat(hyperparameters): def derived type + json I/O --- src/inference_engine/hyperparameters_m.f90 | 49 +++++++++++++++++++ src/inference_engine/hyperparameters_s.f90 | 54 ++++++++++++++++++++ test/hyperparameters_test_m.f90 | 57 ++++++++++++++++++++++ test/main.f90 | 3 ++ training_parameters.json | 9 ++++ 5 files changed, 172 insertions(+) create mode 100644 src/inference_engine/hyperparameters_m.f90 create mode 100644 src/inference_engine/hyperparameters_s.f90 create mode 100644 test/hyperparameters_test_m.f90 create mode 100644 training_parameters.json diff --git a/src/inference_engine/hyperparameters_m.f90 b/src/inference_engine/hyperparameters_m.f90 new file mode 100644 index 000000000..f0e125ef7 --- /dev/null +++ b/src/inference_engine/hyperparameters_m.f90 @@ -0,0 +1,49 @@ +module hyperparameters_m + use sourcery_m, only : file_t + implicit none + + private + public :: hyperparameters_t + public :: initialization_parameters_t + public :: initialization_t + + type initialization_parameters_t + real spread_ + end type + + type initialization_t + character(len=:), allocatable :: initialization_type_ + type(initialization_parameters_t) :: initialization_parameters_ + end type + + type hyperparameters_t + private + character(len=:), allocatable :: activation_ + integer mini_batch_size_ + integer, allocatable :: nodes_per_layer_(:) + type(initialization_t) initialization_ + contains + procedure :: to_json + end type + + interface hyperparameters_t + + pure module function construct_from_json_file(file_) result(hyperparameters) + implicit none + type(file_t), intent(in) :: file_ + type(hyperparameters_t) hyperparameters + end function + + end interface + + interface + + impure elemental module function to_json(self) result(json_file) + implicit none + class(hyperparameters_t), intent(in) :: self + type(file_t) json_file + end function + + end interface + +end module diff --git a/src/inference_engine/hyperparameters_s.f90 b/src/inference_engine/hyperparameters_s.f90 new file mode 100644 index 000000000..29007b92c --- /dev/null +++ b/src/inference_engine/hyperparameters_s.f90 @@ -0,0 +1,54 @@ +submodule(hyperparameters_m) hyperparameters_s + use assert_m, only : assert, intrinsic_array_t + use sourcery_m, only : string_t + implicit none + +contains + + module procedure construct_from_json_file + integer l + type(string_t), allocatable :: lines(:) + + lines = file_%lines() + + l = 1 + call assert(adjustl(lines(l)%string())=="{", 'construct_from_json_file: adjustl(lines(l)%string())=="{"', lines(l)%string()) + + !{ + ! "activation" : "sigmoid", + ! "num_mini_batches" : 10, + ! "nodes per layer" : [2, 72, 2], + ! "initialization" : { + ! "type" : "perturbed identity", + ! "parameters" : [ { "spread" : 0.05 } ] + ! } + !} + + + l = l + 1 + call assert(adjustl(lines(l)%string())=="}", 'construct_from_json_file: adjustl(lines(l)%string())=="}"', lines(l)%string()) + end procedure + + module procedure to_json + type(string_t), allocatable :: lines(:) + integer, parameter :: outer_object_braces = 2 + integer, parameter :: num_lines = outer_object_braces + integer l + + allocate(lines(num_lines)) + + l = 1 + lines(l) = string_t('{') + + l = l + 1 + !lines(line) = string_t(' "modelName": "' // & + !self%metadata_(findloc(key, "modelName", dim=1))%string() // '",') + + + + l = l + 1 + call assert(l == num_lines, "hyperparameters_s(to_json): l == num_lines", intrinsic_array_t([l,num_lines])) + lines(l) = string_t('}') + end procedure + +end submodule hyperparameters_s diff --git a/test/hyperparameters_test_m.f90 b/test/hyperparameters_test_m.f90 new file mode 100644 index 000000000..ee7440741 --- /dev/null +++ b/test/hyperparameters_test_m.f90 @@ -0,0 +1,57 @@ +! Copyright (c), The Regents of the University of California +! Terms of use are as specified in LICENSE.txt +module hyperparameters_test_m + !! Test hyperparameters_t object I/O and construction + + ! External dependencies + use assert_m, only : assert + use sourcery_m, only : string_t, test_t, test_result_t + + ! Internal dependencies + use hyperparameters_m, only : hyperparameters_t + + implicit none + + private + public :: hyperparameters_test_t + + type, extends(test_t) :: hyperparameters_test_t + contains + procedure, nopass :: subject + procedure, nopass :: results + end type + +contains + + pure function subject() result(specimen) + character(len=:), allocatable :: specimen + specimen = "A hyperparameters_t object" + end function + + function results() result(test_results) + type(test_result_t), allocatable :: test_results(:) + + character(len=*), parameter :: longest_description = & + "writing and then reading gives input matching output for perturbed identity network" + + associate( & + descriptions => & + [ character(len=len(longest_description)) :: & + "writing and then reading gives input matching output for perturbed identity network" & + ], & + outcomes => & + [ write_then_read_perturbed_identity() & + ] & + ) + call assert(size(descriptions) == size(outcomes),"hyperparameters_test_m(results): size(descriptions) == size(outcomes)") + test_results = test_result_t(descriptions, outcomes) + end associate + + end function + + function write_then_read_perturbed_identity() result(test_passes) + logical, allocatable :: test_passes(:) + test_passes = [.true.] + end function + +end module hyperparameters_test_m \ No newline at end of file diff --git a/test/main.f90 b/test/main.f90 index af501fd26..0867a6b84 100644 --- a/test/main.f90 +++ b/test/main.f90 @@ -4,11 +4,13 @@ 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 hyperparameters_test_m, only : hyperparameters_test_t implicit none 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(hyperparameters_test_t) hyperparameters_test real t_start, t_finish integer :: passes=0, tests=0 @@ -18,6 +20,7 @@ program main call inference_engine_test%report(passes, tests) call asymmetric_engine_test%report(passes, tests) call trainable_engine_test%report(passes, tests) + call hyperparameters_test%report(passes, tests) #ifndef __INTEL_FORTRAN block use netCDF_file_test_m, only : netCDF_file_test_t diff --git a/training_parameters.json b/training_parameters.json new file mode 100644 index 000000000..37b5701f6 --- /dev/null +++ b/training_parameters.json @@ -0,0 +1,9 @@ +{ + "activation" : "sigmoid", + "num_mini_batches" : 10, + "nodes per layer" : [2, 72, 2], + "initialization" : { + "type" : "perturbed identity", + "parameters" : [ { "spread" : 0.05 } ] + } +} From fd2cc996fe0f99cd1471b95ce4f75509371e241c Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Sun, 29 Oct 2023 20:13:09 -0700 Subject: [PATCH 03/11] refac: update sourcery depency version to 4.4.0 --- fpm.toml | 2 +- .../activation_strategy_m.f90 | 2 +- src/inference_engine/hyperparameters_m.f90 | 26 ++----- src/inference_engine/hyperparameters_s.f90 | 76 +++++++++---------- src/inference_engine/inference_engine_m_.f90 | 3 +- src/inference_engine/inference_engine_s.f90 | 6 +- .../network_configuration_m.f90 | 39 ++++++++++ .../network_configuration_s.f90 | 54 +++++++++++++ src/inference_engine/neuron_m.f90 | 2 +- src/inference_engine/neuron_s.f90 | 8 +- src/inference_engine/relu_m.f90 | 2 +- src/inference_engine/sigmoid_m.f90 | 2 +- src/inference_engine/step_m.f90 | 2 +- src/inference_engine/swish_m.f90 | 2 +- test/asymmetric_engine_test_m.f90 | 4 +- test/inference_engine_test_m.f90 | 5 +- test/netCDF_file_test_m.f90 | 4 +- training_parameters.json | 9 --- 18 files changed, 153 insertions(+), 95 deletions(-) create mode 100644 src/inference_engine/network_configuration_m.f90 create mode 100644 src/inference_engine/network_configuration_s.f90 delete mode 100644 training_parameters.json diff --git a/fpm.toml b/fpm.toml index 3042622a5..bdc742364 100644 --- a/fpm.toml +++ b/fpm.toml @@ -6,5 +6,5 @@ maintainer = "rouson@lbl.gov" [dependencies] assert = {git = "https://github.com/sourceryinstitute/assert", tag = "1.5.0"} -sourcery = {git = "https://github.com/sourceryinstitute/sourcery", tag = "3.9.1"} +sourcery = {git = "https://github.com/sourceryinstitute/sourcery", tag = "4.4.0"} netcdf-interfaces = {git = "https://github.com/rouson/netcdf-interfaces.git", branch = "implicit-interfaces"} diff --git a/src/inference_engine/activation_strategy_m.f90 b/src/inference_engine/activation_strategy_m.f90 index cdb464852..dd888ee7c 100644 --- a/src/inference_engine/activation_strategy_m.f90 +++ b/src/inference_engine/activation_strategy_m.f90 @@ -4,7 +4,7 @@ module activation_strategy_m ! External dependencies use kind_parameters_m, only : rkind - use string_m, only : string_t + use sourcery_m, only : string_t implicit none private diff --git a/src/inference_engine/hyperparameters_m.f90 b/src/inference_engine/hyperparameters_m.f90 index f0e125ef7..dea9e6580 100644 --- a/src/inference_engine/hyperparameters_m.f90 +++ b/src/inference_engine/hyperparameters_m.f90 @@ -1,34 +1,22 @@ module hyperparameters_m - use sourcery_m, only : file_t + use sourcery_m, only : string_t, file_t implicit none private public :: hyperparameters_t - public :: initialization_parameters_t - public :: initialization_t - type initialization_parameters_t - real spread_ - end type - - type initialization_t - character(len=:), allocatable :: initialization_type_ - type(initialization_parameters_t) :: initialization_parameters_ - end type - type hyperparameters_t private - character(len=:), allocatable :: activation_ - integer mini_batch_size_ - integer, allocatable :: nodes_per_layer_(:) - type(initialization_t) initialization_ + integer :: mini_batches_ = 10 + real :: learning_rate_ = 1.5 + character(len=:), allocatable :: optimizer_ contains procedure :: to_json end type interface hyperparameters_t - pure module function construct_from_json_file(file_) result(hyperparameters) + pure module function from_json(file_) result(hyperparameters) implicit none type(file_t), intent(in) :: file_ type(hyperparameters_t) hyperparameters @@ -38,10 +26,10 @@ pure module function construct_from_json_file(file_) result(hyperparameters) interface - impure elemental module function to_json(self) result(json_file) + pure module function to_json(self) result(lines) implicit none class(hyperparameters_t), intent(in) :: self - type(file_t) json_file + type(string_t), allocatable :: lines(:) end function end interface diff --git a/src/inference_engine/hyperparameters_s.f90 b/src/inference_engine/hyperparameters_s.f90 index 29007b92c..c1f9aae94 100644 --- a/src/inference_engine/hyperparameters_s.f90 +++ b/src/inference_engine/hyperparameters_s.f90 @@ -1,54 +1,50 @@ submodule(hyperparameters_m) hyperparameters_s - use assert_m, only : assert, intrinsic_array_t - use sourcery_m, only : string_t + use assert_m, only : assert implicit none + character(len=*), parameter :: mini_batches_key = "mini-batches" + character(len=*), parameter :: learning_rate_key = "learning rate" + character(len=*), parameter :: optimizer_key = "optimizer" + contains - module procedure construct_from_json_file - integer l + module procedure from_json type(string_t), allocatable :: lines(:) + integer l + logical hyperparameters_key_found lines = file_%lines() - - l = 1 - call assert(adjustl(lines(l)%string())=="{", 'construct_from_json_file: adjustl(lines(l)%string())=="{"', lines(l)%string()) - - !{ - ! "activation" : "sigmoid", - ! "num_mini_batches" : 10, - ! "nodes per layer" : [2, 72, 2], - ! "initialization" : { - ! "type" : "perturbed identity", - ! "parameters" : [ { "spread" : 0.05 } ] - ! } - !} - - - l = l + 1 - call assert(adjustl(lines(l)%string())=="}", 'construct_from_json_file: adjustl(lines(l)%string())=="}"', lines(l)%string()) + hyperparameters_key_found = .false. + + loop_through_file: & + 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.) + hyperparameters%optimizer_ = lines(l+3)%get_json_value(string_t(optimizer_key), mold=string_t("")) + return + end if + end do loop_through_file + + call assert(hyperparameters_key_found, "hyperparameters_s(from_json): hyperparameters_found") end procedure module procedure to_json - type(string_t), allocatable :: lines(:) - integer, parameter :: outer_object_braces = 2 - integer, parameter :: num_lines = outer_object_braces - integer l - - allocate(lines(num_lines)) - - l = 1 - lines(l) = string_t('{') - - l = l + 1 - !lines(line) = string_t(' "modelName": "' // & - !self%metadata_(findloc(key, "modelName", dim=1))%string() // '",') - - - - l = l + 1 - call assert(l == num_lines, "hyperparameters_s(to_json): l == num_lines", intrinsic_array_t([l,num_lines])) - lines(l) = string_t('}') + character(len=*), parameter :: indent = repeat(" ",ncopies=4) + integer, parameter :: max_digits = 12 + character(len=max_digits) 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 // '": ' // mini_batches_string ), & + string_t(indent // indent // '"' // learning_rate_key // '": ' // learning_rate_string ), & + string_t(indent // indent // '"' // optimizer_key // '": "' // self%optimizer_ // '"'), & + string_t(indent // '}') & + ] end procedure end submodule hyperparameters_s diff --git a/src/inference_engine/inference_engine_m_.f90 b/src/inference_engine/inference_engine_m_.f90 index cbab99256..358d59a93 100644 --- a/src/inference_engine/inference_engine_m_.f90 +++ b/src/inference_engine/inference_engine_m_.f90 @@ -2,9 +2,8 @@ ! 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 string_m, only : string_t use activation_strategy_m, only : activation_strategy_t - use file_m, only : file_t + use sourcery_m, only : file_t, string_t use kind_parameters_m, only : rkind use tensor_m, only : tensor_t use differentiable_activation_strategy_m, only :differentiable_activation_strategy_t diff --git a/src/inference_engine/inference_engine_s.f90 b/src/inference_engine/inference_engine_s.f90 index 559ef29ab..9e03b08df 100644 --- a/src/inference_engine/inference_engine_s.f90 +++ b/src/inference_engine/inference_engine_s.f90 @@ -1,16 +1,14 @@ ! 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 - use assert_m, only : assert - use intrinsic_array_m, only : intrinsic_array_t + 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 relu_m, only : relu_t use layer_m, only : layer_t use neuron_m, only : neuron_t - use file_m, only : file_t - use formats_m, only : separated_values + use sourcery_m, only : separated_values implicit none interface assert_consistency diff --git a/src/inference_engine/network_configuration_m.f90 b/src/inference_engine/network_configuration_m.f90 new file mode 100644 index 000000000..6f057e7a6 --- /dev/null +++ b/src/inference_engine/network_configuration_m.f90 @@ -0,0 +1,39 @@ +module network_configuration_m + use sourcery_m, only : file_t + implicit none + + private + public :: network_configuration_t + + type network_configuration_t + private + type(string_t) activation_function_ + integer, allocatable :: nodes_per_layer_(:) + logical skip_connections_ + contains + procedure :: to_json + end type + + end type + + interface network_configuration_t + + elemental module function from_json(file_) result(network_configuration) + implicit none + type(file_t), intent(in) :: file_ + type(network_configuration_t) network_configuration + end function + + end interface + + interface + + elemental module function to_json(self) result(json_file) + implicit none + class(network_configuration_t), intent(in) :: self + type(file_t) json_file + end function + + end interface + +end module diff --git a/src/inference_engine/network_configuration_s.f90 b/src/inference_engine/network_configuration_s.f90 new file mode 100644 index 000000000..43249fcc4 --- /dev/null +++ b/src/inference_engine/network_configuration_s.f90 @@ -0,0 +1,54 @@ +submodule(network_configuration_m) network_configuration_s + use assert_m, only : assert + use sourcery_m, only : string_t + implicit none + + character(len=*), parameter :: activation_function_key = "activation function" + character(len=*), parameter :: nodes_per_layer_key = "nodes per layer" + character(len=*), parameter :: skip_connections_key = "skip connections" + +contains + + module procedure from_json + type(string_t), allocatable :: lines(:) + integer l + logical network configuration_key_found + + lines = file_%lines() + network configuration_key_found = .false. + + loop_through_file: & + do l=1,size(lines) + if (line(l)%get_key() == "network configuration") then + network configuration_key_found = .true. + self%activation_function_ = line(l+1)%get_json_value(activation_function_key, mold=string("")) + self%nodes_per_layer_ = line(l+2)%get_json_value(nodes_per_layer_key , mold=[integer::]) + self%skip_connections_ = line(l+2)%get_json_value(skip_connetions_key , mold=.true.) + return + end if + end do loop_through_file + + call assert(network configuration_found, "network configuration_s(from_json): network configuration_found") + end procedure + + module procedure to_json + character(len=:), parameter :: indent = repeat(" ",ncopies=4) + integer, parameter :: max_digits = 12, max_length=5 + character(len=max_digits) activation_function_string, nodes_per_layer_string, skip_connections_string + character(len=max_length) skip_connections_string + + + write(activation_function_string,*) self%activation_function_ + write(nodes_per_layer_string ,*) self%nodes_per_layer_ + write(skip_connections_string ,*) merge("true ","false", self%skip_connections_) + + lines = [ & + string_t(indent // '"network configuration": {'), & + string_t(indent // indent // '"' // activation_function_key //'": ' // activation_function_string ), & + string_t(indent // indent // '"' // nodes_per_layer_key //'": ' // nodes_per_layer_string ), & + string_t(indent // indent // '"' // skip_connections_key //'": "' // skip_connections_string // '"'), & + string_t(indent // '}') & + ] + end procedure + +end submodule network_configuration_s diff --git a/src/inference_engine/neuron_m.f90 b/src/inference_engine/neuron_m.f90 index 03bf2a54a..dca1c45e5 100644 --- a/src/inference_engine/neuron_m.f90 +++ b/src/inference_engine/neuron_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 neuron_m - use string_m, only : string_t + use sourcery_m, only : string_t use kind_parameters_m, only : rkind implicit none diff --git a/src/inference_engine/neuron_s.f90 b/src/inference_engine/neuron_s.f90 index 2b25bd7fc..115675af3 100644 --- a/src/inference_engine/neuron_s.f90 +++ b/src/inference_engine/neuron_s.f90 @@ -11,11 +11,11 @@ character(len=:), allocatable :: line integer i - call assert(adjustl(neuron_lines(start)%string())=='{', "read_json: neuron object start", neuron_lines(start)%string()) + call assert(adjustl(neuron_lines(start)%string())=='{', "neuron_s(construct): neuron object start",neuron_lines(start)%string()) line = neuron_lines(start+1)%string() associate(colon => index(line, ":")) - call assert(adjustl(line(:colon-1))=='"weights"', "read_json: neuron weights", line) + call assert(adjustl(line(:colon-1))=='"weights"', "neuron_s(construct): 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)])) @@ -30,12 +30,12 @@ line = neuron_lines(start+2)%string() associate(colon => index(line, ":")) - call assert(adjustl(line(:colon-1))=='"bias"', "read_json: neuron bias", line) + call assert(adjustl(line(:colon-1))=='"bias"', "neuron_s(construct): neuron bias", line) read(line(colon+1:), fmt=*) neuron%bias_ end associate line = adjustl(neuron_lines(start+3)%string()) - call assert(line(1:1)=='}', "read_json: neuron object end", line) + call assert(line(1:1)=='}', "neuron_s(construct): neuron object end", line) line = adjustr(neuron_lines(start+3)%string()) if (line(len(line):len(line)) == ",") neuron%next = construct(neuron_lines, start+4) diff --git a/src/inference_engine/relu_m.f90 b/src/inference_engine/relu_m.f90 index 2108e7a78..0fae43e22 100644 --- a/src/inference_engine/relu_m.f90 +++ b/src/inference_engine/relu_m.f90 @@ -3,7 +3,7 @@ module relu_m use differentiable_activation_strategy_m, only : differentiable_activation_strategy_t use kind_parameters_m, only : rkind - use string_m, only : string_t + use sourcery_m, only : string_t implicit none private diff --git a/src/inference_engine/sigmoid_m.f90 b/src/inference_engine/sigmoid_m.f90 index d66324560..3f27b3c81 100644 --- a/src/inference_engine/sigmoid_m.f90 +++ b/src/inference_engine/sigmoid_m.f90 @@ -3,7 +3,7 @@ module sigmoid_m use differentiable_activation_strategy_m, only : differentiable_activation_strategy_t use kind_parameters_m, only : rkind - use string_m, only : string_t + use sourcery_m, only : string_t implicit none private diff --git a/src/inference_engine/step_m.f90 b/src/inference_engine/step_m.f90 index e3661770c..297a99473 100644 --- a/src/inference_engine/step_m.f90 +++ b/src/inference_engine/step_m.f90 @@ -3,7 +3,7 @@ module step_m use activation_strategy_m, only : activation_strategy_t use kind_parameters_m, only : rkind - use string_m, only : string_t + use sourcery_m, only : string_t implicit none private diff --git a/src/inference_engine/swish_m.f90 b/src/inference_engine/swish_m.f90 index d0c24c64e..64edac8dd 100644 --- a/src/inference_engine/swish_m.f90 +++ b/src/inference_engine/swish_m.f90 @@ -3,7 +3,7 @@ module swish_m use differentiable_activation_strategy_m, only : differentiable_activation_strategy_t use kind_parameters_m, only : rkind - use string_m, only : string_t + use sourcery_m, only : string_t implicit none private diff --git a/test/asymmetric_engine_test_m.f90 b/test/asymmetric_engine_test_m.f90 index 9b1f8e9bb..932f07b9b 100644 --- a/test/asymmetric_engine_test_m.f90 +++ b/test/asymmetric_engine_test_m.f90 @@ -5,9 +5,7 @@ module asymmetric_engine_test_m ! External dependencies use assert_m, only : assert - use string_m, only : string_t - use test_m, only : test_t - use test_result_m, only : test_result_t + use sourcery_m, only : string_t, test_t, test_result_t ! Internal dependencies use inference_engine_m, only : inference_engine_t, tensor_t diff --git a/test/inference_engine_test_m.f90 b/test/inference_engine_test_m.f90 index 7f66aa333..44f40a25b 100644 --- a/test/inference_engine_test_m.f90 +++ b/test/inference_engine_test_m.f90 @@ -6,10 +6,7 @@ module inference_engine_test_m ! External dependencies use assert_m, only : assert use kind_parameters_m, only : rkind - use string_m, only : string_t - use test_m, only : test_t - use test_result_m, only : test_result_t - use file_m, only : file_t + use sourcery_m, only : string_t, test_t, test_result_t, file_t ! Internal dependencies use inference_engine_m, only : inference_engine_t, tensor_t, difference_t diff --git a/test/netCDF_file_test_m.f90 b/test/netCDF_file_test_m.f90 index f01c1b75c..6cbfc84c8 100644 --- a/test/netCDF_file_test_m.f90 +++ b/test/netCDF_file_test_m.f90 @@ -8,9 +8,7 @@ module NetCDF_file_test_m ! External dependencies use assert_m, only : assert - use string_m, only : string_t - use test_m, only : test_t - use test_result_m, only : test_result_t + use sourcery_m, only : string_t, test_t, test_result_t use netcdf, only : & nf90_create, nf90_def_dim, nf90_def_var, nf90_enddef, nf90_put_var, nf90_inquire_dimension, & ! functions nf90_close, nf90_open, nf90_inq_varid, nf90_get_var, nf90_inquire_variable, & diff --git a/training_parameters.json b/training_parameters.json deleted file mode 100644 index 37b5701f6..000000000 --- a/training_parameters.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "activation" : "sigmoid", - "num_mini_batches" : 10, - "nodes per layer" : [2, 72, 2], - "initialization" : { - "type" : "perturbed identity", - "parameters" : [ { "spread" : 0.05 } ] - } -} From 0c95d20f4525a73f487f7eae7fc5a6012b9fafc4 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Tue, 31 Oct 2023 07:58:16 -0700 Subject: [PATCH 04/11] test(hyperparameters): add unit test & finish type --- src/inference_engine/hyperparameters_m.f90 | 20 ++++++- src/inference_engine/hyperparameters_s.f90 | 34 ++++++++---- .../network_configuration_m.f90 | 39 -------------- .../network_configuration_s.f90 | 54 ------------------- src/inference_engine_m.f90 | 1 + test/hyperparameters_test_m.f90 | 21 +++++--- 6 files changed, 58 insertions(+), 111 deletions(-) delete mode 100644 src/inference_engine/network_configuration_m.f90 delete mode 100644 src/inference_engine/network_configuration_s.f90 diff --git a/src/inference_engine/hyperparameters_m.f90 b/src/inference_engine/hyperparameters_m.f90 index dea9e6580..174545008 100644 --- a/src/inference_engine/hyperparameters_m.f90 +++ b/src/inference_engine/hyperparameters_m.f90 @@ -12,13 +12,23 @@ module hyperparameters_m character(len=:), allocatable :: optimizer_ contains procedure :: to_json + procedure :: equals + generic :: operator(==) => equals end type interface hyperparameters_t - pure module function from_json(file_) result(hyperparameters) + pure module function from_json(lines) result(hyperparameters) implicit none - type(file_t), intent(in) :: file_ + type(string_t), intent(in) :: lines(:) + type(hyperparameters_t) hyperparameters + end function + + pure module function from_components(mini_batches, learning_rate, optimizer) result(hyperparameters) + implicit none + integer, intent(in) :: mini_batches + real, intent(in) :: learning_rate + character(len=*), intent(in) :: optimizer type(hyperparameters_t) hyperparameters end function @@ -32,6 +42,12 @@ 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) + implicit none + class(hyperparameters_t), intent(in) :: lhs, rhs + logical lhs_equals_rhs + end function + end interface end module diff --git a/src/inference_engine/hyperparameters_s.f90 b/src/inference_engine/hyperparameters_s.f90 index c1f9aae94..aadab89ed 100644 --- a/src/inference_engine/hyperparameters_s.f90 +++ b/src/inference_engine/hyperparameters_s.f90 @@ -8,15 +8,31 @@ contains + module procedure from_components + hyperparameters%mini_batches_ = mini_batches + hyperparameters%learning_rate_ = learning_rate + hyperparameters%optimizer_ = optimizer + end procedure + + module procedure equals + + real, parameter :: tolerance = 1.E-08 + + call assert(allocated(lhs%optimizer_) .and. allocated(rhs%optimizer_), "hyperparameters_s(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 from_json - type(string_t), allocatable :: lines(:) integer l logical hyperparameters_key_found - lines = file_%lines() hyperparameters_key_found = .false. - loop_through_file: & do l=1,size(lines) if (lines(l)%get_json_key() == "hyperparameters") then hyperparameters_key_found = .true. @@ -25,24 +41,24 @@ hyperparameters%optimizer_ = lines(l+3)%get_json_value(string_t(optimizer_key), mold=string_t("")) return end if - end do loop_through_file + end do call assert(hyperparameters_key_found, "hyperparameters_s(from_json): hyperparameters_found") end procedure module procedure to_json character(len=*), parameter :: indent = repeat(" ",ncopies=4) - integer, parameter :: max_digits = 12 - character(len=max_digits) mini_batches_string, learning_rate_string + integer, parameter :: max_width= 18 + 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 // '": ' // mini_batches_string ), & - string_t(indent // indent // '"' // learning_rate_key // '": ' // learning_rate_string ), & - string_t(indent // indent // '"' // optimizer_key // '": "' // self%optimizer_ // '"'), & + string_t(indent // indent // '"' // mini_batches_key // '" : ' // mini_batches_string ), & + string_t(indent // indent // '"' // learning_rate_key // '" : ' // learning_rate_string ), & + string_t(indent // indent // '"' // optimizer_key // '" : "' // self%optimizer_ // '"'), & string_t(indent // '}') & ] end procedure diff --git a/src/inference_engine/network_configuration_m.f90 b/src/inference_engine/network_configuration_m.f90 deleted file mode 100644 index 6f057e7a6..000000000 --- a/src/inference_engine/network_configuration_m.f90 +++ /dev/null @@ -1,39 +0,0 @@ -module network_configuration_m - use sourcery_m, only : file_t - implicit none - - private - public :: network_configuration_t - - type network_configuration_t - private - type(string_t) activation_function_ - integer, allocatable :: nodes_per_layer_(:) - logical skip_connections_ - contains - procedure :: to_json - end type - - end type - - interface network_configuration_t - - elemental module function from_json(file_) result(network_configuration) - implicit none - type(file_t), intent(in) :: file_ - type(network_configuration_t) network_configuration - end function - - end interface - - interface - - elemental module function to_json(self) result(json_file) - implicit none - class(network_configuration_t), intent(in) :: self - type(file_t) json_file - end function - - end interface - -end module diff --git a/src/inference_engine/network_configuration_s.f90 b/src/inference_engine/network_configuration_s.f90 deleted file mode 100644 index 43249fcc4..000000000 --- a/src/inference_engine/network_configuration_s.f90 +++ /dev/null @@ -1,54 +0,0 @@ -submodule(network_configuration_m) network_configuration_s - use assert_m, only : assert - use sourcery_m, only : string_t - implicit none - - character(len=*), parameter :: activation_function_key = "activation function" - character(len=*), parameter :: nodes_per_layer_key = "nodes per layer" - character(len=*), parameter :: skip_connections_key = "skip connections" - -contains - - module procedure from_json - type(string_t), allocatable :: lines(:) - integer l - logical network configuration_key_found - - lines = file_%lines() - network configuration_key_found = .false. - - loop_through_file: & - do l=1,size(lines) - if (line(l)%get_key() == "network configuration") then - network configuration_key_found = .true. - self%activation_function_ = line(l+1)%get_json_value(activation_function_key, mold=string("")) - self%nodes_per_layer_ = line(l+2)%get_json_value(nodes_per_layer_key , mold=[integer::]) - self%skip_connections_ = line(l+2)%get_json_value(skip_connetions_key , mold=.true.) - return - end if - end do loop_through_file - - call assert(network configuration_found, "network configuration_s(from_json): network configuration_found") - end procedure - - module procedure to_json - character(len=:), parameter :: indent = repeat(" ",ncopies=4) - integer, parameter :: max_digits = 12, max_length=5 - character(len=max_digits) activation_function_string, nodes_per_layer_string, skip_connections_string - character(len=max_length) skip_connections_string - - - write(activation_function_string,*) self%activation_function_ - write(nodes_per_layer_string ,*) self%nodes_per_layer_ - write(skip_connections_string ,*) merge("true ","false", self%skip_connections_) - - lines = [ & - string_t(indent // '"network configuration": {'), & - string_t(indent // indent // '"' // activation_function_key //'": ' // activation_function_string ), & - string_t(indent // indent // '"' // nodes_per_layer_key //'": ' // nodes_per_layer_string ), & - string_t(indent // indent // '"' // skip_connections_key //'": "' // skip_connections_string // '"'), & - string_t(indent // '}') & - ] - end procedure - -end submodule network_configuration_s diff --git a/src/inference_engine_m.f90 b/src/inference_engine_m.f90 index 81e3614f9..fd3bcdfc9 100644 --- a/src/inference_engine_m.f90 +++ b/src/inference_engine_m.f90 @@ -15,5 +15,6 @@ module inference_engine_m use swish_m, only : swish_t use tensor_m, only : tensor_t use trainable_engine_m, only : trainable_engine_t + use hyperparameters_m, only : hyperparameters_t implicit none end module diff --git a/test/hyperparameters_test_m.f90 b/test/hyperparameters_test_m.f90 index ee7440741..f2d1f65b4 100644 --- a/test/hyperparameters_test_m.f90 +++ b/test/hyperparameters_test_m.f90 @@ -5,7 +5,8 @@ module hyperparameters_test_m ! External dependencies use assert_m, only : assert - use sourcery_m, only : string_t, test_t, test_result_t + use sourcery_m, only : string_t, test_t, test_result_t, file_t + use inference_engine_m, only : hyperparameters_t ! Internal dependencies use hyperparameters_m, only : hyperparameters_t @@ -32,15 +33,15 @@ function results() result(test_results) type(test_result_t), allocatable :: test_results(:) character(len=*), parameter :: longest_description = & - "writing and then reading gives input matching output for perturbed identity network" + "component-wise construction followed by conversion to and from JSON" associate( & descriptions => & [ character(len=len(longest_description)) :: & - "writing and then reading gives input matching output for perturbed identity network" & + "component-wise construction followed by conversion to and from JSON" & ], & outcomes => & - [ write_then_read_perturbed_identity() & + [ write_then_read_hyperparameters() & ] & ) call assert(size(descriptions) == size(outcomes),"hyperparameters_test_m(results): size(descriptions) == size(outcomes)") @@ -49,9 +50,15 @@ function results() result(test_results) end function - function write_then_read_perturbed_identity() result(test_passes) - logical, allocatable :: test_passes(:) - test_passes = [.true.] + function write_then_read_hyperparameters() result(test_passes) + logical test_passes + + associate(hyperparameters => hyperparameters_t(mini_batches=5, learning_rate=1., optimizer = "stochastic gradient descent")) + associate(from_json => hyperparameters_t(hyperparameters%to_json())) + test_passes = hyperparameters == from_json + end associate + end associate + end function end module hyperparameters_test_m \ No newline at end of file From 829b967c3cc446aac2671006366bf7c87e4f629e Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Tue, 31 Oct 2023 10:26:04 -0700 Subject: [PATCH 05/11] feat(network_configuration): add type & JSON I/O --- .../network_configuration_m.f90 | 53 +++++++++++++++ .../network_configuration_s.f90 | 68 +++++++++++++++++++ src/inference_engine_m.f90 | 1 + test/main.f90 | 3 + test/network_configuration_test_m.f90 | 65 ++++++++++++++++++ 5 files changed, 190 insertions(+) create mode 100644 src/inference_engine/network_configuration_m.f90 create mode 100644 src/inference_engine/network_configuration_s.f90 create mode 100644 test/network_configuration_test_m.f90 diff --git a/src/inference_engine/network_configuration_m.f90 b/src/inference_engine/network_configuration_m.f90 new file mode 100644 index 000000000..f86eb637e --- /dev/null +++ b/src/inference_engine/network_configuration_m.f90 @@ -0,0 +1,53 @@ +module network_configuration_m + use sourcery_m, only : string_t, file_t + implicit none + + private + public :: network_configuration_t + + type network_configuration_t + private + logical :: skip_connections_ = .false. + integer, allocatable :: nodes_per_layer_(:) + character(len=:), allocatable :: activation_function_ + contains + procedure :: to_json + procedure :: equals + generic :: operator(==) => equals + end type + + interface network_configuration_t + + pure module function from_json(lines) result(network_configuration) + implicit none + type(string_t), intent(in) :: lines(:) + type(network_configuration_t) network_configuration + end function + + pure module function from_components(skip_connections, nodes_per_layer, activation_function) result(network_configuration) + implicit none + logical, intent(in) :: skip_connections + integer, intent(in) :: nodes_per_layer(:) + character(len=*), intent(in) :: activation_function + type(network_configuration_t) network_configuration + end function + + end interface + + interface + + pure module function to_json(self) result(lines) + implicit none + class(network_configuration_t), intent(in) :: self + type(string_t), allocatable :: lines(:) + end function + + elemental module function equals(lhs, rhs) result(lhs_equals_rhs) + implicit none + class(network_configuration_t), intent(in) :: lhs, rhs + logical lhs_equals_rhs + end function + + end interface + +end module diff --git a/src/inference_engine/network_configuration_s.f90 b/src/inference_engine/network_configuration_s.f90 new file mode 100644 index 000000000..66cebec50 --- /dev/null +++ b/src/inference_engine/network_configuration_s.f90 @@ -0,0 +1,68 @@ +submodule(network_configuration_m) network_configuration_s + use assert_m, only : assert + use sourcery_m, only : csv + implicit none + + character(len=*), parameter :: skip_connections_key = "skip connections" + character(len=*), parameter :: nodes_per_layer_key = "nodes per layer" + character(len=*), parameter :: activation_function_key = "activation function" + +contains + + module procedure from_components + network_configuration%skip_connections_ = skip_connections + network_configuration%nodes_per_layer_ = nodes_per_layer + network_configuration%activation_function_ = activation_function + end procedure + + module procedure equals + + call assert(allocated(lhs%activation_function_) .and. allocated(rhs%activation_function_), "network_configuration_s(equals): allocated activation_functions") + + lhs_equals_rhs = & + lhs%skip_connections_ .eqv. rhs%skip_connections_ .and. & + lhs%activation_function_ == rhs%activation_function_ .and. & + all(lhs%nodes_per_layer_ == rhs%nodes_per_layer_) + + end procedure + + module procedure from_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_integer_array(string_t(nodes_per_layer_key), mold=[0,1]) + network_configuration%activation_function_ = lines(l+3)%get_json_value(string_t(activation_function_key), mold=string_t("")) + return + end if + end do + + call assert(network_configuration_key_found, "network_configuration_s(from_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 + character(len=max_logical_width) skip_connections_string + character(len=:), allocatable :: nodes_per_layer_string + + allocate(character(len=size(self%nodes_per_layer_)*char_per_elem + brackets) :: nodes_per_layer_string) + + write(skip_connections_string,*) trim(merge("true ","false",self%skip_connections_)) + write(nodes_per_layer_string, csv) self%nodes_per_layer_ + + lines = [ & + string_t(indent // '"network configuration": {'), & + string_t(indent // indent // '"' // skip_connections_key // '" : ' // skip_connections_string ), & + string_t(indent // indent // '"' // nodes_per_layer_key // '" : [' // trim(nodes_per_layer_string) // ']' ), & + string_t(indent // indent // '"' // activation_function_key // '" : "' // self%activation_function_ // '"'), & + string_t(indent // '}') & + ] + end procedure + +end submodule network_configuration_s diff --git a/src/inference_engine_m.f90 b/src/inference_engine_m.f90 index fd3bcdfc9..94b1d4a1e 100644 --- a/src/inference_engine_m.f90 +++ b/src/inference_engine_m.f90 @@ -16,5 +16,6 @@ module inference_engine_m use tensor_m, only : tensor_t use trainable_engine_m, only : trainable_engine_t use hyperparameters_m, only : hyperparameters_t + use network_configuration_m, only : network_configuration_t implicit none end module diff --git a/test/main.f90 b/test/main.f90 index 0867a6b84..c90821cb4 100644 --- a/test/main.f90 +++ b/test/main.f90 @@ -5,12 +5,14 @@ program main use asymmetric_engine_test_m, only : asymmetric_engine_test_t use trainable_engine_test_m, only : trainable_engine_test_t use hyperparameters_test_m, only : hyperparameters_test_t + use network_configuration_test_m, only : network_configuration_test_t implicit none 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(hyperparameters_test_t) hyperparameters_test + type(network_configuration_test_t) network_configuration_test real t_start, t_finish integer :: passes=0, tests=0 @@ -21,6 +23,7 @@ program main call asymmetric_engine_test%report(passes, tests) call trainable_engine_test%report(passes, tests) call hyperparameters_test%report(passes, tests) + call network_configuration_test%report(passes, tests) #ifndef __INTEL_FORTRAN block use netCDF_file_test_m, only : netCDF_file_test_t diff --git a/test/network_configuration_test_m.f90 b/test/network_configuration_test_m.f90 new file mode 100644 index 000000000..1c6c2cbd8 --- /dev/null +++ b/test/network_configuration_test_m.f90 @@ -0,0 +1,65 @@ +! Copyright (c), The Regents of the University of California +! Terms of use are as specified in LICENSE.txt +module network_configuration_test_m + !! Test network_configuration_t object I/O and construction + + ! External dependencies + use assert_m, only : assert + use sourcery_m, only : string_t, test_t, test_result_t, file_t + use inference_engine_m, only : network_configuration_t + + ! Internal dependencies + use network_configuration_m, only : network_configuration_t + + implicit none + + private + public :: network_configuration_test_t + + type, extends(test_t) :: network_configuration_test_t + contains + procedure, nopass :: subject + procedure, nopass :: results + end type + +contains + + pure function subject() result(specimen) + character(len=:), allocatable :: specimen + specimen = "A network_configuration_t object" + end function + + function results() result(test_results) + type(test_result_t), allocatable :: test_results(:) + + character(len=*), parameter :: longest_description = & + "component-wise construction followed by conversion to and from JSON" + + associate( & + descriptions => & + [ character(len=len(longest_description)) :: & + "component-wise construction followed by conversion to and from JSON" & + ], & + outcomes => & + [ write_then_read_network_configuration() & + ] & + ) + call assert(size(descriptions) == size(outcomes),"network_configuration_test_m(results): size(descriptions) == size(outcomes)") + test_results = test_result_t(descriptions, outcomes) + end associate + + end function + + function write_then_read_network_configuration() result(test_passes) + logical test_passes + + associate(constructed_from_components=> & + network_configuration_t(skip_connections=.false., nodes_per_layer=[2,72,2], activation_function="sigmoid")) + associate(constructed_from_json => network_configuration_t(constructed_from_components%to_json())) + test_passes = constructed_from_components == constructed_from_json + end associate + end associate + + end function + +end module network_configuration_test_m \ No newline at end of file From e629617b31b66ddcd9286c498b705b8c4bb4f30f Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Tue, 31 Oct 2023 12:33:13 -0700 Subject: [PATCH 06/11] feat(example): add print-sample-input-file.f90 --- example/print-sample-input-file.f90 | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 example/print-sample-input-file.f90 diff --git a/example/print-sample-input-file.f90 b/example/print-sample-input-file.f90 new file mode 100644 index 000000000..b8cc6922a --- /dev/null +++ b/example/print-sample-input-file.f90 @@ -0,0 +1,24 @@ +program print_sample_input_file + use inference_engine_m, only : hyperparameters_t, network_configuration_t + implicit none + + associate(params => hyperparameters_t(mini_batches=10, learning_rate=1.5, optimizer = "adam")) + associate(net_conf=> network_configuration_t(skip_connections=.false., nodes_per_layer=[2,72,2], activation_function="sigmoid")) + associate(params_json => params%to_json(), net_json => net_conf%to_json()) + print *,"{" + block + integer line + do line = 1, size(params_json) + print *, (params_json(line)%string()) + end do + do line = 1, size(net_json) + print *, (net_json(line)%string()) + end do + end block + print *,"}" + end associate + end associate + end associate + + +end program From cda51ee9e201c6be72e70f5259ea3ae73455e13c Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Tue, 31 Oct 2023 16:30:55 -0700 Subject: [PATCH 07/11] fix(network_configuration): update dependency This commit updates the sourcery library dependency to version 4.4.2, which resolves a generic interface disambiguation bug that blocked the network_configuration_t unit test from passing. --- fpm.toml | 2 +- src/inference_engine/network_configuration_s.f90 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fpm.toml b/fpm.toml index bdc742364..128595586 100644 --- a/fpm.toml +++ b/fpm.toml @@ -6,5 +6,5 @@ maintainer = "rouson@lbl.gov" [dependencies] assert = {git = "https://github.com/sourceryinstitute/assert", tag = "1.5.0"} -sourcery = {git = "https://github.com/sourceryinstitute/sourcery", tag = "4.4.0"} +sourcery = {git = "https://github.com/sourceryinstitute/sourcery", tag = "4.4.2"} netcdf-interfaces = {git = "https://github.com/rouson/netcdf-interfaces.git", branch = "implicit-interfaces"} diff --git a/src/inference_engine/network_configuration_s.f90 b/src/inference_engine/network_configuration_s.f90 index 66cebec50..9f93d8d47 100644 --- a/src/inference_engine/network_configuration_s.f90 +++ b/src/inference_engine/network_configuration_s.f90 @@ -36,7 +36,7 @@ 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_integer_array(string_t(nodes_per_layer_key), mold=[0,1]) + network_configuration%nodes_per_layer_ = lines(l+2)%get_json_value(string_t(nodes_per_layer_key), mold=[integer::]) network_configuration%activation_function_ = lines(l+3)%get_json_value(string_t(activation_function_key), mold=string_t("")) return end if From de39ecbb61319319b3bb392dd02066e84838f83a Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Tue, 31 Oct 2023 17:43:04 -0700 Subject: [PATCH 08/11] refac(hyperparams|net_conf):rm unused dependencies --- src/inference_engine/hyperparameters_m.f90 | 2 +- src/inference_engine/network_configuration_m.f90 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/inference_engine/hyperparameters_m.f90 b/src/inference_engine/hyperparameters_m.f90 index 174545008..35eb5156a 100644 --- a/src/inference_engine/hyperparameters_m.f90 +++ b/src/inference_engine/hyperparameters_m.f90 @@ -1,5 +1,5 @@ module hyperparameters_m - use sourcery_m, only : string_t, file_t + use sourcery_m, only : string_t implicit none private diff --git a/src/inference_engine/network_configuration_m.f90 b/src/inference_engine/network_configuration_m.f90 index f86eb637e..30e85cf0d 100644 --- a/src/inference_engine/network_configuration_m.f90 +++ b/src/inference_engine/network_configuration_m.f90 @@ -1,5 +1,5 @@ module network_configuration_m - use sourcery_m, only : string_t, file_t + use sourcery_m, only : string_t implicit none private From 628de786eee92b9ea5814b0e13fdc834fe4ea9fb Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Tue, 31 Oct 2023 18:44:28 -0700 Subject: [PATCH 09/11] feat(training_configuration_t): add type, JSON I/O --- src/inference_engine/hyperparameters_s.f90 | 6 +- .../network_configuration_s.f90 | 6 +- .../training_configuration_m.f90 | 53 +++++++++++++++ .../training_configuration_s.f90 | 49 +++++++++++++ src/inference_engine_m.f90 | 5 +- test/main.f90 | 3 + test/training_configuration_test_m.f90 | 68 +++++++++++++++++++ 7 files changed, 182 insertions(+), 8 deletions(-) create mode 100644 src/inference_engine/training_configuration_m.f90 create mode 100644 src/inference_engine/training_configuration_s.f90 create mode 100644 test/training_configuration_test_m.f90 diff --git a/src/inference_engine/hyperparameters_s.f90 b/src/inference_engine/hyperparameters_s.f90 index aadab89ed..31e67b64b 100644 --- a/src/inference_engine/hyperparameters_s.f90 +++ b/src/inference_engine/hyperparameters_s.f90 @@ -56,9 +56,9 @@ lines = [ & string_t(indent // '"hyperparameters": {'), & - string_t(indent // indent // '"' // mini_batches_key // '" : ' // mini_batches_string ), & - string_t(indent // indent // '"' // learning_rate_key // '" : ' // learning_rate_string ), & - string_t(indent // indent // '"' // optimizer_key // '" : "' // self%optimizer_ // '"'), & + 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 diff --git a/src/inference_engine/network_configuration_s.f90 b/src/inference_engine/network_configuration_s.f90 index 9f93d8d47..99a810aa4 100644 --- a/src/inference_engine/network_configuration_s.f90 +++ b/src/inference_engine/network_configuration_s.f90 @@ -58,9 +58,9 @@ lines = [ & string_t(indent // '"network configuration": {'), & - string_t(indent // indent // '"' // skip_connections_key // '" : ' // skip_connections_string ), & - string_t(indent // indent // '"' // nodes_per_layer_key // '" : [' // trim(nodes_per_layer_string) // ']' ), & - string_t(indent // indent // '"' // activation_function_key // '" : "' // self%activation_function_ // '"'), & + string_t(indent // indent // '"' // skip_connections_key // '" : ' // trim(adjustl(skip_connections_string )) // ','), & + string_t(indent // indent // '"' // nodes_per_layer_key // '" : [' // trim(adjustl(nodes_per_layer_string )) // '],'), & + string_t(indent // indent // '"' // activation_function_key // '" : "' // trim(adjustl(self%activation_function_)) // '"' ), & string_t(indent // '}') & ] end procedure diff --git a/src/inference_engine/training_configuration_m.f90 b/src/inference_engine/training_configuration_m.f90 new file mode 100644 index 000000000..300990289 --- /dev/null +++ b/src/inference_engine/training_configuration_m.f90 @@ -0,0 +1,53 @@ +module training_configuration_m + use sourcery_m, only : string_t, file_t + use hyperparameters_m, only : hyperparameters_t + use network_configuration_m, only : network_configuration_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_ + contains + procedure :: to_json + procedure :: equals + generic :: operator(==) => equals + end type + + interface training_configuration_t + + module function 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) + implicit none + type(file_t), intent(in) :: file_object + type(training_configuration_t) training_configuration + end function + + end interface + + interface + + pure module function 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) + implicit none + class(training_configuration_t), intent(in) :: lhs, rhs + logical lhs_eq_rhs + end function + + end interface + +end module diff --git a/src/inference_engine/training_configuration_s.f90 b/src/inference_engine/training_configuration_s.f90 new file mode 100644 index 000000000..29382a665 --- /dev/null +++ b/src/inference_engine/training_configuration_s.f90 @@ -0,0 +1,49 @@ +submodule(training_configuration_m) training_configuration_s + use assert_m, only : assert + implicit none + + character(len=*), parameter :: header="{", footer="}", separator = "," + +contains + + module procedure 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 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 + + training_configuration%file_t = file_object + + associate(lines => training_configuration%file_t%lines()) + call assert(trim(adjustl(lines(file_start)%string()))==header,"training_configuration_s(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", & + 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)) + end associate + end procedure + + module procedure to_json + json_lines = self%lines() + end procedure + + module procedure equals + lhs_eq_rhs = & + lhs%hyperparameters_ == rhs%hyperparameters_ .and. & + lhs%network_configuration_ == rhs%network_configuration_ + end procedure + +end submodule training_configuration_s diff --git a/src/inference_engine_m.f90 b/src/inference_engine_m.f90 index 94b1d4a1e..081eb3773 100644 --- a/src/inference_engine_m.f90 +++ b/src/inference_engine_m.f90 @@ -4,18 +4,19 @@ 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 hyperparameters_m, only : hyperparameters_t use input_output_pair_m, only : input_output_pair_t, shuffle use inference_engine_m_, only : inference_engine_t, difference_t use kind_parameters_m, only : rkind use mini_batch_m, only : mini_batch_t use NetCDF_file_m, only : NetCDF_file_t + use network_configuration_m, only : network_configuration_t use relu_m, only : relu_t use sigmoid_m, only : sigmoid_t use step_m, only : step_t use swish_m, only : swish_t use tensor_m, only : tensor_t use trainable_engine_m, only : trainable_engine_t - use hyperparameters_m, only : hyperparameters_t - use network_configuration_m, only : network_configuration_t + use training_configuration_m, only : training_configuration_t implicit none end module diff --git a/test/main.f90 b/test/main.f90 index c90821cb4..cc3f840fd 100644 --- a/test/main.f90 +++ b/test/main.f90 @@ -6,6 +6,7 @@ program main use trainable_engine_test_m, only : trainable_engine_test_t use hyperparameters_test_m, only : hyperparameters_test_t use network_configuration_test_m, only : network_configuration_test_t + use training_configuration_test_m, only : training_configuration_test_t implicit none type(inference_engine_test_t) inference_engine_test @@ -13,6 +14,7 @@ program main type(trainable_engine_test_t) trainable_engine_test type(hyperparameters_test_t) hyperparameters_test type(network_configuration_test_t) network_configuration_test + type(training_configuration_test_t) training_configuration_test real t_start, t_finish integer :: passes=0, tests=0 @@ -24,6 +26,7 @@ program main call trainable_engine_test%report(passes, tests) call hyperparameters_test%report(passes, tests) call network_configuration_test%report(passes, tests) + call training_configuration_test%report(passes, tests) #ifndef __INTEL_FORTRAN block use netCDF_file_test_m, only : netCDF_file_test_t diff --git a/test/training_configuration_test_m.f90 b/test/training_configuration_test_m.f90 new file mode 100644 index 000000000..3701a6ad5 --- /dev/null +++ b/test/training_configuration_test_m.f90 @@ -0,0 +1,68 @@ +! Copyright (c), The Regents of the University of California +! Terms of use are as specified in LICENSE.txt +module training_configuration_test_m + !! Test training_configuration_t object I/O and construction + + ! External dependencies + use assert_m, only : assert + use sourcery_m, only : string_t, test_t, test_result_t, file_t + use inference_engine_m, only : training_configuration_t, hyperparameters_t, network_configuration_t + + ! Internal dependencies + use training_configuration_m, only : training_configuration_t + + implicit none + + private + public :: training_configuration_test_t + + type, extends(test_t) :: training_configuration_test_t + contains + procedure, nopass :: subject + procedure, nopass :: results + end type + +contains + + pure function subject() result(specimen) + character(len=:), allocatable :: specimen + specimen = "A training_configuration_t object" + end function + + function results() result(test_results) + type(test_result_t), allocatable :: test_results(:) + + character(len=*), parameter :: longest_description = & + "component-wise construction followed by conversion to and from JSON" + + associate( & + descriptions => & + [ character(len=len(longest_description)) :: & + "component-wise construction followed by conversion to and from JSON" & + ], & + outcomes => & + [ construct_and_convert_to_and_from_json() & + ] & + ) + call assert(size(descriptions) == size(outcomes),"training_configuration_test_m(results): size(descriptions)==size(outcomes)") + test_results = test_result_t(descriptions, outcomes) + end associate + + end function + + function construct_and_convert_to_and_from_json() result(test_passes) + logical test_passes + + + associate(training_configuration => training_configuration_t( & + hyperparameters_t(mini_batches=5, learning_rate=1., optimizer = "adam"), & + network_configuration_t(skip_connections=.false., nodes_per_layer=[2,72,2], activation_function="sigmoid") & + )) + associate(from_json => training_configuration_t(file_t(training_configuration%to_json()))) + test_passes = training_configuration == from_json + end associate + end associate + + end function + +end module training_configuration_test_m \ No newline at end of file From 8b71baeb6e5d344f89a4855d58dc4df080d0576b Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Tue, 31 Oct 2023 18:45:06 -0700 Subject: [PATCH 10/11] feat(exampl): add print-training-configuration.f90 replaces print-sample-input-file.f90 --- example/print-sample-input-file.f90 | 24 ------------------------ example/print-training-configuration.f90 | 15 +++++++++++++++ 2 files changed, 15 insertions(+), 24 deletions(-) delete mode 100644 example/print-sample-input-file.f90 create mode 100644 example/print-training-configuration.f90 diff --git a/example/print-sample-input-file.f90 b/example/print-sample-input-file.f90 deleted file mode 100644 index b8cc6922a..000000000 --- a/example/print-sample-input-file.f90 +++ /dev/null @@ -1,24 +0,0 @@ -program print_sample_input_file - use inference_engine_m, only : hyperparameters_t, network_configuration_t - implicit none - - associate(params => hyperparameters_t(mini_batches=10, learning_rate=1.5, optimizer = "adam")) - associate(net_conf=> network_configuration_t(skip_connections=.false., nodes_per_layer=[2,72,2], activation_function="sigmoid")) - associate(params_json => params%to_json(), net_json => net_conf%to_json()) - print *,"{" - block - integer line - do line = 1, size(params_json) - print *, (params_json(line)%string()) - end do - do line = 1, size(net_json) - print *, (net_json(line)%string()) - end do - end block - print *,"}" - end associate - end associate - end associate - - -end program diff --git a/example/print-training-configuration.f90 b/example/print-training-configuration.f90 new file mode 100644 index 000000000..c002286c2 --- /dev/null +++ b/example/print-training-configuration.f90 @@ -0,0 +1,15 @@ +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 sourcery_m, only : file_t + implicit none + + associate(training_configuration => training_configuration_t( & + hyperparameters_t(mini_batches=10, learning_rate=1.5, optimizer = "adam"), & + network_configuration_t(skip_connections=.false., nodes_per_layer=[2,72,2], activation_function="sigmoid") & + )) + associate(json_file => file_t(training_configuration%to_json())) + call json_file%write_lines() + end associate + end associate +end program From d93e16811d93b4a535ff09101c1fbcdc2224efff Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Tue, 31 Oct 2023 19:13:17 -0700 Subject: [PATCH 11/11] feat(fpm): update sourcery dependency version --- fpm.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fpm.toml b/fpm.toml index 128595586..f8274f992 100644 --- a/fpm.toml +++ b/fpm.toml @@ -6,5 +6,5 @@ maintainer = "rouson@lbl.gov" [dependencies] assert = {git = "https://github.com/sourceryinstitute/assert", tag = "1.5.0"} -sourcery = {git = "https://github.com/sourceryinstitute/sourcery", tag = "4.4.2"} +sourcery = {git = "https://github.com/sourceryinstitute/sourcery", tag = "4.4.3"} netcdf-interfaces = {git = "https://github.com/rouson/netcdf-interfaces.git", branch = "implicit-interfaces"}