Skip to content

Commit 078b400

Browse files
authored
Merge pull request #124 from jvdp1/stat_mean_dev_1
addition of stdlib_experimental_stats mean
2 parents 4473a8c + c8bf3c1 commit 078b400

16 files changed

+568
-4
lines changed

.github/workflows/CI.yml

+5-2
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ jobs:
3939
if: contains(matrix.os, 'ubuntu')
4040
run: ci/install_cmake.sh
4141

42+
- name: Install fypp
43+
run: pip install --upgrade fypp
44+
4245
- name: Install GFortran Linux
4346
if: contains( matrix.os, 'ubuntu')
4447
run: |
@@ -54,7 +57,7 @@ jobs:
5457
run: brew install gcc@${GCC_V} || brew upgrade gcc@${GCC_V} || true
5558

5659
- name: Configure with CMake
57-
run: cmake -Wdev -DCMAKE_BUILD_TYPE=Release -S . -B build
60+
run: cmake -Wdev -DCMAKE_BUILD_TYPE=Release -DCMAKE_MAXIMUM_RANK=4 -S . -B build
5861

5962
- name: Build and compile
6063
run: cmake --build build
@@ -77,6 +80,6 @@ jobs:
7780
- name: Test manual makefiles
7881
if: contains(matrix.os, 'ubuntu') && contains(matrix.gcc_v, '9')
7982
run: |
80-
make -f Makefile.manual
83+
make -f Makefile.manual FYPPFLAGS="-DMAXRANK=4"
8184
make -f Makefile.manual test
8285
make -f Makefile.manual clean

.github/workflows/ci_windows.yml

+4-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@ jobs:
1515
steps:
1616
- uses: actions/checkout@v1
1717

18-
- run: cmake -G "MinGW Makefiles" -DCMAKE_SH="CMAKE_SH-NOTFOUND" -Wdev -B build -DCMAKE_BUILD_TYPE=Debug -DCMAKE_Fortran_FLAGS_DEBUG="-Wall -Wextra -Wimplicit-interface -fPIC -g -fcheck=all -fbacktrace"
18+
- name: Install fypp
19+
run: pip install fypp
20+
21+
- run: cmake -G "MinGW Makefiles" -DCMAKE_SH="CMAKE_SH-NOTFOUND" -Wdev -B build -DCMAKE_BUILD_TYPE=Debug -DCMAKE_Fortran_FLAGS_DEBUG="-Wall -Wextra -Wimplicit-interface -fPIC -g -fcheck=all -fbacktrace" -DCMAKE_MAXIMUM_RANK=4
1922

2023
env:
2124
FC: gfortran

CMakeLists.txt

+5
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ endif()
1515
include(CheckFortranSourceCompiles)
1616
include(CheckFortranSourceRuns)
1717
check_fortran_source_compiles("error stop i; end" f18errorstop SRC_EXT f90)
18+
check_fortran_source_compiles("real, allocatable :: array(:, :, :, :, :, :, :, :, :, :); end" f03rank SRC_EXT f90)
1819
check_fortran_source_runs("use, intrinsic :: iso_fortran_env, only : real128; real(real128) :: x; x = x+1; end" f03real128)
1920

21+
if(DEFINED CMAKE_MAXIMUM_RANK)
22+
set(CMAKE_MAXIMUM_RANK ${CMAKE_MAXIMUM_RANK})
23+
endif()
24+
2025
add_subdirectory(src)

Makefile.manual

+2
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22

33
FC = gfortran
44
FFLAGS = -Wall -Wextra -Wimplicit-interface -fPIC -g -fcheck=all
5+
FYPPFLAGS=
56

67
export FC
78
export FFLAGS
9+
export FYPPFLAGS
810

911
.PHONY: all clean test
1012

src/CMakeLists.txt

+49
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,59 @@
1+
### Pre-process: .fpp -> .f90 via Fypp
2+
3+
# Create a list of the files to be preprocessed
4+
set(fppFiles
5+
stdlib_experimental_stats.fypp
6+
stdlib_experimental_stats_mean.fypp
7+
)
8+
9+
# Pre-process
10+
foreach(infileName IN LISTS fppFiles)
11+
12+
# Generate output file name
13+
string(REGEX REPLACE ".fypp\$" ".f90" outfileName "${infileName}")
14+
15+
# Create the full path for the new file
16+
set(outfile "${CMAKE_CURRENT_BINARY_DIR}/${outfileName}")
17+
18+
# Generate input file name
19+
set(infile "${CMAKE_CURRENT_SOURCE_DIR}/${infileName}")
20+
21+
# Custom command to do the processing
22+
if(DEFINED CMAKE_MAXIMUM_RANK)
23+
add_custom_command(
24+
OUTPUT "${outfile}"
25+
COMMAND fypp -DMAXRANK=${CMAKE_MAXIMUM_RANK} "${infile}" "${outfile}"
26+
MAIN_DEPENDENCY "${infile}"
27+
VERBATIM)
28+
elseif(f03rank)
29+
add_custom_command(
30+
OUTPUT "${outfile}"
31+
COMMAND fypp "${infile}" "${outfile}"
32+
MAIN_DEPENDENCY "${infile}"
33+
VERBATIM)
34+
else()
35+
add_custom_command(
36+
OUTPUT "${outfile}"
37+
COMMAND fypp -DVERSION90 "${infile}" "${outfile}"
38+
MAIN_DEPENDENCY "${infile}"
39+
VERBATIM)
40+
endif()
41+
42+
# Finally add output file to a list
43+
set(outFiles ${outFiles} "${outfile}")
44+
45+
endforeach(infileName)
46+
47+
48+
149
set(SRC
250
stdlib_experimental_ascii.f90
351
stdlib_experimental_io.f90
452
stdlib_experimental_error.f90
553
stdlib_experimental_kinds.f90
654
stdlib_experimental_optval.f90
755
stdlib_experimental_system.F90
56+
${outFiles}
857
)
958

