Skip to content

Commit 1dd6f33

Browse files
authored
Merge pull request #48 from sourceryinstitute/add-bins
Add bin abstraction
2 parents 87030a0 + abc5361 commit 1dd6f33

File tree

6 files changed

+161
-1
lines changed

6 files changed

+161
-1
lines changed

fpm.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@ maintainer = "[email protected]"
66
copyright = "2020-2023 Sourcery Institute"
77

88
[dependencies]
9-
assert = {git = "https://github.com/sourceryinstitute/assert", tag = "1.4.0"}
9+
assert = {git = "https://github.com/sourceryinstitute/assert", tag = "1.5.0"}

src/sourcery/bin_m.f90

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
module bin_m
2+
!! distribute item numbers across bins such that the number of items differs by at most 1 between any two bins
3+
implicit none
4+
5+
private
6+
public :: bin_t
7+
8+
type bin_t
9+
!! encapsulate a range of item numbers associated with a bin
10+
private
11+
integer first_, last_
12+
contains
13+
procedure first
14+
procedure last
15+
end type
16+
17+
interface bin_t
18+
19+
elemental module function construct(num_items, num_bins, bin_number) result(bin)
20+
!! the result is a bin associated with a range of item numbers
21+
integer, intent(in) :: num_items, num_bins, bin_number
22+
type(bin_t) bin
23+
end function
24+
25+
end interface
26+
27+
interface
28+
29+
elemental module function first(self, bin_number) result(first_item_number)
30+
!! the result is the first item number associated with the given bin
31+
implicit none
32+
class(bin_t), intent(in) :: self
33+
integer, intent(in) :: bin_number
34+
integer first_item_number
35+
end function
36+
37+
elemental module function last(self, bin_number) result(last_item_number)
38+
!! the result is the last item number associated with the given bin
39+
implicit none
40+
class(bin_t), intent(in) :: self
41+
integer, intent(in) :: bin_number
42+
integer last_item_number
43+
end function
44+
45+
end interface
46+
47+
end module bin_m

src/sourcery/bin_s.f90

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
submodule(bin_m) bin_s
2+
use assert_m, only : assert, intrinsic_array_t
3+
implicit none
4+
5+
contains
6+
7+
module procedure construct
8+
9+
call assert( num_items>=num_bins, "bin_s(construct): num_items>=num_bins", intrinsic_array_t([num_items,num_bins]))
10+
11+
associate( remainder => mod(num_items, num_bins), items_per_bin => num_items/num_bins)
12+
13+
if (bin_number <= remainder) then
14+
bin%first_ = 1 + (bin_number-1)*(items_per_bin+1)
15+
bin%last_ = bin_number*(items_per_bin+1)
16+
else
17+
bin%first_ = 1 + (remainder-1)*(items_per_bin+1) + 1 + (bin_number-remainder)*items_per_bin
18+
bin%last_ = remainder*(items_per_bin+1) + (bin_number-remainder)*items_per_bin
19+
end if
20+
21+
end associate
22+
23+
end procedure
24+
25+
module procedure first
26+
first_item_number = self%first_
27+
end procedure
28+
29+
module procedure last
30+
last_item_number = self%last_
31+
end procedure
32+
33+
end submodule bin_s

src/sourcery_m.f90

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ module sourcery_m
22
!! export all public entities from every other sourcery module
33
use command_line_m, only : command_line_t
44
use data_partition_m, only : data_partition_t
5+
use bin_m, only : bin_t
56
use formats_m, only : csv, cscv, separated_values
67
use file_m, only : file_t
78
use string_m, only : string_t

test/bin_test.f90

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
module bin_test_m
2+
!! verify data partitioning across bins
3+
use sourcery_m, only : bin_t, test_t, test_result_t
4+
use assert_m, only : assert
5+
implicit none
6+
7+
private
8+
public :: bin_test_t
9+
10+
type, extends(test_t) :: bin_test_t
11+
contains
12+
procedure, nopass :: subject
13+
procedure, nopass :: results
14+
end type
15+
16+
contains
17+
18+
pure function subject() result(specimen)
19+
character(len=:), allocatable :: specimen
20+
specimen = "An array of bin_t objects (bins)"
21+
end function
22+
23+
function results() result(test_results)
24+
type(test_result_t), allocatable :: test_results(:)
25+
character(len=*), parameter :: longest_description = &
26+
"partitioning all item across all bins without item loss"
27+
28+
associate( &
29+
descriptions => &
30+
[ character(len=len(longest_description)) :: &
31+
"partitioning items nearly evenly across bins", &
32+
"partitioning all item across all bins without item loss" &
33+
], &
34+
outcomes => &
35+
[ verify_block_partitioning(), &
36+
verify_all_items_partitioned() &
37+
] &
38+
)
39+
call assert(size(descriptions) == size(outcomes), "bin_test_m(results): size(descriptions) == size(outcomes)")
40+
test_results = test_result_t(descriptions, outcomes)
41+
end associate
42+
43+
end function
44+
45+
function verify_block_partitioning() result(test_passes)
46+
!! Verify that the items are partitioned across bins evenly to within a difference of one item per bin
47+
logical test_passes
48+
49+
type(bin_t), allocatable :: bins(:)
50+
integer, parameter :: n_items=11, n_bins=7
51+
integer b
52+
53+
bins = [( bin_t(num_items=n_items, num_bins=n_bins, bin_number=b), b = 1,n_bins )]
54+
associate(in_bin => [(bins(b)%last(b) - bins(b)%first(b) + 1, b = 1, n_bins)])
55+
associate(remainder => mod(n_items, n_bins), items_per_bin => n_items/n_bins)
56+
test_passes = all([(in_bin(1:remainder) == items_per_bin + 1)]) .and. all([(in_bin(remainder+1:) == items_per_bin)])
57+
end associate
58+
end associate
59+
60+
end function
61+
62+
function verify_all_items_partitioned() result(test_passes)
63+
!! Verify that the number of items in each bin sums to the total number of items
64+
type(bin_t) partition
65+
logical test_passes
66+
67+
type(bin_t), allocatable :: bins(:)
68+
integer, parameter :: n_items=11, n_bins=6
69+
integer b
70+
71+
bins = [( bin_t(num_items=n_items, num_bins=n_bins, bin_number=b), b = 1,n_bins )]
72+
test_passes = sum([(bins(b)%last(b) - bins(b)%first(b) + 1, b = 1, n_bins)]) == n_items
73+
74+
end function
75+
76+
end module bin_test_m

test/main.f90

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
program main
22
use user_defined_collectives_test_m, only : collectives_test_t
33
use data_partition_test_m, only : data_partition_test_t
4+
use bin_test_m, only : bin_test_t
45
use object_m_test_m, only : object_test_t
56
use formats_test_m, only : formats_test_t
67
use test_result_test_m, only : test_result_test_t
@@ -10,6 +11,7 @@ program main
1011

1112
type(collectives_test_t) collectives_test
1213
type(data_partition_test_t) data_partition_test
14+
type(bin_test_t) bin_test
1315
type(formats_test_t) formats_test
1416
type(object_test_t) object_test
1517
type(test_result_test_t) test_result_test
@@ -19,6 +21,7 @@ program main
1921
integer :: passes=0, tests=0
2022

2123

24+
call bin_test%report(passes, tests)
2225
call data_partition_test%report(passes, tests)
2326
call collectives_test%report(passes, tests)
2427
call object_test%report(passes, tests)

0 commit comments

Comments
 (0)