diff --git a/test/specialfunctions/CMakeLists.txt b/test/specialfunctions/CMakeLists.txt index 536c36e12..60eefea71 100644 --- a/test/specialfunctions/CMakeLists.txt +++ b/test/specialfunctions/CMakeLists.txt @@ -9,4 +9,6 @@ set(fppFiles fypp_f90("${fyppFlags}" "${fppFiles}" outFiles) ADDTEST(specialfunctions_gamma) -ADDTEST(specialfunctions_activations) \ No newline at end of file +ADDTEST(specialfunctions_activations) + +target_link_libraries(specialfunctions_activations PRIVATE stdlib_io) \ No newline at end of file diff --git a/test/specialfunctions/data/elu_derivative.npy b/test/specialfunctions/data/elu_derivative.npy new file mode 100644 index 000000000..f15b52c38 Binary files /dev/null and b/test/specialfunctions/data/elu_derivative.npy differ diff --git a/test/specialfunctions/data/elu_input.npy b/test/specialfunctions/data/elu_input.npy new file mode 100644 index 000000000..f6001e377 Binary files /dev/null and b/test/specialfunctions/data/elu_input.npy differ diff --git a/test/specialfunctions/data/elu_output.npy b/test/specialfunctions/data/elu_output.npy new file mode 100644 index 000000000..a983bb0f8 Binary files /dev/null and b/test/specialfunctions/data/elu_output.npy differ diff --git a/test/specialfunctions/data/relu_derivative.npy b/test/specialfunctions/data/relu_derivative.npy new file mode 100644 index 000000000..13e7f0582 Binary files /dev/null and b/test/specialfunctions/data/relu_derivative.npy differ diff --git a/test/specialfunctions/data/relu_input.npy b/test/specialfunctions/data/relu_input.npy new file mode 100644 index 000000000..f6001e377 Binary files /dev/null and b/test/specialfunctions/data/relu_input.npy differ diff --git a/test/specialfunctions/data/relu_output.npy b/test/specialfunctions/data/relu_output.npy new file mode 100644 index 000000000..aab040e5a Binary files /dev/null and b/test/specialfunctions/data/relu_output.npy differ diff --git a/test/specialfunctions/data/selu_derivative.npy b/test/specialfunctions/data/selu_derivative.npy new file mode 100644 index 000000000..14967b85f Binary files /dev/null and b/test/specialfunctions/data/selu_derivative.npy differ diff --git a/test/specialfunctions/data/selu_input.npy b/test/specialfunctions/data/selu_input.npy new file mode 100644 index 000000000..f6001e377 Binary files /dev/null and b/test/specialfunctions/data/selu_input.npy differ diff --git a/test/specialfunctions/data/selu_output.npy b/test/specialfunctions/data/selu_output.npy new file mode 100644 index 000000000..6aec3d35e Binary files /dev/null and b/test/specialfunctions/data/selu_output.npy differ diff --git a/test/specialfunctions/data/sigmoid_derivative.npy b/test/specialfunctions/data/sigmoid_derivative.npy new file mode 100644 index 000000000..08a40a16a Binary files /dev/null and b/test/specialfunctions/data/sigmoid_derivative.npy differ diff --git a/test/specialfunctions/data/sigmoid_input.npy b/test/specialfunctions/data/sigmoid_input.npy new file mode 100644 index 000000000..f6001e377 Binary files /dev/null and b/test/specialfunctions/data/sigmoid_input.npy differ diff --git a/test/specialfunctions/data/sigmoid_output.npy b/test/specialfunctions/data/sigmoid_output.npy new file mode 100644 index 000000000..57061d736 Binary files /dev/null and b/test/specialfunctions/data/sigmoid_output.npy differ diff --git a/test/specialfunctions/data/softplus_derivative.npy b/test/specialfunctions/data/softplus_derivative.npy new file mode 100644 index 000000000..74289d257 Binary files /dev/null and b/test/specialfunctions/data/softplus_derivative.npy differ diff --git a/test/specialfunctions/data/softplus_input.npy b/test/specialfunctions/data/softplus_input.npy new file mode 100644 index 000000000..f6001e377 Binary files /dev/null and b/test/specialfunctions/data/softplus_input.npy differ diff --git a/test/specialfunctions/data/softplus_output.npy b/test/specialfunctions/data/softplus_output.npy new file mode 100644 index 000000000..474ed0142 Binary files /dev/null and b/test/specialfunctions/data/softplus_output.npy differ diff --git a/test/specialfunctions/data/softsign_derivative.npy b/test/specialfunctions/data/softsign_derivative.npy new file mode 100644 index 000000000..b26ff4bb2 Binary files /dev/null and b/test/specialfunctions/data/softsign_derivative.npy differ diff --git a/test/specialfunctions/data/softsign_input.npy b/test/specialfunctions/data/softsign_input.npy new file mode 100644 index 000000000..f6001e377 Binary files /dev/null and b/test/specialfunctions/data/softsign_input.npy differ diff --git a/test/specialfunctions/data/softsign_output.npy b/test/specialfunctions/data/softsign_output.npy new file mode 100644 index 000000000..0cff8674b Binary files /dev/null and b/test/specialfunctions/data/softsign_output.npy differ diff --git a/test/specialfunctions/data/tanh_derivative.npy b/test/specialfunctions/data/tanh_derivative.npy new file mode 100644 index 000000000..4d0be2e26 Binary files /dev/null and b/test/specialfunctions/data/tanh_derivative.npy differ diff --git a/test/specialfunctions/data/tanh_input.npy b/test/specialfunctions/data/tanh_input.npy new file mode 100644 index 000000000..f6001e377 Binary files /dev/null and b/test/specialfunctions/data/tanh_input.npy differ diff --git a/test/specialfunctions/data/tanh_output.npy b/test/specialfunctions/data/tanh_output.npy new file mode 100644 index 000000000..9f057c66c Binary files /dev/null and b/test/specialfunctions/data/tanh_output.npy differ diff --git a/test/specialfunctions/generate_activation_data.py b/test/specialfunctions/generate_activation_data.py new file mode 100644 index 000000000..92d50d2db --- /dev/null +++ b/test/specialfunctions/generate_activation_data.py @@ -0,0 +1,47 @@ +# --- python script: generate_activation_data.py --- +# place this in test/specialfunctions/ + +import numpy as np +import torch +import os + +# create a directory to store the reference data +data_dir = 'data' +if not os.path.exists(data_dir): + os.makedirs(data_dir) + +# define a range of inputs to test +x_np = np.linspace(-10.0, 10.0, num=1001, dtype=np.float64) +x_torch = torch.tensor(x_np, requires_grad=True) + +# map the activation functions available in stdlib to their PyTorch equivalents +activations = { + "sigmoid": torch.sigmoid, + "tanh": torch.tanh, + "relu": torch.relu, + "elu": torch.nn.functional.elu, + "selu": torch.nn.functional.selu, + "softplus": torch.nn.functional.softplus, + "softsign": torch.nn.functional.softsign, +} + +print("Generating reference data...") + +for name, func in activations.items(): + # Reset gradients for each function + if x_torch.grad is not None: + x_torch.grad.zero_() + + # calculate the function and its derivative + y = func(x_torch) + y.sum().backward() + derivative = x_torch.grad.numpy() + + # save input, output, and derivative as .npy files + np.save(f'{data_dir}/{name}_input.npy', x_np) + np.save(f'{data_dir}/{name}_output.npy', y.detach().numpy()) + np.save(f'{data_dir}/{name}_derivative.npy', derivative) + + print(f"Generated data for: {name}") + +print("All reference data generated successfully.") \ No newline at end of file diff --git a/test/specialfunctions/test_specialfunctions_activations.fypp b/test/specialfunctions/test_specialfunctions_activations.fypp index 7ff05d3bc..24b4641ea 100644 --- a/test/specialfunctions/test_specialfunctions_activations.fypp +++ b/test/specialfunctions/test_specialfunctions_activations.fypp @@ -6,6 +6,7 @@ module test_specialfunctions_activation use stdlib_kinds use stdlib_specialfunctions use stdlib_math, only: linspace + use stdlib_io_npy_load, only: load_npy implicit none private @@ -31,7 +32,8 @@ contains new_unittest("sigmoid", test_sigmoid), & new_unittest("silu" , test_silu), & new_unittest("softmax", test_softmax), & - new_unittest("logsoftmax", test_logsoftmax) & + new_unittest("logsoftmax", test_logsoftmax), & + new_unittest("sigmoid_pytorch", test_sigmoid_pytorch) & ] end subroutine collect_specialfunctions_activation @@ -382,6 +384,29 @@ contains #:endfor end subroutine test_logsoftmax + subroutine test_sigmoid_pytorch(error) + type(error_type), allocatable, intent(out) :: error + + real(dp), allocatable :: x(:), y_ref(:), y_calc(:) + integer :: n + + ! 1. Load the data generated by Python + ! Make sure you ran 'python generate_data.py' in this folder first! + call load_npy('data/sigmoid_input.npy', x) + call load_npy('data/sigmoid_output.npy', y_ref) + + n = size(x) + allocate(y_calc(n)) + + ! 2. Calculate using stdlib + y_calc = sigmoid(x) + + ! 3. Compare + call check(error, norm2(y_calc - y_ref) < 1.0e-9_dp, & + "Sigmoid mismatch against PyTorch reference") + if (allocated(error)) return + + end subroutine test_sigmoid_pytorch end module test_specialfunctions_activation