1059
add_library(fortran_stdlib ${SRC})

src/Makefile.manual

+11-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ SRC = stdlib_experimental_ascii.f90 \
33
stdlib_experimental_io.f90 \
44
stdlib_experimental_optval.f90 \
55
stdlib_experimental_kinds.f90 \
6-
f18estop.f90
6+
f18estop.f90 \
7+
stdlib_experimental_stats.f90 \
8+
stdlib_experimental_stats_mean.f90
79

810
LIB = libstdlib.a
911

@@ -26,6 +28,8 @@ clean:
2628
%.o: %.f90
2729
$(FC) $(FFLAGS) -c $<
2830

31+
%.f90: %.fypp
32+
fypp $(FYPPFLAGS) $< $@
2933

3034
# Fortran module dependencies
3135
f18estop.o: stdlib_experimental_error.o
@@ -34,3 +38,9 @@ stdlib_experimental_io.o: \
3438
stdlib_experimental_optval.o \
3539
stdlib_experimental_kinds.o
3640
stdlib_experimental_optval.o: stdlib_experimental_kinds.o
41+
stdlib_experimental_stats_mean.o: \
42+
stdlib_experimental_optval.o \
43+
stdlib_experimental_kinds.o \
44+
stdlib_experimental_stats.o
45+
stdlib_experimental_stats.f90: stdlib_experimental_stats.fypp
46+
stdlib_experimental_stats_mean.f90: stdlib_experimental_stats_mean.fypp

src/stdlib_experimental_stats.fypp

