From 0eb18416a14ea5b2cd1a4b5a05af962bd06b0740 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Fri, 8 Aug 2025 07:18:48 -0700 Subject: [PATCH 1/4] refac: convert assertions to call_julienne_assert_ --- example/learn-addition.F90 | 15 +++++--- example/learn-exponentiation.F90 | 13 +++++-- example/learn-multiplication.F90 | 14 +++++-- example/learn-power-series.F90 | 14 +++++-- example/learn-saturated-mixing-ratio.F90 | 8 ++-- example/train-and-write.F90 | 4 +- src/fiats/layer_s.F90 | 12 ++++-- src/fiats/neural_network_s.F90 | 48 ++++++++++-------------- src/fiats/neuron_s.F90 | 3 +- src/fiats/tensor_map_s.F90 | 15 ++++---- test/asymmetric_network_test_m.F90 | 41 ++++++++++---------- test/hyperparameters_test_m.F90 | 9 +++-- test/main.F90 | 12 ++++-- test/metadata_test_m.F90 | 8 ++-- test/network_configuration_test_m.F90 | 9 ++--- test/neural_network_test_m.F90 | 24 ++++++------ test/tensor_map_test_m.F90 | 8 ++-- test/tensor_names_test.f90 | 8 ++-- test/tensor_test_m.f90 | 17 +++------ test/trainable_network_test_m.F90 | 39 +++++++++---------- test/training_configuration_test_m.F90 | 20 +++++----- test/training_data_files_test_m.F90 | 6 +-- 22 files changed, 181 insertions(+), 166 deletions(-) diff --git a/example/learn-addition.F90 b/example/learn-addition.F90 index de77bef72..737bf9816 100644 --- a/example/learn-addition.F90 +++ b/example/learn-addition.F90 @@ -6,7 +6,11 @@ module addition_m !! Define a function that produces the desired network output for a given network input use fiats_m, only : tensor_t - use julienne_m, only : call_julienne_assert_, operator(.isAtLeast.), operator(.isAtMost.), operator(.also.) + use julienne_m, only : & + call_julienne_assert_ & + ,operator(.also.) & + ,operator(.isAtLeast.) & + ,operator(.isAtMost.) implicit none contains @@ -14,11 +18,12 @@ elemental function y(x_tensor) result(a_tensor) type(tensor_t), intent(in) :: x_tensor type(tensor_t) a_tensor associate(x => x_tensor%values()) - call_julienne_assert((ubound(x,1) .isAtLeast. 7) .also. (lbound(x,1) .isAtMost. 2)) - a_tensor = tensor_t([x(1)+x(2), x(2)+x(3), x(3)+x(4), x(4)+x(5), x(5)+x(6), x(6)+x(8)]) + associate(sufficient_input => (ubound(x,1) .isAtLeast. 7) .also. (lbound(x,1) .isAtMost. 2)) + call_julienne_assert(sufficient_input) + a_tensor = tensor_t([x(1)+x(2), x(2)+x(3), x(3)+x(4), x(4)+x(5), x(5)+x(6), x(6)+x(8)]) + end associate end associate end function - end module program learn_addition @@ -62,7 +67,7 @@ program learn_addition inputs = [(tensor_t(real([(j*i, j = 1,num_inputs)])/(num_inputs*num_pairs)), i = 1, num_pairs)] desired_outputs = y(inputs) output_sizes = [(size(desired_outputs(i)%values()),i=1,size(desired_outputs))] - call_julienne_assert(.all.(num_outputs .equalsExpected. output_sizes)) + call_julienne_assert(.all. (num_outputs .equalsExpected. output_sizes)) end block input_output_pairs = input_output_pair_t(inputs, desired_outputs) block diff --git a/example/learn-exponentiation.F90 b/example/learn-exponentiation.F90 index 9f59dbf84..f2fa5a07c 100644 --- a/example/learn-exponentiation.F90 +++ b/example/learn-exponentiation.F90 @@ -6,7 +6,11 @@ module exponentiation_m !! Define a function that produces the desired network output for a given network input use fiats_m, only : tensor_t - use julienne_m, only : call_julienne_assert_, operator(.isAtLeast.), operator(.isAtMost.), operator(.also.) + use julienne_m, only : & + call_julienne_assert_ & + ,operator(.also.) & + ,operator(.isAtMost.) & + ,operator(.isAtLeast.) implicit none contains @@ -14,11 +18,12 @@ elemental function y(x_tensor) result(a_tensor) type(tensor_t), intent(in) :: x_tensor type(tensor_t) a_tensor associate(x => x_tensor%values()) - call_julienne_assert(ubound(x,1) .isAtLeast. 7 .also. lbound(x,1) .isAtMost. 2) - a_tensor = tensor_t([x(1)**2, x(2)**3, x(3)**4, x(4)**4, x(5)**3, x(6)**2]) + associate(suffient_input => (ubound(x,1) .isAtLeast. 7) .also. (lbound(x,1) .isAtMost. 2)) + call_julienne_assert(suffient_input) + a_tensor = tensor_t([x(1)**2, x(2)**3, x(3)**4, x(4)**4, x(5)**3, x(6)**2]) + end associate end associate end function - end module program learn_exponentiation diff --git a/example/learn-multiplication.F90 b/example/learn-multiplication.F90 index e595e9215..4894f5d4e 100644 --- a/example/learn-multiplication.F90 +++ b/example/learn-multiplication.F90 @@ -6,7 +6,11 @@ module multiply_inputs !! Define a function that produces the desired network output for a given network input use fiats_m, only : tensor_t - use julienne_m, only : call_julienne_assert_, operator(.isAtMost.), operator(.isAtLeast.), operator(.also.) + use julienne_m, only : & + call_julienne_assert_ & + ,operator(.also.) & + ,operator(.isAtLeast.) & + ,operator(.isAtMost.) implicit none contains @@ -14,8 +18,10 @@ elemental function y(x_tensor) result(a_tensor) type(tensor_t), intent(in) :: x_tensor type(tensor_t) a_tensor associate(x => x_tensor%values()) - call_julienne_assert((ubound(x,1) .isAtMost. 7) .also. (lbound(x,1) .isAtMost. 2)) - a_tensor = tensor_t([x(1)*x(2), x(2)*x(3), x(3)*x(4), x(4)*x(5), x(5)*x(6), x(6)*x(8)]) + associate(sufficient_inputs => (ubound(x,1).isAtLeast. 7) .also. (lbound(x,1) .isAtMost. 2)) + call_julienne_assert(sufficient_inputs) + a_tensor = tensor_t([x(1)*x(2), x(2)*x(3), x(3)*x(4), x(4)*x(5), x(5)*x(6), x(6)*x(8)]) + end associate end associate end function @@ -60,7 +66,7 @@ program learn_multiplication inputs = [(tensor_t(real([(j*i, j = 1,num_inputs)])/(num_inputs*num_pairs)), i = 1, num_pairs)] desired_outputs = y(inputs) output_sizes = [(size(desired_outputs(i)%values()),i=1,size(desired_outputs))] - call_julienne_assert(num_outputs .equalsExpected. output_sizes) + call_julienne_assert(.all. (num_outputs .equalsExpectd. output_sizes)) end block input_output_pairs = input_output_pair_t(inputs, desired_outputs) block diff --git a/example/learn-power-series.F90 b/example/learn-power-series.F90 index 305aedb9f..5a4988db7 100644 --- a/example/learn-power-series.F90 +++ b/example/learn-power-series.F90 @@ -6,16 +6,22 @@ module power_series !! Define a function that produces the desired network output for a given network input use fiats_m, only : tensor_t - use julienne_m, only : call_julienne_assert_, operator(.isAtMost.), operator(.isAtLeast.), operator(.also.) + use julienne_m, only : & + call_julienne_assert_ & + ,operator(.also.) & + ,operator(.isAtLeast.) & + ,operator(.isAtMost.) implicit none contains elemental function y(x_in) result(a) type(tensor_t), intent(in) :: x_in type(tensor_t) a - associate(x => x_in%values()) - call_julienne_assert((ubound(x,1) .isAtMost. 7) .also. (lbound(x,1) .isAtMost. 2)) - a = tensor_t([1 + x(1) + (x(1)**2)/2 + (x(1)**3)/6, x(2), x(3), x(4), x(5), x(6)]) + associate(x => x_in%values()) sufficient_input => (ubound(x,1) .isAtLeast. 7) .also. (lbound(x,1) .isAtMost. 2)) + associate(sufficient_input => (ubound(x,1) .isAtLeast. 7) .also. (lbound(x,1) .isAtMost. 2)) + call_julienne_assert(sufficient_input) + a = tensor_t([1 + x(1) + (x(1)**2)/2 + (x(1)**3)/6, x(2), x(3), x(4), x(5), x(6)]) + end associate end associate end function diff --git a/example/learn-saturated-mixing-ratio.F90 b/example/learn-saturated-mixing-ratio.F90 index af4db8b70..b825826f4 100644 --- a/example/learn-saturated-mixing-ratio.F90 +++ b/example/learn-saturated-mixing-ratio.F90 @@ -7,9 +7,9 @@ program train_saturated_mixture_ratio !! This program trains a neural network to learn the saturated mixing ratio function of ICAR. use fiats_m, only : trainable_network_t, mini_batch_t, tensor_t, input_output_pair_t, shuffle use julienne_m, only : & - command_line_t & + call_julienne_assert_ & + ,command_line_t & ,bin_t & - ,call_julienne_assert_ & ,csv & ,file_t & ,operator(.all.) & @@ -73,7 +73,9 @@ program train_saturated_mixture_ratio integer, allocatable :: output_sizes(:) inputs = [( [(tensor_t([T(i), p(j)]), j=1,size(p))], i = 1,size(T))] num_pairs = size(inputs) - call_julienne_assert(num_pairs .equalsExpected. size(T)*size(p)) + associate(inputs_tensor_array_complete => num_pairs .equalsExpected. size(T)*size(p)) + call_julienne_assert(inputs_tensor_array_complete) + end associate desired_outputs = y(inputs) output_sizes = [(size(desired_outputs(i)%values()),i=1,size(desired_outputs))] call_julienne_assert(.all. (num_outputs .equalsExpected. output_sizes)) diff --git a/example/train-and-write.F90 b/example/train-and-write.F90 index 3507d38ad..68b3f6d42 100644 --- a/example/train-and-write.F90 +++ b/example/train-and-write.F90 @@ -13,7 +13,7 @@ program train_and_write !! that is uniformly distributed on the range [0,0.1]. 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, operator(.equalsExpected.) + use julienne_m, only : string_t, file_t, command_line_t, bin_t, call_julienne_assert_, operator(.equalsExpected.) implicit none type(string_t) final_network_file @@ -42,7 +42,7 @@ program train_and_write associate(num_inputs => trainable_network%num_inputs(), num_outputs => trainable_network%num_outputs()) - call_julienne_assert(num_inputs .equalsExepcted. num_outputs) + call_julienne_assert(num_inputs .equalsExpected. num_outputs) block integer i, j inputs = [(tensor_t(real([(j*i, j = 1,num_inputs)])/(num_inputs*num_pairs)), i = 1, num_pairs)] diff --git a/src/fiats/layer_s.F90 b/src/fiats/layer_s.F90 index 49338d76a..c5faebac9 100644 --- a/src/fiats/layer_s.F90 +++ b/src/fiats/layer_s.F90 @@ -36,7 +36,9 @@ end do line = trim(adjustl(layer_lines(start+4*neurons_in_layer+1)%string())) - call_julienne_assert(line(1:1) .equalsExpected. ']') + associate(hidden_layer_end => ']') + call_julienne_assert(line(1:1) .equalsExpected. hidden_layer_end) + end associate if (line(len(line):len(line)) == ",") layer%next = layer_t(layer_lines, start+4*neurons_in_layer+2) @@ -67,7 +69,9 @@ end do line = trim(adjustl(layer_lines(start+4*neurons_in_layer+1)%string())) - call_julienne_assert(line(1:1) .equalsExpected. ']') + associate(hidden_layer_end => ']') + call_julienne_assert(line(1:1) .equalsExpected. ']') + end associate if (line(len(line):len(line)) == ",") layer%next = layer_t(layer_lines, start+4*neurons_in_layer+2) @@ -82,7 +86,7 @@ num_hidden_layers => hidden_layers%count_layers(), & num_output_layers => output_layer%count_layers() & ) - call_assert(num_output_layers==1) + call_julienne_assert(num_output_layers .equalsExpected. 1) associate(nodes => [num_inputs, neurons_per_hidden_layer, num_outputs]) associate(n_max => maxval(nodes)) @@ -154,7 +158,7 @@ num_hidden_layers => hidden_layers%count_layers(), & num_output_layers => output_layer%count_layers() & ) - call_assert(num_output_layers==1) + call_julienne_assert(num_output_layers .equalsExpected. 1) associate(nodes => [num_inputs, neurons_per_hidden_layer, num_outputs]) associate(n_max => maxval(nodes)) diff --git a/src/fiats/neural_network_s.F90 b/src/fiats/neural_network_s.F90 index fd14a1806..2f1137e21 100644 --- a/src/fiats/neural_network_s.F90 +++ b/src/fiats/neural_network_s.F90 @@ -7,7 +7,7 @@ submodule(neural_network_m) neural_network_s use assert_m - use julienne_m, only : call_julienne_assert_, operator(.equalsExpected.) + use julienne_m, only : call_julienne_assert_ , operator(.all.), operator(.equalsExpected.) use double_precision_string_m, only : double_precision_string_t use kind_parameters_m, only : double_precision use layer_m, only : layer_t @@ -152,7 +152,7 @@ elemental module subroutine double_precision_assert_conformable_with(self, neura call_assert(allocated(self%nodes_)) associate(max_width=>maxval(self%nodes_), component_sizes=>[size(self%biases_,1), size(self%weights_,1), size(self%weights_,2)]) - call_julienne_assert(component_sizes .equalsExpected. max_width) + call_julienne_assert(.all. (component_sizes .equalsExpected. max_width)) end associate associate(input_subscript => lbound(self%nodes_,1)) @@ -171,7 +171,9 @@ elemental module subroutine double_precision_assert_conformable_with(self, neura call_julienne_assert(all(component_sizes .equalsExpected. max_width)) end associate - call_julienne_assert(lbound(self%nodes_,1) .equalsExpected. input_layer) + associate(input_subscript => lbound(self%nodes_,1)) + call_julienne_assert(input_subscript .equalsExpected. input_layer) + end associate end procedure @@ -492,15 +494,10 @@ elemental module subroutine double_precision_assert_conformable_with(self, neura type(layer_t) hidden_layers, output_layer lines = file_%lines() - call_julienne_assert(adjustl(lines(1)%string()) .equalsExpected. "{") - - check_git_tag: & - block - character(len=:), allocatable :: tag - - tag = lines(2)%get_json_value("minimum_acceptable_tag", mold="") - call_julienne_assert(tag .equalsExpected. minimum_acceptable_tag) - end block check_git_tag + associate(outermost_object => '{') + call_julienne_assert(adjustl(lines(1)%string()) .equalsExpected. outermost_object) + end associate + call_julienne_assert(lines(2)%get_json_value("minimum_acceptable_tag", mold="") .equalsExpected. minimum_acceptable_tag) num_file_lines = size(lines) @@ -585,15 +582,10 @@ elemental module subroutine double_precision_assert_conformable_with(self, neura type(layer_t(double_precision)) hidden_layers, output_layer lines = file%double_precision_lines() - call_julienne_assert(adjustl(lines(1)%string()) .equalsExpected. "{") - - check_git_tag: & - block - character(len=:), allocatable :: tag - - tag = lines(2)%get_json_value("minimum_acceptable_tag", mold="") - call_julienne_assert(tag .equalsExpected. minimum_acceptable_tag) - end block check_git_tag + associate(outermost_object => '{') + call_julienne_assert(adjustl(lines(1)%string()) .equalsExpected. outermost_object) + end associate + call_julienne_assert(lines(2)%get_json_value("minimum_acceptable_tag", mold="") .equalsExpected. minimum_acceptable_tag) num_file_lines = size(lines) @@ -671,9 +663,9 @@ elemental module subroutine double_precision_assert_conformable_with(self, neura call_assert_consistency(self) - call_assert(all(shape(self%weights_) == shape(neural_network%weights_))) - call_assert(all(shape(self%biases_) == shape(neural_network%biases_))) - call_assert(all(shape(self%nodes_) == shape(neural_network%nodes_))) + call_julienne_assert(.all. (shape(self%weights_) .equalsExpected. shape(neural_network%weights_))) + call_julienne_assert(.all. (shape(self%biases_) .equalsExpected. shape(neural_network%biases_))) + call_julienne_assert(.all. (shape(self%nodes_) .equalsExpected. shape(neural_network%nodes_))) call_assert(self%activation_ == neural_network%activation_) end procedure @@ -682,10 +674,10 @@ elemental module subroutine double_precision_assert_conformable_with(self, neura call_assert_consistency(self) - call_assert(all(shape(self%weights_) == shape(neural_network%weights_))) - call_assert(all(shape(self%biases_) == shape(neural_network%biases_))) - call_assert(all(shape(self%nodes_) == shape(neural_network%nodes_))) - call_assert(self%activation_ == neural_network%activation_) + call_julienne_assert(.all. (shape(self%weights_) .equalsExpected. shape(neural_network%weights_))) + call_julienne_assert(.all. (shape(self%biases_) .equalsExpected. shape(neural_network%biases_))) + call_julienne_assert(.all. (shape(self%nodes_) .equalsExpected. shape(neural_network%nodes_))) + call_julienne_assert(self%activation_ == neural_network%activation_) end procedure diff --git a/src/fiats/neuron_s.F90 b/src/fiats/neuron_s.F90 index 6980e6aff..f45971c67 100644 --- a/src/fiats/neuron_s.F90 +++ b/src/fiats/neuron_s.F90 @@ -6,8 +6,7 @@ submodule(neuron_m) neuron_s use assert_m - use julienne_formats_m, only : separated_values - use julienne_m, only : operator(.equalsExpected.), call_julienne_assert_ + use julienne_m, only : operator(.equalsExpected.), call_julienne_assert_, separated_values implicit none contains diff --git a/src/fiats/tensor_map_s.F90 b/src/fiats/tensor_map_s.F90 index d4ac6fbb9..5251d5cab 100644 --- a/src/fiats/tensor_map_s.F90 +++ b/src/fiats/tensor_map_s.F90 @@ -1,10 +1,11 @@ ! Copyright (c) 2023-2025, The Regents of the University of California ! Terms of use are as specified in LICENSE.txt +#include "julienne-assert-macros.h" #include "assert_macros.h" submodule(tensor_map_m) tensor_map_s - use assert_m + use julienne_m, only : call_julienne_assert_, operator(.equalsExpected.) use julienne_m, only : separated_values use kind_parameters_m, only : default_real implicit none @@ -12,7 +13,7 @@ contains module procedure construct_default_real - call_assert(size(minima)==size(maxima)) + call_julienne_assert(size(minima) .equalsExpected. size(maxima)) tensor_map%layer_ = layer tensor_map%intercept_ = minima tensor_map%slope_ = maxima - minima @@ -35,7 +36,7 @@ end procedure module procedure construct_double_precision - call_assert(size(minima)==size(maxima)) + call_julienne_assert(size(minima) .equalsExpected. size(maxima)) tensor_map%layer_ = layer tensor_map%intercept_ = minima tensor_map%slope_ = maxima - minima @@ -85,8 +86,8 @@ call_assert(allocated(lhs%layer_ ) .and. allocated(rhs%layer_ )) call_assert(allocated(lhs%intercept_) .and. allocated(rhs%intercept_)) call_assert(allocated(lhs%slope_ ) .and. allocated(rhs%slope_ )) - call_assert(size(lhs%intercept_) == size(rhs%intercept_)) - call_assert(size(lhs%slope_ ) == size(rhs%slope_ )) + call_julienne_assert(size(lhs%intercept_) .equalsExpected. size(rhs%intercept_)) + call_julienne_assert(size(lhs%slope_ ) .equalsExpected. size(rhs%slope_ )) lhs_equals_rhs = & lhs%layer_ == rhs%layer_ .and. & @@ -100,8 +101,8 @@ call_assert(allocated(lhs%layer_ ) .and. allocated(rhs%layer_ )) call_assert(allocated(lhs%intercept_) .and. allocated(rhs%intercept_)) call_assert(allocated(lhs%slope_ ) .and. allocated(rhs%slope_ )) - call_assert(size(lhs%intercept_) == size(rhs%intercept_)) - call_assert(size(lhs%slope_ ) == size(rhs%slope_ )) + call_julienne_assert(size(lhs%intercept_) .equalsExpected. size(rhs%intercept_)) + call_julienne_assert(size(lhs%slope_ ) .equalsExpected. size(rhs%slope_ )) lhs_equals_rhs = & lhs%layer_ == rhs%layer_ .and. & diff --git a/test/asymmetric_network_test_m.F90 b/test/asymmetric_network_test_m.F90 index c1bce3729..362917c0e 100644 --- a/test/asymmetric_network_test_m.F90 +++ b/test/asymmetric_network_test_m.F90 @@ -1,25 +1,25 @@ ! Copyright (c), The Regents of the University of California ! Terms of use are as specified in LICENSE.txt -#include "assert_macros.h" +#include "julienne-assert-macros.h" module asymmetric_network_test_m !! Define tests in which the desired output depends asymmetrically on the inputs ! External dependencies - use assert_m use julienne_m, only : & - operator(.approximates.) & + call_julienne_assert_ & ,operator(.all.) & ,operator(.also.) & + ,operator(.approximates.) & ,operator(.equalsExpected.) & ,operator(.within.) & + ,string_t & + ,test_description_substring & ,test_description_t & ,test_diagnosis_t & - ,test_t & ,test_result_t & - ,test_description_substring & - ,string_t + ,test_t ! Internal dependencies use fiats_m, only : neural_network_t, tensor_t @@ -47,15 +47,15 @@ function results() result(test_results) type(test_description_t), allocatable :: test_descriptions(:) test_descriptions = [ & - test_description_t("matching the truth table for XOR-and-2nd-input gate", check_xor_and_2nd_input_truth_table) & + test_description_t("learning the truth table for XOR-AND-the-2nd-input logic", xor_and_2nd_input_truth_table) & ] - associate(substring_in_subject => index(subject(), test_description_substring) /= 0) - associate(substring_in_test_diagnosis => test_descriptions%contains_text(test_description_substring)) - associate(matching_descriptions => pack(test_descriptions, substring_in_subject .or. substring_in_test_diagnosis)) - test_results = matching_descriptions%run() - end associate - end associate + 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 xor_and_2nd_input_network() result(neural_network) @@ -72,7 +72,7 @@ function xor_and_2nd_input_network() result(neural_network) allocate(biases(n_max, layers)) l = 1 - call_assert(n(l-1)==2) + call_julienne_assert(n(l-1) .equalsExpected. 2) j = 1 weights(j,1:n(l-1),l) = [1,0] j = 2 @@ -82,10 +82,10 @@ function xor_and_2nd_input_network() result(neural_network) j = 4 weights(j,1:n(l-1),l) = [0,1] biases(1:n(l),l) = [0., -1.99, 0., 0.] - call_assert(j == n(l)) + call_julienne_assert(j .equalsExpected. n(l)) l = 2 - call_assert(n(l-1)==4) + call_julienne_assert(n(l-1) .equalsExpected. 4) j = 1 weights(j,1:n(l-1),l) = [0,0,0,0] j = 2 @@ -95,21 +95,20 @@ function xor_and_2nd_input_network() result(neural_network) j = 4 weights(j,1:n(l-1),l) = [0,0,0,1] biases(1:n(l),l) = [0,0,0,0] - call_assert(j == n(l)) - + call_julienne_assert(j .equalsExpected. n(l)) l = 3 - call_assert(n(l-1)==4) + call_julienne_assert(n(l-1) .equalsExpected. 4) j = 1 weights(j,1:n(l-1),l) = [0,1,0,1] biases(1:n(l),l) = [-1] - call_assert(j == n(l)) + call_julienne_assert(j .equalsExpected. n(l)) neural_network = neural_network_t(metadata, weights, biases, nodes = n) end function - function check_xor_and_2nd_input_truth_table() result(test_diagnosis) + function xor_and_2nd_input_truth_table() result(test_diagnosis) type(test_diagnosis_t) test_diagnosis type(neural_network_t) asymmetric real, parameter :: tolerance = 1.E-08, false = 0., true = 1. diff --git a/test/hyperparameters_test_m.F90 b/test/hyperparameters_test_m.F90 index 52c6c7fa5..a6ce491ec 100644 --- a/test/hyperparameters_test_m.F90 +++ b/test/hyperparameters_test_m.F90 @@ -1,5 +1,6 @@ ! 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 @@ -7,11 +8,11 @@ module hyperparameters_test_m use fiats_m, only : hyperparameters_t use julienne_m, only : & string_t & + ,test_description_substring & + ,test_description_t & ,test_diagnosis_t & - ,test_t & ,test_result_t & - ,test_description_t & - ,test_description_substring + ,test_t ! Internal dependencies use hyperparameters_m, only : hyperparameters_t @@ -60,7 +61,7 @@ function write_then_read_hyperparameters() result(test_diagnosis) associate(hyperparameters => hyperparameters_t(mini_batches=5, learning_rate=1., optimizer = "stochastic gradient descent")) associate(from_json => hyperparameters_t(hyperparameters%to_json())) #endif - test_diagnosis = test_diagnosis_t(test_passed = hyperparameters == from_json, diagnostics_string="hyperparameters /= from_json") + test_diagnosis = test_diagnosis_t(hyperparameters == from_json, diagnostics_string="hyperparameters /= from_json") #ifndef _CRAYFTN end associate end associate diff --git a/test/main.F90 b/test/main.F90 index 1de09298c..a4d66f5f3 100644 --- a/test/main.F90 +++ b/test/main.F90 @@ -64,10 +64,14 @@ program main print * print *,"Test suite execution time: ",t_finish - t_start print * - print '(*(a,:,g0))',"_________ In total, ",passes, " of ",tests, " tests pass. ", skips, " tests were skipped. _________" -#ifdef MULTI_IMAGE_SUPPORT - sync all +#if HAVE_MULTI_IMAGE_SUPPORT + if (this_image()==1) then +#endif + print * + print '(*(a,:,g0))', "_________ In total, ",passes," of ",tests, " tests pass. ", skips, " tests were skipped. _________" +#if HAVE_MULTI_IMAGE_SUPPORT + end if #endif print * - if (passes + skips/=tests) error stop "-------- One or more tests failed. See the above report. ---------" + if (passes + skips /= tests) error stop "Some executed tests failed." end program diff --git a/test/metadata_test_m.F90 b/test/metadata_test_m.F90 index 6bb837a8d..fe922c8da 100644 --- a/test/metadata_test_m.F90 +++ b/test/metadata_test_m.F90 @@ -8,10 +8,10 @@ module metadata_test_m use julienne_m, only : & string_t & ,test_diagnosis_t & - ,test_t & - ,test_result_t & ,test_description_t & - ,test_description_substring + ,test_description_substring & + ,test_result_t & + ,test_t ! Internal dependencies use metadata_m, only : metadata_t @@ -74,7 +74,7 @@ function write_then_read_metadata() result(test_diagnosis) ) associate(from_json => metadata_t(metadata%to_json())) #endif - test_diagnosis = test_diagnosis_t(test_passed = metadata == from_json, diagnostics_string="metadata /= from_json") + test_diagnosis = test_diagnosis_t(metadata == from_json, "metadata /= from_json") #ifndef _CRAYFTN end associate end associate diff --git a/test/network_configuration_test_m.F90 b/test/network_configuration_test_m.F90 index 483f5a0c2..79b23d415 100644 --- a/test/network_configuration_test_m.F90 +++ b/test/network_configuration_test_m.F90 @@ -6,13 +6,12 @@ module network_configuration_test_m ! External dependencies use fiats_m, only : network_configuration_t use julienne_m, only : & - file_t & - ,string_t & + string_t & + ,test_description_t & + ,test_description_substring & ,test_diagnosis_t & - ,test_t & ,test_result_t & - ,test_description_t & - ,test_description_substring + ,test_t ! Internal dependencies use network_configuration_m, only : network_configuration_t diff --git a/test/neural_network_test_m.F90 b/test/neural_network_test_m.F90 index ab9e4155b..f39ed6b82 100644 --- a/test/neural_network_test_m.F90 +++ b/test/neural_network_test_m.F90 @@ -12,11 +12,11 @@ module neural_network_test_m ,operator(.approximates.) & ,operator(.within.) & ,string_t & + ,test_description_substring & + ,test_description_t & ,test_diagnosis_t & - ,test_t & ,test_result_t & - ,test_description_t & - ,test_description_substring + ,test_t& ! Internal dependencies use fiats_m, only : neural_network_t, tensor_t, metadata_t @@ -176,22 +176,21 @@ function multi_hidden_layer_net_to_from_json() result(test_diagnosis) neural_network = distinct_parameters() json_file = neural_network%to_json() from_json = neural_network_t(json_file) - test_diagnosis = test_diagnosis_t(test_passed = neural_network == from_json, diagnostics_string= "neural_network == from_json") + test_diagnosis = test_diagnosis_t(neural_network == from_json, "neural_network /= from_json") end function function varying_width_net_to_from_json() result(test_diagnosis) - type(test_diagnosis_t) test_diagnosis + type (test_diagnosis_t) test_diagnosis associate(neural_network => varying_width()) associate(from_json => neural_network_t( neural_network%to_json() )) - test_diagnosis = test_diagnosis_t(test_passed = neural_network == from_json, diagnostics_string= "neural_network == from_json") + test_diagnosis = test_diagnosis_t(neural_network == from_json, "neural_network /= from_json") end associate end associate end function function infer_with_varying_width_net() result(test_diagnosis) type(test_diagnosis_t) test_diagnosis - logical test_passes type(neural_network_t) neural_network type(tensor_t) inputs, outputs real, parameter :: tolerance = 1.E-08 @@ -199,12 +198,11 @@ function infer_with_varying_width_net() result(test_diagnosis) neural_network = decrement_split_combine_increment() inputs = tensor_t([1.1, 2.7]) outputs = neural_network%infer(inputs) - test_diagnosis = .all. (inputs%values() .approximates. outputs%values() .within. tolerance) + test_diagnosis = .all. (inputs%values() .approximates. outputs%values() .within. tolerance) end function function double_precision_inference() result(test_diagnosis) type(test_diagnosis_t) test_diagnosis - logical test_passes type(neural_network_t(double_precision)) neural_network type(tensor_t(double_precision)) inputs, outputs double precision, parameter :: tolerance = 1.D-08 @@ -212,7 +210,7 @@ function double_precision_inference() result(test_diagnosis) neural_network = double_precision_network() inputs = tensor_t([1.1D0, 2.7D0]) outputs = neural_network%infer(inputs) - test_diagnosis = .all. (inputs%values() .approximates. outputs%values() .within. tolerance) + test_diagnosis = .all. (inputs%values() .approximates. outputs%values() .within. tolerance) end function function elemental_infer_with_1_hidden_layer_xor_net() result(test_diagnosis) @@ -252,9 +250,9 @@ function elemental_infer_with_2_hidden_layer_xor_net() result(test_diagnosis) truth_table = neural_network%infer(array_of_inputs) end associate test_diagnosis = & - .all. (truth_table(1)%values() .approximates. (false) .within. tolerance) & - .also. (.all. (truth_table(2)%values() .approximates. ( true) .within. tolerance)) & - .also. (.all. (truth_table(3)%values() .approximates. ( true) .within. tolerance)) & + (.all. (truth_table(1)%values() .approximates. (false) .within. tolerance)) & + .also. (.all. (truth_table(2)%values() .approximates. (true ) .within. tolerance)) & + .also. (.all. (truth_table(3)%values() .approximates. (true ) .within. tolerance)) & .also. (.all. (truth_table(4)%values() .approximates. (false) .within. tolerance)) end block end function diff --git a/test/tensor_map_test_m.F90 b/test/tensor_map_test_m.F90 index 0bff41c4d..a46bd7559 100644 --- a/test/tensor_map_test_m.F90 +++ b/test/tensor_map_test_m.F90 @@ -5,7 +5,8 @@ module tensor_map_test_m ! External dependencies use julienne_m, only : & - operator(.all.) & + file_t + ,operator(.all.) & ,operator(.approximates.) & ,operator(.within.) & ,string_t & @@ -14,7 +15,6 @@ module tensor_map_test_m ,test_diagnosis_t & ,test_result_t & ,test_t & - ,file_t use fiats_m, only : tensor_map_t, tensor_t ! Internal dependencies @@ -81,12 +81,12 @@ function map_to_from_training_range() result(test_diagnosis) tensor_map = tensor_map_t(layer="output", minima=[-4., 0., 1., -1.], maxima=[0., 2., 5., 1.]) tensor = tensor_t([-2., 0., 5., 0.]) round_trip = tensor_map%map_from_training_range(tensor_map%map_to_training_range(tensor)) - test_diagnosis = .all. (tensor%values() .approximates. round_trip%values() .within. tolerance) + test_diagnosis = .all. (tensor%values() .approximates. round_trip%values() .within. tolerance) #else associate(tensor_map => tensor_map_t(layer="output", minima=[-4., 0., 1., -1.], maxima=[0., 2., 5., 1.])) associate(tensor => tensor_t([-2., 0., 5., 0.])) associate(round_trip => tensor_map%map_from_training_range(tensor_map%map_to_training_range(tensor))) - test_diagnosis = .all. (tensor%values() .approximates. round_trip%values() .within. tolerance) + test_diagnosis = .all. (tensor%values() .approximates. round_trip%values() .within. tolerance) end associate end associate end associate diff --git a/test/tensor_names_test.f90 b/test/tensor_names_test.f90 index 9c6b3d773..f245c1b4a 100644 --- a/test/tensor_names_test.f90 +++ b/test/tensor_names_test.f90 @@ -6,12 +6,12 @@ module tensor_names_test_m ! External dependencies use fiats_m, only : tensor_names_t use julienne_m, only : & - test_description_t & + string_t & + ,test_description_t & ,test_description_substring & ,test_diagnosis_t & ,test_result_t & - ,test_t & - ,string_t + ,test_t ! Internal dependencies use tensor_names_m, only : tensor_names_t @@ -58,7 +58,7 @@ function write_then_read_tensor_names() result(test_diagnosis) outputs = [string_t("qc"), string_t("qv")] & ) ) associate(from_json => tensor_names_t(from_components%to_json())) - test_diagnosis = test_diagnosis_t(test_passed = from_components == from_json, diagnostics_string="from_components /= from_json") + test_diagnosis = test_diagnosis_t(from_components == from_json, "from_components /= from_json") end associate end associate end function diff --git a/test/tensor_test_m.f90 b/test/tensor_test_m.f90 index 56a86ab45..75f4331ce 100644 --- a/test/tensor_test_m.f90 +++ b/test/tensor_test_m.f90 @@ -9,14 +9,14 @@ module tensor_test_m operator(.all.) & ,operator(.also.) & ,operator(.approximates.) & + ,operator(.equalsExpected.) & ,operator(.within.) & + ,string_t & ,test_description_substring & ,test_description_t & ,test_diagnosis_t & ,test_result_t & - ,test_t & - ,string_t & - ,file_t + ,test_t ! Internal dependencies use fiats_m, only : tensor_t @@ -63,14 +63,9 @@ function double_precision_construction() result(test_diagnosis) tensor = tensor_t(values) ! this will fail to compile if no double_precision constructor exists and the associate(tensor_values => tensor%values()) - associate(kinds => kind(tensor_values)) - test_diagnosis = & - test_diagnosis_t( & - test_passed = kinds == double_precision & - ,diagnostics_string = "kind "// string_t(kinds) // " is not double precision" & - ) & - .also. (.all. (tensor_values .approximates. values .within. tolerance)) - end associate + test_diagnosis = & + (kind(tensor_values) .equalsExpected. double_precision) & + .also. (.all. (tensor_values .approximates. values .within. tolerance)) end associate end function diff --git a/test/trainable_network_test_m.F90 b/test/trainable_network_test_m.F90 index 3adc05d60..2d2c9d442 100644 --- a/test/trainable_network_test_m.F90 +++ b/test/trainable_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 -#include "assert_macros.h" +#include "julienne-assert-macros.h" module trainable_network_test_m !! Define inference tests and procedures required for reporting results @@ -9,16 +9,17 @@ module trainable_network_test_m ! External dependencies use assert_m use julienne_m, only : & - operator(.all.) & + bin_t & + ,call_julienne_assert_ & + ,operator(.all.) & ,operator(.approximates.) & ,operator(.within.) & - ,bin_t & + ,string_t & ,test_description_substring & ,test_description_t & ,test_diagnosis_t & ,test_result_t & - ,test_t & - ,string_t + ,test_t ! Internal dependencies use fiats_m, only : trainable_network_t, neural_network_t, tensor_t, input_output_pair_t, mini_batch_t, shuffle @@ -64,6 +65,7 @@ function results() result(test_results) ,test_description_t("learning OR from symmetric OR-gate data and random initial weights", learn_or_from_random_weights) & ,test_description_t("learning XOR from symmetric XOR-gate data and random initial weights", learn_xor_from_random_weights) & ] + associate( & substring_in_subject => index(subject(), test_description_substring) /= 0, & substring_in_description => test_descriptions%contains_text(string_t(test_description_substring)) & @@ -84,7 +86,7 @@ subroutine print_truth_table(gate_name, gate_function_ptr, test_inputs, actual_o type(tensor_t) expected_outputs integer i - call_assert(size(test_inputs) == size(actual_outputs)) + call_julienne_assert(size(test_inputs) .equalsExpected. size(actual_outputs)) print *,"_______" // gate_name // "_______" @@ -144,6 +146,7 @@ function learn_and_from_skewed_data() result(test_diagnosis) call random_number(harvest) harvest = 2.*(harvest - 0.5) ! skew toward more input values being true + ! The following temporary copies are required by gfortran bug 100650 and possibly 49324 ! See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=100650 and https://gcc.gnu.org/bugzilla/show_bug.cgi?id=49324 tmp = [([(tensor_t(merge(true, false, harvest(:,batch,iter) < 0.5E0)), batch=1, mini_batch_size)], iter=1, num_iterations)] @@ -173,7 +176,6 @@ elemental function and(inputs_object) result(expected_outputs_object) end function function learn_not_and_from_skewed_data() result(test_diagnosis) - !! NOT AND truth table: [(true,true), (false,true), (true,false), (false,false)] -> [false, true, true, true] type(test_diagnosis_t) test_diagnosis type(mini_batch_t), allocatable :: mini_batches(:) type(tensor_t), allocatable :: training_inputs(:,:), tmp(:), test_inputs(:) @@ -218,7 +220,6 @@ function not_and(inputs) result(expected_outputs) end function function learn_or_from_random_weights() result(test_diagnosis) - !! OR truth table: [(true,true), (false,true), (true,false), (false,false)] -> [ true, true, true, false] type(test_diagnosis_t) test_diagnosis type(mini_batch_t), allocatable :: mini_batches(:) type(tensor_t), allocatable :: training_inputs(:,:), test_inputs(:), actual_outputs(:) @@ -229,6 +230,8 @@ function learn_or_from_random_weights() result(test_diagnosis) integer, parameter :: num_inputs=2, mini_batch_size = 1, num_iterations=50000 integer batch, iter, i + test_diagnosis = test_diagnosis_t(.false.,"") + allocate(harvest(num_inputs, mini_batch_size, num_iterations)) call random_number(harvest) @@ -264,7 +267,6 @@ pure function or(inputs) result(expected_outputs) end function function learn_xor_from_random_weights() result(test_diagnosis) - !! XOR truth table: [(true,true), (false,true), (true,false), (false,false)] -> [false, true, true, false] type(test_diagnosis_t) test_diagnosis type(mini_batch_t), allocatable :: mini_batches(:) type(tensor_t), allocatable, dimension(:,:) :: training_inputs, training_outputs @@ -272,6 +274,7 @@ function learn_xor_from_random_weights() result(test_diagnosis) type(trainable_network_t) trainable_network 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. @@ -283,7 +286,7 @@ function learn_xor_from_random_weights() result(test_diagnosis) !! Depending on where in the random-number sequence the weights start, this test can pass for lower !! numbers of iterations, e.g., 400000. Using more iterations gives more robust convergence. #endif - integer batch, iter + integer batch, iter, i allocate(harvest(num_inputs, mini_batch_size, num_iterations)) call random_number(harvest) @@ -295,6 +298,8 @@ function learn_xor_from_random_weights() result(test_diagnosis) training_outputs(batch, iter) = local_xor(training_inputs(batch, iter)) end do + test_diagnosis = test_diagnosis_t(.false., "") + allocate(mini_batches(size(training_inputs,1)*num_iterations)) do concurrent(iter=1:num_iterations) mini_batches(iter) = mini_batch_t(input_output_pair_t(training_inputs(:,iter), training_outputs(:,iter))) @@ -305,13 +310,9 @@ function learn_xor_from_random_weights() result(test_diagnosis) 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_outputs = [(local_xor(test_inputs(i)), i=1, size(test_inputs))] - actual_outputs = trainable_network%infer(test_inputs) - test_diagnosis = .all. [(actual_outputs(i)%values() .approximates. expected_outputs(i)%values() .within. tolerance, i=1,size(actual_outputs))] - end block + expected_outputs = [(local_xor(test_inputs(i)), i=1, size(test_inputs))] + actual_outputs = trainable_network%infer(test_inputs) + test_diagnosis = .all. [(actual_outputs(i)%values() .approximates. expected_outputs(i)%values() .within. tolerance, i=1,size(actual_outputs))] contains @@ -368,7 +369,7 @@ function preserve_identity() result(test_diagnosis) associate(num_inputs => trainable_network%num_inputs(), num_outputs => trainable_network%num_outputs()) - call_assert(num_inputs == num_outputs) + call_julienne_assert(num_inputs .equalsExpected. num_outputs) #ifdef _CRAYFTN allocate(inputs(num_pairs)) do i = 1, num_pairs @@ -427,7 +428,7 @@ function learn_identity() result(test_diagnosis) associate(num_inputs => trainable_network%num_inputs(), num_outputs => trainable_network%num_outputs()) - call_assert(num_inputs == num_outputs) + call_julienne_assert(num_inputs .equalsExpected. num_outputs) #ifdef _CRAYFTN allocate(inputs(num_pairs)) do i = 1, num_pairs diff --git a/test/training_configuration_test_m.F90 b/test/training_configuration_test_m.F90 index 808aa12b0..a0c91ffeb 100644 --- a/test/training_configuration_test_m.F90 +++ b/test/training_configuration_test_m.F90 @@ -4,14 +4,15 @@ module training_configuration_test_m !! Test training_configuration_t object I/O and construction ! External dependencies + use fiats_m, only : training_configuration_t, hyperparameters_t, network_configuration_t, tensor_names_t use julienne_m, only : & - file_t & - ,string_t & - ,test_diagnosis_t & - ,test_description_substring & - ,test_description_t & - ,test_result_t & - ,test_t + file_t & + ,string_t & + ,test_description_t & + ,test_description_substring & + ,test_diagnosis_t & + ,test_result_t & + ,test_t ! Internal dependencies use fiats_m, only : training_configuration_t, hyperparameters_t, network_configuration_t, tensor_names_t @@ -67,10 +68,7 @@ function construct_convert_to_from_json() result(test_diagnosis) )) associate(from_json => training_configuration_t(file_t(training_configuration%to_json()))) #endif - test_diagnosis = test_diagnosis_t( & - test_passed = training_configuration == from_json & - ,diagnostics_string = "training_configuration /= from_json" & - ) + test_diagnosis = test_diagnosis_t(training_configuration == from_json, "training_configuration /= from_json") #ifndef _CRAYFTN end associate end associate diff --git a/test/training_data_files_test_m.F90 b/test/training_data_files_test_m.F90 index 6c518ab3e..89c525b4c 100644 --- a/test/training_data_files_test_m.F90 +++ b/test/training_data_files_test_m.F90 @@ -4,11 +4,11 @@ module training_data_files_test_m !! Test training_data_files_t object I/O and construction ! External dependencies + use fiats_m, only : training_data_files_t use julienne_m, only : & - file_t & - ,string_t & - ,test_description_substring & + string_t & ,test_description_t & + ,test_description_substring & ,test_diagnosis_t & ,test_result_t & ,test_t From 5533d0d19bb47e59212f13dbcb9106c5dbcb0b7c Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Fri, 8 Aug 2025 09:42:32 -0700 Subject: [PATCH 2/4] fix(example): add required associate nesting --- example/learn-power-series.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/learn-power-series.F90 b/example/learn-power-series.F90 index 5a4988db7..0695c8da7 100644 --- a/example/learn-power-series.F90 +++ b/example/learn-power-series.F90 @@ -17,7 +17,7 @@ module power_series elemental function y(x_in) result(a) type(tensor_t), intent(in) :: x_in type(tensor_t) a - associate(x => x_in%values()) sufficient_input => (ubound(x,1) .isAtLeast. 7) .also. (lbound(x,1) .isAtMost. 2)) + associate(x => x_in%values()) associate(sufficient_input => (ubound(x,1) .isAtLeast. 7) .also. (lbound(x,1) .isAtMost. 2)) call_julienne_assert(sufficient_input) a = tensor_t([1 + x(1) + (x(1)**2)/2 + (x(1)**3)/6, x(2), x(3), x(4), x(5), x(6)]) From b52d3148ad2cec95a541bd1af3dff5e4962b6861 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Fri, 8 Aug 2025 09:45:32 -0700 Subject: [PATCH 3/4] feat: rm gfortran workaround This commit removes the test-suite workarounds for a Fortran 2008 feature that was added in gfortran 14.3 and later. --- test/hyperparameters_test_m.F90 | 2 +- test/neural_network_test_m.F90 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/hyperparameters_test_m.F90 b/test/hyperparameters_test_m.F90 index a6ce491ec..93f69a670 100644 --- a/test/hyperparameters_test_m.F90 +++ b/test/hyperparameters_test_m.F90 @@ -12,7 +12,7 @@ module hyperparameters_test_m ,test_description_t & ,test_diagnosis_t & ,test_result_t & - ,test_t + ,test_t ! Internal dependencies use hyperparameters_m, only : hyperparameters_t diff --git a/test/neural_network_test_m.F90 b/test/neural_network_test_m.F90 index f39ed6b82..7b1df63fd 100644 --- a/test/neural_network_test_m.F90 +++ b/test/neural_network_test_m.F90 @@ -16,7 +16,7 @@ module neural_network_test_m ,test_description_t & ,test_diagnosis_t & ,test_result_t & - ,test_t& + ,test_t ! Internal dependencies use fiats_m, only : neural_network_t, tensor_t, metadata_t From 7cc48ac574e5f83835bafb01ec66adb793bf8320 Mon Sep 17 00:00:00 2001 From: Damian Rouson Date: Fri, 8 Aug 2025 09:47:03 -0700 Subject: [PATCH 4/4] fix(tensor_map_test): typo --- test/tensor_map_test_m.F90 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/tensor_map_test_m.F90 b/test/tensor_map_test_m.F90 index a46bd7559..221e7a0d6 100644 --- a/test/tensor_map_test_m.F90 +++ b/test/tensor_map_test_m.F90 @@ -5,7 +5,7 @@ module tensor_map_test_m ! External dependencies use julienne_m, only : & - file_t + file_t & ,operator(.all.) & ,operator(.approximates.) & ,operator(.within.) & @@ -14,7 +14,7 @@ module tensor_map_test_m ,test_description_t & ,test_diagnosis_t & ,test_result_t & - ,test_t & + ,test_t use fiats_m, only : tensor_map_t, tensor_t ! Internal dependencies