Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
add_library(tuff tuff.f90)

foreach(execid
input1d_layer
input2d_layer
Expand Down Expand Up @@ -27,7 +29,7 @@ foreach(execid
metrics
)
add_executable(test_${execid} test_${execid}.f90)
target_link_libraries(test_${execid} PRIVATE neural-fortran ${LIBS})
target_link_libraries(test_${execid} PRIVATE tuff neural-fortran ${LIBS})

add_test(NAME test_${execid} COMMAND test_${execid})
endforeach()
69 changes: 17 additions & 52 deletions test/test_dense_layer.f90
Original file line number Diff line number Diff line change
@@ -1,58 +1,23 @@
program test_dense_layer
use iso_fortran_env, only: stderr => error_unit
use nf, only: dense, layer
use nf_activation, only: relu
use nf, only: dense, layer, relu
use tuff, only: test, test_result
implicit none
type(layer) :: layer1, layer2
logical :: ok = .true.
type(layer) :: layer1, layer2, layer3
type(test_result) :: tests

layer1 = dense(10)

if (.not. layer1 % name == 'dense') then
ok = .false.
write(stderr, '(a)') 'dense layer has its name set correctly.. failed'
end if

if (.not. all(layer1 % layer_shape == [10])) then
ok = .false.
write(stderr, '(a)') 'dense layer is created with requested size.. failed'
end if

if (layer1 % initialized) then
ok = .false.
write(stderr, '(a)') 'dense layer should not be marked as initialized yet.. failed'
end if

if (.not. layer1 % activation == 'sigmoid') then
ok = .false.
write(stderr, '(a)') 'dense layer is defaults to sigmoid activation.. failed'
end if

layer1 = dense(10, activation=relu())

if (.not. layer1 % activation == 'relu') then
ok = .false.
write(stderr, '(a)') 'dense layer is created with the specified activation.. failed'
end if

layer2 = dense(20)
call layer2 % init(layer1)

if (.not. layer2 % initialized) then
ok = .false.
write(stderr, '(a)') 'dense layer should now be marked as initialized.. failed'
end if

if (.not. all(layer2 % input_layer_shape == [10])) then
ok = .false.
write(stderr, '(a)') 'dense layer should have a correct input layer shape.. failed'
end if

if (ok) then
print '(a)', 'test_dense_layer: All tests passed.'
else
write(stderr, '(a)') 'test_dense_layer: One or more tests failed.'
stop 1
end if
layer2 = dense(10, activation=relu())
layer3 = dense(20)
call layer3 % init(layer1)

tests = test("test_dense_layer", [ &
test("layer name is set", layer1 % name == 'dense'), &
test("layer shape is correct", all(layer1 % layer_shape == [10])), &
test("layer is initialized", layer3 % initialized), &
test("layer's default activation is sigmoid", layer1 % activation == 'sigmoid'), &
test("user set activation works", layer2 % activation == 'relu'), &
test("layer initialized after init", layer3 % initialized), &
test("layer input shape is set after init", all(layer3 % input_layer_shape == [10])) &
])

end program test_dense_layer
76 changes: 38 additions & 38 deletions test/test_dense_network.f90
Original file line number Diff line number Diff line change
@@ -1,33 +1,40 @@
program test_dense_network
use iso_fortran_env, only: stderr => error_unit
use nf, only: dense, input, network
use nf_optimizers, only: sgd
use nf, only: dense, input, network, sgd
use tuff, only: test, test_result
implicit none
type(network) :: net
logical :: ok = .true.
type(test_result) :: tests

! Minimal 2-layer network
net = network([ &
input(1), &
dense(1) &
])

if (.not. size(net % layers) == 2) then
write(stderr, '(a)') 'dense network should have 2 layers.. failed'
ok = .false.
end if
tests = test("test_dense_network", [ &
test("network has 2 layers", size(net % layers) == 2), &
test("network predicts 0.5 for input 0", all(net % predict([0.]) == 0.5)), &
test(simple_training), &
test(larger_network_size) &
])

if (.not. all(net % predict([0.]) == 0.5)) then
write(stderr, '(a)') &
'dense network should output exactly 0.5 for input 0.. failed'
ok = .false.
end if
contains

training: block
type(test_result) function simple_training() result(res)
real :: x(1), y(1)
real :: tolerance = 1e-3
integer :: n
integer, parameter :: num_iterations = 1000
integer, parameter :: num_iterations = 1000
type(network) :: net

res % name = 'simple training'

! Minimal 2-layer network
net = network([ &
input(1), &
dense(1) &
])

x = [0.123]
y = [0.765]
Expand All @@ -39,32 +46,25 @@ program test_dense_network
if (all(abs(net % predict(x) - y) < tolerance)) exit
end do

if (.not. n <= num_iterations) then
write(stderr, '(a)') &
'dense network should converge in simple training.. failed'
ok = .false.
end if
res % ok = n <= num_iterations

end block training
end function simple_training

! A bit larger multi-layer network
net = network([ &
input(784), &
dense(30), &
dense(20), &
dense(10) &
])
type(test_result) function larger_network_size() result(res)
type(network) :: net

res % name = 'larger network training'

! A bit larger multi-layer network
net = network([ &
input(784), &
dense(30), &
dense(20), &
dense(10) &
])

if (.not. size(net % layers) == 4) then
write(stderr, '(a)') 'dense network should have 4 layers.. failed'
ok = .false.
end if
res % ok = size(net % layers) == 4

if (ok) then
print '(a)', 'test_dense_network: All tests passed.'
else
write(stderr, '(a)') 'test_dense_network: One or more tests failed.'
stop 1
end if
end function larger_network_size

end program test_dense_network
end program test_dense_network
73 changes: 73 additions & 0 deletions test/tuff.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
module tuff
! Testing Unframework for Fortran (TUFF)
use iso_fortran_env, only: stderr => error_unit, stdout => output_unit
implicit none

private
public :: test, test_result

type :: test_result
character(:), allocatable :: name
logical :: ok = .true.
real :: elapsed = 0.
end type test_result

interface test
module procedure test_logical, test_func, test_array
end interface test

abstract interface
function func() result(res)
import :: test_result
type(test_result) :: res
end function func
end interface

contains

type(test_result) function test_logical(name, cond) result(res)
! Test a single logical expression.
character(*), intent(in) :: name
logical, intent(in) :: cond
res % name = name
res % ok = .true.
res % elapsed = 0.
if (.not. cond) then
write(stderr, '(a)') 'Test ' // trim(name) // ' failed.'
res % ok = .false.
end if
end function test_logical


type(test_result) function test_func(f) result(res)
! Test a user-provided function f that returns a test_result.
! f is responsible for setting the test name and the ok field.
procedure(func) :: f
real :: t1, t2
res % name = ''
call cpu_time(t1)
res = f()
call cpu_time(t2)
res % elapsed = t2 - t1
if (len_trim(res % name) == 0) res % name = 'Anonymous test'
if (.not. res % ok) then
write(stderr, '(a, f6.3)') 'Test failed: ' // trim(res % name)
end if
end function test_func


type(test_result) function test_array(name, tests) result(suite)
! Test a suite of tests, each of which is a test_result.
character(*), intent(in) :: name
type(test_result), intent(in) :: tests(:)
suite % ok = all(tests % ok)
suite % elapsed = sum(tests % elapsed)
if (suite % ok) then
write(stdout, '(a)') trim(name) // ": All tests passed."
else
write(stderr, '(i0,a,i0,a)') count(.not. tests % ok), '/', size(tests), &
" tests failed in suite: " // trim(name)
end if
end function test_array

end module tuff