+126
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
module stdlib_experimental_stats
2+
3+
#:set VERSION90 = defined('VERSION90')
4+
#:set REALKINDS = ["sp", "dp", "qp"]
5+
#:set INTKINDS = ["int8", "int16", "int32", "int64"]
6+
#:set REALTYPES = ["real({})".format(k) for k in REALKINDS]
7+
#:set INTTYPES = ["integer({})".format(k) for k in INTKINDS]
8+
#:set iktr = list(zip(range(len(REALKINDS)), REALKINDS, REALTYPES))
9+
#:set ikti = list(zip(range(len(INTKINDS)), INTKINDS, INTTYPES))
10+
11+
use stdlib_experimental_kinds, only: sp, dp, qp, &
12+
int8, int16, int32, int64
13+
implicit none
14+
private
15+
! Public API
16+
public :: mean
17+
18+
interface mean
19+
#:for i1, k1, t1 in iktr
20+
module function mean_1_${k1}$_${k1}$(x) result(res)
21+
${t1}$, intent(in) :: x(:)
22+
${t1}$ :: res
23+
end function mean_1_${k1}$_${k1}$
24+
#:endfor
25+
26+
#:for i1, k1, t1 in ikti
27+
module function mean_1_${k1}$_dp(x) result(res)
28+
${t1}$, intent(in) :: x(:)
29+
real(dp) :: res
30+
end function mean_1_${k1}$_dp
31+
#:endfor
32+
33+
34+
#:for i1, k1, t1 in iktr
35+
module function mean_2_all_${k1}$_${k1}$(x) result(res)
36+
${t1}$, intent(in) :: x(:,:)
37+
${t1}$ :: res
38+
end function mean_2_all_${k1}$_${k1}$
39+
#:endfor
40+
41+
#:for i1, k1, t1 in ikti
42+
module function mean_2_all_${k1}$_dp(x) result(res)
43+
${t1}$, intent(in) :: x(:,:)
44+
real(dp) :: res
45+
end function mean_2_all_${k1}$_dp
46+
#:endfor
47+
48+
#:for i1, k1, t1 in iktr
49+
module function mean_2_${k1}$_${k1}$(x, dim) result(res)
50+
${t1}$, intent(in) :: x(:,:)
51+
integer, intent(in) :: dim
52+
${t1}$ :: res(merge(size(x, 1), size(x, 2), mask = 1 < dim ))
53+
end function mean_2_${k1}$_${k1}$
54+
#:endfor
55+
56+
#:for i1, k1, t1 in ikti
57+
module function mean_2_${k1}$_dp(x, dim) result(res)
58+
${t1}$, intent(in) :: x(:,:)
59+
integer, intent(in) :: dim
60+
real(dp) :: res(merge(size(x, 1), size(x, 2), mask = 1 < dim ))
61+
end function mean_2_${k1}$_dp
62+
#:endfor
63+
64+
65+
#:def ranksuffix(rank)
66+
#{if rank > 0}#(${":" + ",:" * (rank - 1)}$)#{endif}#
67+
#:enddef
68+
69+
#:if defined('MAXRANK')
70+
#:set ranks = range(3,MAXRANK+1)
71+
#:elif VERSION90
72+
#:set ranks = range(3,8)
73+
#:else
74+
#:set ranks = range(3,16)
75+
#:endif
76+
77+
78+
#:for i1, k1, t1 in iktr
79+
#:for rank in ranks
80+
module function mean_${rank}$_all_${k1}$_${k1}$(x) result(res)
81+
${t1}$, intent(in) :: x${ranksuffix(rank)}$
82+
${t1}$ :: res
83+
end function mean_${rank}$_all_${k1}$_${k1}$
84+
#:endfor
85+
#:endfor
86+
87+
#:for i1, k1, t1 in ikti
88+
#:for rank in ranks
89+
module function mean_${rank}$_all_${k1}$_dp(x) result(res)
90+
${t1}$, intent(in) :: x${ranksuffix(rank)}$
91+
real(dp) :: res
92+
end function mean_${rank}$_all_${k1}$_dp
93+
#:endfor
94+
#:endfor
95+
96+
#:for i1, k1, t1 in iktr
97+
#:for rank in ranks
98+
module function mean_${rank}$_${k1}$_${k1}$(x, dim) result(res)
99+
${t1}$, intent(in) :: x${ranksuffix(rank)}$
100+
integer, intent(in) :: dim
101+
${t1}$ :: res( &
102+
#:for imerge in range(1,rank-1)
103+
merge(size(x,${imerge}$),size(x,${imerge + 1}$),mask = ${imerge}$ < dim ), &
104+
#:endfor
105+
merge(size(x,${rank-1}$),size(x,${rank}$),mask = ${rank-1}$ < dim ) )
106+
end function mean_${rank}$_${k1}$_${k1}$
107+
#:endfor
108+
#:endfor
109+
110+
#:for i1, k1, t1 in ikti
111+
#:for rank in ranks
112+
module function mean_${rank}$_${k1}$_dp(x, dim) result(res)
113+
${t1}$, intent(in) :: x${ranksuffix(rank)}$
114+
integer, intent(in) :: dim
115+
real(dp) :: res( &
116+
#:for imerge in range(1,rank-1)
117+
merge(size(x,${imerge}$),size(x,${imerge + 1}$),mask = ${imerge}$ < dim ), &
118+
#:endfor
119+
merge(size(x,${rank-1}$),size(x,${rank}$),mask = ${rank-1}$ < dim ) )
120+
end function mean_${rank}$_${k1}$_dp
121+
#:endfor
122+
#:endfor
123+
124+
end interface
125+
126+
end module

src/stdlib_experimental_stats.md

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Descriptive statistics
2+
3+
## Implemented
4+
5+
* `mean`
6+
7+
## `mean` - mean of array elements
8+
9+
### Description
10+
11+
Returns the mean of all the elements of `array`, or of the elements of `array` along dimension `dim` if provided.
12+
13+
### Syntax
14+
15+
`result = mean(array)`
16+
17+
`result = mean(array, dim)`
18+
19+
### Arguments
20+
21+
`array`: Shall be an array of type `integer`, or `real`.
22+
23+
`dim`: Shall be a scalar of type `integer` with a value in the range from 1 to n, where n is the rank of `array`.
24+
25+
### Return value
26+
27+
If `array` is of type `real`, the result is of the same type as `array`.
28+
If `array` is of type `integer`, the result is of type `double precision`.
29+
30+
If `dim` is absent, a scalar with the mean of all elements in `array` is returned. Otherwise, an array of rank n-1, where n equals the rank of `array`, and a shape similar to that of `array` with dimension `dim` dropped is returned.
31+
32+
### Example
33+
34+
```fortran
35+
program test
36+
use stdlib_experimental_stats, only: mean
37+
implicit none
38+
real :: x(1:6) = [ 1., 2., 3., 4., 5., 6. ]
39+
print *, mean(x) !returns 21.
40+
print *, mean( reshape(x, [ 2, 3 ] )) !returns 21.
41+
print *, mean( reshape(x, [ 2, 3 ] ), 1) !returns [ 3., 7., 11. ]
42+
end program
43+
```

0 commit comments

Comments
 (0)