ParaMonte Fortran 2.0.0
Parallel Monte Carlo and Machine Learning Library
See the latest version documentation.
pm_distUnif Module Reference

This module contains classes and procedures for computing various statistical quantities related to the univariate Uniform distribution. More...

Data Types

type  distUnif_type
 This is the derived type for signifying distributions that are of type Uniform as defined in the description of pm_distUnif. More...
 
interface  getUnifCDF
 Generate and return the Cumulative Distribution Function (CDF) of a univariate Standard Uniform distribution or a Uniform distribution with the specified support via lower and upper input arguments at the specified input values. More...
 
interface  getUnifRand
 Generate and return a scalar or a contiguous array of rank 1 of length s1 of randomly uniformly distributed discrete logical, integer, character value(s), or continuous real or `complex value(s) within the specified input range.
More...
 
interface  getUnifRandState
 Generate and return an allocatable array of rank 1 containing the state vector of the Fortran default random number generator (RNG) or, optionally set the RNG state based on a reference input scalar seed, optionally distinctly on each processor. More...
 
interface  getUnifRandStateSize
 Generate and return the size of the seed vector of the Fortran default random number generator (RNG). More...
 
type  rngf_type
 This is a concrete derived type whose instances can be used to define/request the default uniform random number generator (RNG) of the Fortran standard.
More...
 
interface  rngf_typer
 Generate and return a scalar object of type rngf_type. More...
 
type  rngu_type
 This is the abstract base derived type for defining various Uniform Random Number Generator (URNG) derived types.
More...
 
interface  setUnifCDF
 Return the Cumulative Distribution Function (CDF) of a univariate Standard Uniform distribution or a Uniform distribution with the specified support via lower and upper input arguments at the specified input values. More...
 
interface  setUnifRand
 Return a uniform random scalar or contiguous array of arbitrary rank of randomly uniformly distributed discrete logical, integer, character value(s), or continuous real or `complex value(s) within the specified input range. More...
 
interface  setUnifRandState
 Set the state of the Fortran default random number generator (RNG) to a random value or to an optionally deterministic, optionally processor-dependent value based on the user-specified input scalar seed and processor ID. More...
 
type  splitmix64_type
 This is the derived type for declaring and generating objects of type splitmix64_type containing a unique instance of an splitmix64 random number generator (RNG). More...
 
interface  splitmix64_typer
 Generate, initialize, and return a scalar object of type splitmix64_type. More...
 
type  xoshiro256ss_type
 This is the abstract base derived type for defining variants of Xoshiro256** Uniform Random Number Generator derived types.
More...
 
type  xoshiro256ssg_type
 This is the derived type for declaring and generating objects of type xoshiro256ssg_type containing a unique instance of a greedy Xoshiro256** random number generator (RNG). More...
 
interface  xoshiro256ssg_typer
 Generate, initialize, and return a scalar object of type xoshiro256ssg_type. More...
 
type  xoshiro256ssw_type
 This is the derived type for declaring and generating objects of type xoshiro256ssw_type containing a unique instance of a Xoshiro256** random number generator (RNG). More...
 
interface  xoshiro256ssw_typer
 Generate, initialize, and return a scalar object of type xoshiro256ssw_type. More...
 

Variables

character(*, SK), parameter MODULE_NAME = "@pm_distUnif"
 
integer(IK), parameter xoshiro256ssStreamBitSize = int(bit_size(0_IK64), IK)
 The constant scalar of type integer of default kind containing the number of binary digits of the stream component Xoshiro256** random number generator.
More...
 
integer(IK), parameter xoshiro256ssStateSize = 4_IK
 The constant scalar of type integer of default kind IK containing the size of the state vector of Xoshiro256** random number generator.
More...
 
integer(IK64), dimension(xoshiro256ssStateSize), parameter xoshiro256ssJump128 = [ +1733541517147835066_IK64 , -3051731464161248980_IK64 , -6244198995065845334_IK64 , +4155657270789760540_IK64 ]
 The constant vector of size xoshiro256ssStateSize of type integer of kind IK64 containing the state jump for the Xoshiro256** random number generator.
More...
 
integer(IK64), dimension(xoshiro256ssStateSize), parameter xoshiro256ssJump192 = [ +8566230491382795199_IK64 , -4251311993797857357_IK64 , +8606660816089834049_IK64 , +4111957640723818037_IK64 ]
 The constant vector of size xoshiro256ssStateSize of type integer of kind IK64 containing the state jump for the Xoshiro256** random number generator.
More...
 
type(rngf_typerngf
 The scalar constant object of type rngf_type whose presence signified the use of the Fortran intrinsic random number generator (RNGF).
More...
 

Detailed Description

This module contains classes and procedures for computing various statistical quantities related to the univariate Uniform distribution.

Specifically, this module contains routines for computing the following quantities of the univariate Uniform distribution:

  1. the Probability Density Function (PDF)
  2. the Cumulative Distribution Function (CDF)
  3. the Random Number Generation from the distribution (RNG)
  4. the Inverse Cumulative Distribution Function (ICDF) or the Quantile Function

The continuous uniform distributions or rectangular distributions are a family of symmetric probability distributions.
Such a distribution describes an experiment where there is an arbitrary outcome that lies between certain bounds.
The bounds are defined by the parameters, \(a\) and \(b\), which are the minimum and maximum values.
The interval can either be closed (i.e., \([a, b]\)) or open (i.e., \((a, b)\)).
Therefore, the distribution is often abbreviated as \(U(a,b)\) where \(U\) stands for uniform distribution.
The difference between the bounds defines the interval length.
All intervals of the same length on the distribution's support are equally probable.

Note
The Uniform distribution is the maximum entropy probability distribution for a random variable \(x\) under no constraint other than that it is contained in the distribution's support.

Probability density function (PDF)

The PDF of the continuous uniform distribution is,

\begin{equation} f(x) = \begin{cases} \frac{1}{b - a} & \text{for} a\leq x \leq b ~, \\ 0 & \text{for} x < a ~ \text{or} ~ x > b ~. \end{cases} \end{equation}

The values of \(f(x)\) at the two boundaries \(a\) and \(b\) are usually unimportant, because they do not alter the value of \(\int_c^d f(x) dx\) over any interval \([c,d]\) nor of \(\int_a^b x f(x) dx\) nor of any higher moment.
Sometimes they are chosen to be zero, and sometimes chosen to be \(\frac{1}{b-a}\).
The latter is appropriate in the context of estimation by the method of maximum likelihood.
In the context of Fourier analysis, one may take the value of \(f(a)\) or \(f(b)\) to be \(\frac{1}{2(b - a)}\) because then the inverse transform of many integral transforms of this uniform function will yield back the function itself, rather than a function which is equal almost everywhere, i.e., except on a set of points with zero measure.
Also, it is consistent with the sign function, which has no such ambiguity.
Any probability density function integrates to \(1\).
Thus, the PDF of the continuous uniform distribution is graphically portrayed as a rectangle where \(b − a\) is the base length and \(\frac{1}{b-a}\) is the height.
As the base length increases, the height (the density at any particular value within the distribution boundaries) decreases.
In terms of mean \(\mu\) and variance \(\sigma^{2}\) the probability density function of the continuous uniform distribution is,

\begin{equation} f(x) = \begin{cases} \frac{1}{2\sigma\sqrt{3}} & \text{for} -\sigma\sqrt{3} \leq x - \mu \leq \sigma\sqrt{3} ~, \\ 0 & \text{otherwise} ~. \end{cases} \end{equation}

Cumulative distribution function (CDF)

The CDF of the continuous uniform distribution is,

\begin{equation} F(x) = \begin{cases} 0 & \text{for} x < a ~,\\ \frac{x - a}{b - a} & \text{for} a\leq x\leq b ~, \\ 1 & \text{for} x > b ~. \end{cases} \end{equation}

In terms of mean \(\mu\) and variance \(\sigma^{2}\), the cumulative distribution function of the continuous uniform distribution is,

\begin{equation} F(x) = \begin{cases} 0 & \text{for} x - \mu < -\sigma\sqrt{3} ~, \\ \frac{1}{2}\left(\frac{x - \mu}{\sigma\sqrt{3}} + 1\right) & \text{for} -\sigma\sqrt{3} \leq x - \mu < \sigma\sqrt{3} ~, \\ 1 & \text{for} x - \mu \geq \sigma\sqrt{3} \end{cases} \end{equation}

Note
  1. The procedures under the generic interface getUnifCDF of this module are elemental functions that accept optional arguments of arbitrary ranks.
    As such, the procedures offer great flexibility in coding.
    However, the elemental nature of the procedures impacts their runtime performance negatively.
    See the benchmarks below for more information.
  2. The procedures under the generic interface setUnifCDF are subroutines that accept a limited range of specific arguments ranks.
    As such, they offer much better runtime performance compared to getUnifCDF but have significantly less flexibility.
  3. Which procedures should I use?
    The elemental procedures appear to incur no performance penalty with scalar arguments. However, there appears to exist a runtime performance penalty of ~2-3 times more than the rank-specific routines for array arguments, comparable to 10-20 CPU cycles.
    These penalties are due to the looping that occur in the elemental procedures for array arguments.
    However, this elemental performance penalty is likely insignificant in most practical cases, unless the elemental procedures are to be called on the order of tens of billions of times in a program, in which, case, the over all performance penalty, as of 2022, appears to be on the order of a few minutes or less.
  4. Note that the following benchmarks represent the worst case scenarios.
    There may be situations where the compiler could inline the elemental procedures and remove the overhead of repeatedly calling the elemental function.

Inverse Cumulative distribution function (ICDF) or Quantile Function

The Quantile function of continuous Uniform distribution is given by,

\begin{equation} F^{-1}(p) = a + p(b - a) \quad \text{for} 0 < p < 1 ~. \end{equation}

In terms of mean \(\mu\) and variance \(\sigma^{2}\), the Quantile function of the continuous uniform distribution is,

\begin{equation} F^{-1}(p) = \sigma\sqrt{3} (2p - 1) + \mu \quad \text{for} 0 \leq p \leq 1 ~. \end{equation}

Random Number Generation (RNG)

This module contains two generic functional and subroutine interfaces

  1. getUnifRand
  2. setUnifRand

for generating uniformly distributed random values of all intrinsic types and kinds supported by the Fortran standard and the processor (character, integer, logical, complex, real).
The functional interface is merely a wrapper around the generic subroutine interface.

This module also contains four random number generator (RNG) algorithms that can be specified via the corresponding types,

  1. rngf_type (the default intrinsic Fortran uniform RNG via random_number())
  2. splitmix64_type
  3. xoshiro256ssg_type
  4. xoshiro256ssw_type

Usage

  1. xoshiro256ssw_type is the recommended RNG for all serial and parallel tasks.
  2. xoshiro256ssg_type is the recommended RNG for tasks that mostly require logical random values, although it can be used for random value generation of any type and kind.
  3. splitmix64_type is the recommended RNG for initializing other RNGs or for simple serial tasks, although it can be used for random value generation of any type and kind.
  4. The default Fortran RNG rngf_type, although flexible to use and fast, will not generate deterministic results across different compilers.
Benchmarks:


Benchmark :: The runtime performance of scalar getUnifCDF vs. setUnifCDF without bounds

1program benchmark
2
3 use pm_kind, only: IK, RK, SK
4 use pm_bench, only: bench_type
5
6 implicit none
7
8 integer(IK) :: i,j
9 integer(IK) :: iarr
10 integer(IK) :: fileUnit
11 integer(IK) , parameter :: MINITER = 10**5_IK
12 integer(IK) , parameter :: NBENCH = 2_IK
13 real(RK) :: cdf
14 real(RK) :: point
15 real(RK) :: dummy = 0._RK
16 type(bench_type) :: bench(NBENCH)
17
18 bench(1) = bench_type(name = SK_"getUnifCDF", exec = getUnifCDF, overhead = setOverhead)
19 bench(2) = bench_type(name = SK_"setUnifCDF", exec = setUnifCDF, overhead = setOverhead)
20
21 write(*,"(*(g0,:,' '))")
22 write(*,"(*(g0,:,' '))") "getUnifCDF() vs. setUnifCDF()."
23 write(*,"(*(g0,:,' '))")
24
25 open(newunit = fileUnit, file = "main.out", status = "replace")
26
27 write(fileUnit, "(*(g0,:,','))") (bench(i)%name, i = 1, NBENCH)
28
29 call random_number(point)
30 do i = 1, NBENCH
31 bench(i)%timing = bench(i)%getTiming(miniter = MINITER)
32 end do
33
34 do j = 1, MINITER
35 write(fileUnit,"(*(g0,:,','))") (max(epsilon(0._RK),bench(i)%timing%values(j)), i = 1, NBENCH)
36 end do
37
38 write(*,"(*(g0,:,' '))") dummy
39 write(*,"(*(g0,:,' '))")
40
41 close(fileUnit)
42
43contains
44
45 !%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
46 ! procedure wrappers.
47 !%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
48
49 subroutine setOverhead()
50 call finalize()
51 end subroutine
52
53 subroutine finalize()
54 dummy = dummy + cdf
55 end subroutine
56
57 subroutine getUnifCDF()
58 block
59 use pm_distUnif, only: getUnifCDF
60 cdf = getUnifCDF(point)
61 call finalize()
62 end block
63 end subroutine
64
65 subroutine setUnifCDF()
66 block
67 use pm_distUnif, only: setUnifCDF
68 call setUnifCDF(cdf, point)
69 call finalize()
70 end block
71 end subroutine
72
73end program benchmark
Generate and return an object of type timing_type containing the benchmark timing information and sta...
Definition: pm_bench.F90:574
Generate and return the Cumulative Distribution Function (CDF) of a univariate Standard Uniform distr...
Return the Cumulative Distribution Function (CDF) of a univariate Standard Uniform distribution or a ...
This module contains abstract interfaces and types that facilitate benchmarking of different procedur...
Definition: pm_bench.F90:41
This module contains classes and procedures for computing various statistical quantities related to t...
This module defines the relevant Fortran kind type-parameters frequently used in the ParaMonte librar...
Definition: pm_kind.F90:268
integer, parameter RK
The default real kind in the ParaMonte library: real64 in Fortran, c_double in C-Fortran Interoperati...
Definition: pm_kind.F90:543
integer, parameter IK
The default integer kind in the ParaMonte library: int32 in Fortran, c_int32_t in C-Fortran Interoper...
Definition: pm_kind.F90:540
integer, parameter SK
The default character kind in the ParaMonte library: kind("a") in Fortran, c_char in C-Fortran Intero...
Definition: pm_kind.F90:539
This is the class for creating benchmark and performance-profiling objects.
Definition: pm_bench.F90:386

Example Unix compile command via Intel ifort compiler
1#!/usr/bin/env sh
2rm main.exe
3ifort -fpp -standard-semantics -O3 -Wl,-rpath,../../../lib -I../../../inc main.F90 ../../../lib/libparamonte* -o main.exe
4./main.exe

Example Windows Batch compile command via Intel ifort compiler
1del main.exe
2set PATH=..\..\..\lib;%PATH%
3ifort /fpp /standard-semantics /O3 /I:..\..\..\include main.F90 ..\..\..\lib\libparamonte*.lib /exe:main.exe
4main.exe

Example Unix / MinGW compile command via GNU gfortran compiler
1#!/usr/bin/env sh
2rm main.exe
3gfortran -cpp -ffree-line-length-none -O3 -Wl,-rpath,../../../lib -I../../../inc main.F90 ../../../lib/libparamonte* -o main.exe
4./main.exe

Postprocessing of the benchmark output
1#!/usr/bin/env python
2
3import matplotlib.pyplot as plt
4import pandas as pd
5import numpy as np
6
7fontsize = 14
8
9methods = ["setUnifCDF", "getUnifCDF"]
10
11df = pd.read_csv("main.out")
12
13
16
17ax = plt.figure(figsize = 1.25 * np.array([6.4,4.6]), dpi = 200)
18ax = plt.subplot()
19
20for method in methods:
21 plt.hist( np.log10(df[method].values)
22 , histtype = "stepfilled"
23 , density = True
24 , alpha = 0.7
25 , bins = 30
26 )
27
28plt.xticks(fontsize = fontsize)
29plt.yticks(fontsize = fontsize)
30ax.set_xlabel("$\log_{10}$ ( Runtime [ seconds ] )", fontsize = fontsize)
31ax.set_ylabel("Count", fontsize = fontsize)
32ax.set_title("Rank-0 Runtime:\ngetUnifCDF vs. setUnifCDF.\nLower is better.", fontsize = fontsize)
33#ax.set_xscale("log")
34#ax.set_yscale("log")
35plt.minorticks_on()
36plt.grid(visible = True, which = "both", axis = "both", color = "0.85", linestyle = "-")
37ax.tick_params(axis = "y", which = "minor")
38ax.tick_params(axis = "x", which = "minor")
39ax.legend ( methods
40 #, loc='center left'
41 #, bbox_to_anchor=(1, 0.5)
42 , fontsize = fontsize
43 )
44
45plt.tight_layout()
46plt.savefig("benchmark.getUnifCDF_vs_setUnifCDF_D0.runtime.png")
47
48
51
52ax = plt.figure(figsize = 1.25 * np.array([6.4,4.6]), dpi = 200)
53ax = plt.subplot()
54
55plt.hist( np.log10(df["getUnifCDF"].values / df["setUnifCDF"].values)
56 , histtype = "stepfilled"
57 , density = True
58 , alpha = 0.7
59 , bins = 30
60 )
61
62plt.xticks(fontsize = fontsize)
63plt.yticks(fontsize = fontsize)
64ax.set_xlabel(r"$\log_{10}$ ( Runtime Ratio [ seconds ] )", fontsize = fontsize)
65ax.set_ylabel("Count", fontsize = fontsize)
66ax.set_title(r"""$\log_{10}$ ( Rank-0 Runtime Ratio ): getUnifCDF to setUnifCDF.
67A value < $\log_{10}(1)$ implies better performance of getUnifCDF.""", fontsize = fontsize)
68#ax.set_xscale("log")
69#ax.set_yscale("log")
70plt.minorticks_on()
71plt.grid(visible = True, which = "both", axis = "both", color = "0.85", linestyle = "-")
72ax.tick_params(axis = "y", which = "minor")
73ax.tick_params(axis = "x", which = "minor")
74
75plt.tight_layout()
76plt.savefig("benchmark.getUnifCDF_vs_setUnifCDF_D0.runtime.ratio.png")

Visualization of the benchmark output

Benchmark moral
  1. The procedures under the generic interface getUnifCDF are elemental functions with optional arguments. In the absence of the optional arguments, the default values are used, but the associated computations will be redundant.
    As such, one expects the getUnifCDF to perform less efficiently than the procedures under the generic interface setUnifCDF which are rank-specific and carefully designed to avoid redundant computations.


Benchmark :: The runtime performance of array getUnifCDF vs. setUnifCDF without bounds

1program benchmark
2
3 use pm_kind, only: IK, RK, SK
4 use pm_bench, only: bench_type
5
6 implicit none
7
8 integer(IK) :: i
9 integer(IK) :: iarr
10 integer(IK) :: fileUnit
11 integer(IK) , parameter :: NARR = 16_IK
12 integer(IK) , parameter :: NBENCH = 2_IK
13 integer(IK) :: arraySize(NARR)
14 real(RK) , allocatable :: cdf(:)
15 real(RK) , allocatable :: point(:)
16 real(RK) :: dummy = 0._RK
17 type(bench_type) :: bench(NBENCH)
18
19 bench(1) = bench_type(name = SK_"getUnifCDF", exec = getUnifCDF, overhead = setOverhead)
20 bench(2) = bench_type(name = SK_"setUnifCDF", exec = setUnifCDF, overhead = setOverhead)
21
22 arraySize = [( 2_IK**iarr, iarr = 1_IK, NARR )]
23
24 write(*,"(*(g0,:,' '))")
25 write(*,"(*(g0,:,' '))") "getUnifCDF() vs. setUnifCDF()."
26 write(*,"(*(g0,:,' '))")
27
28 open(newunit = fileUnit, file = "main.out", status = "replace")
29
30 write(fileUnit, "(*(g0,:,','))") "arraySize", (bench(i)%name, i = 1, NBENCH)
31
32 loopOverMatrixSize: do iarr = 1, NARR
33
34 write(*,"(*(g0,:,' '))") "Benchmarking getUnifCDF() vs. setUnifCDF() with array size", arraySize(iarr)
35 allocate(cdf(arraySize(iarr)), point(arraySize(iarr)))
36 call random_number(point)
37
38 do i = 1, NBENCH
39 bench(i)%timing = bench(i)%getTiming()
40 end do
41
42 deallocate(cdf, point)
43 write(fileUnit,"(*(g0,:,','))") arraySize(iarr), (bench(i)%timing%mean, i = 1, NBENCH)
44
45 end do loopOverMatrixSize
46 write(*,"(*(g0,:,' '))")
47
48 close(fileUnit)
49
50contains
51
52 !%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
53 ! procedure wrappers.
54 !%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
55
56 subroutine setOverhead()
57 call finalize()
58 end subroutine
59
60 subroutine finalize()
61 dummy = dummy + sum(cdf)
62 end subroutine
63
64 subroutine getUnifCDF()
65 block
66 use pm_distUnif, only: getUnifCDF
67 cdf(:) = getUnifCDF(point)
68 call finalize()
69 end block
70 end subroutine
71
72 subroutine setUnifCDF()
73 block
74 use pm_distUnif, only: setUnifCDF
75 call setUnifCDF(cdf, point)
76 call finalize()
77 end block
78 end subroutine
79
80end program benchmark

Example Unix compile command via Intel ifort compiler
1#!/usr/bin/env sh
2rm main.exe
3ifort -fpp -standard-semantics -O3 -Wl,-rpath,../../../lib -I../../../inc main.F90 ../../../lib/libparamonte* -o main.exe
4./main.exe

Example Windows Batch compile command via Intel ifort compiler
1del main.exe
2set PATH=..\..\..\lib;%PATH%
3ifort /fpp /standard-semantics /O3 /I:..\..\..\include main.F90 ..\..\..\lib\libparamonte*.lib /exe:main.exe
4main.exe

Example Unix / MinGW compile command via GNU gfortran compiler
1#!/usr/bin/env sh
2rm main.exe
3gfortran -cpp -ffree-line-length-none -O3 -Wl,-rpath,../../../lib -I../../../inc main.F90 ../../../lib/libparamonte* -o main.exe
4./main.exe

Postprocessing of the benchmark output
1#!/usr/bin/env python
2
3import matplotlib.pyplot as plt
4import pandas as pd
5import numpy as np
6
7fontsize = 14
8
9methods = ["setUnifCDF", "getUnifCDF"]
10
11df = pd.read_csv("main.out")
12
13
16
17ax = plt.figure(figsize = 1.25 * np.array([6.4,4.6]), dpi = 200)
18ax = plt.subplot()
19
20for method in methods:
21 plt.plot( df["arraySize"].values
22 , df[method].values
23 , linewidth = 2
24 )
25
26plt.xticks(fontsize = fontsize)
27plt.yticks(fontsize = fontsize)
28ax.set_xlabel("Array Size", fontsize = fontsize)
29ax.set_ylabel("Runtime [ seconds ]", fontsize = fontsize)
30ax.set_title("Rank-1 Runtime:\ngetUnifCDF vs. setUnifCDF.\nLower is better.", fontsize = fontsize)
31ax.set_xscale("log")
32ax.set_yscale("log")
33plt.minorticks_on()
34plt.grid(visible = True, which = "both", axis = "both", color = "0.85", linestyle = "-")
35ax.tick_params(axis = "y", which = "minor")
36ax.tick_params(axis = "x", which = "minor")
37ax.legend ( methods
38 #, loc='center left'
39 #, bbox_to_anchor=(1, 0.5)
40 , fontsize = fontsize
41 )
42
43plt.tight_layout()
44plt.savefig("benchmark.getUnifCDF_vs_setUnifCDF_D1.runtime.png")
45
46
49
50ax = plt.figure(figsize = 1.25 * np.array([6.4,4.6]), dpi = 200)
51ax = plt.subplot()
52
53plt.plot( df["arraySize"].values
54 , np.ones( len(df["arraySize"].values) )
55 , linestyle = "-"
56 , linewidth = 2
57 #, color = "black"
58 )
59plt.plot( df["arraySize"].values
60 , df["getUnifCDF"].values / df["setUnifCDF"].values
61 , linewidth = 2
62 #, color = "r"
63 )
64
65plt.xticks(fontsize = fontsize)
66plt.yticks(fontsize = fontsize)
67ax.set_xlabel("Array Size", fontsize = fontsize)
68ax.set_ylabel("Runtime Ratio", fontsize = fontsize)
69ax.set_title("""Rank-1 Runtime Ratio: getUnifCDF to setUnifCDF.
70A value < 1 implies better performance of getUnifCDF.""", fontsize = fontsize)
71ax.set_xscale("log")
72#ax.set_yscale("log")
73plt.minorticks_on()
74plt.grid(visible = True, which = "both", axis = "both", color = "0.85", linestyle = "-")
75ax.tick_params(axis = "y", which = "minor")
76ax.tick_params(axis = "x", which = "minor")
77ax.legend ( ["setUnifCDF", "getUnifCDF"]
78 #, loc='center left'
79 #, bbox_to_anchor=(1, 0.5)
80 , fontsize = fontsize
81 )
82
83plt.tight_layout()
84plt.savefig("benchmark.getUnifCDF_vs_setUnifCDF_D1.runtime.ratio.png")

Visualization of the benchmark output

Benchmark moral
  1. The procedures under the generic interface getUnifCDF are elemental functions with optional arguments.
    In the absence of the optional arguments, the default values are used, but the associated computations will be redundant.
    Furthermore, elemental functions incur a performance penalty for input array arguments due to internal looping performed by the compiler to call the function repeatedly for different array elements.
    As such, one expects the getUnifCDF to perform less efficiently than the procedures under the generic interface setUnifCDF which are rank-specific and carefully designed to avoid redundant computations.


Benchmark :: The runtime performance of scalar getUnifCDF vs. setUnifCDF with bounds

1program benchmark
2
3 use pm_kind, only: IK, RK, SK
4 use pm_bench, only: bench_type
5
6 implicit none
7
8 integer(IK) :: i,j
9 integer(IK) :: iarr
10 integer(IK) :: fileUnit
11 integer(IK) , parameter :: MINITER = 10**5_IK
12 integer(IK) , parameter :: NBENCH = 2_IK
13 real(RK) , parameter :: LOWER = -2._RK
14 real(RK) , parameter :: UPPER = +2._RK
15 real(RK) :: cdf
16 real(RK) :: point
17 real(RK) :: dummy = 0._RK
18 type(bench_type) :: bench(NBENCH)
19
20 bench(1) = bench_type(name = SK_"getUnifCDF", exec = getUnifCDF, overhead = setOverhead)
21 bench(2) = bench_type(name = SK_"setUnifCDF", exec = setUnifCDF, overhead = setOverhead)
22
23 write(*,"(*(g0,:,' '))")
24 write(*,"(*(g0,:,' '))") "getUnifCDF() vs. setUnifCDF()."
25 write(*,"(*(g0,:,' '))")
26
27 open(newunit = fileUnit, file = "main.out", status = "replace")
28
29 write(fileUnit, "(*(g0,:,','))") (bench(i)%name, i = 1, NBENCH)
30
31 call random_number(point)
32 do i = 1, NBENCH
33 bench(i)%timing = bench(i)%getTiming(miniter = MINITER)
34 end do
35
36 do j = 1, MINITER
37 write(fileUnit,"(*(g0,:,','))") (max(epsilon(0._RK),bench(i)%timing%values(j)), i = 1, NBENCH)
38 end do
39
40 write(*,"(*(g0,:,' '))") dummy
41 write(*,"(*(g0,:,' '))")
42
43 close(fileUnit)
44
45contains
46
47 !%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
48 ! procedure wrappers.
49 !%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
50
51 subroutine setOverhead()
52 call finalize()
53 end subroutine
54
55 subroutine finalize()
56 dummy = dummy + cdf
57 end subroutine
58
59 subroutine getUnifCDF()
60 block
61 use pm_distUnif, only: getUnifCDF
62 cdf = getUnifCDF(point, LOWER, UPPER)
63 call finalize()
64 end block
65 end subroutine
66
67 subroutine setUnifCDF()
68 block
69 use pm_distUnif, only: setUnifCDF
70 call setUnifCDF(cdf, point, LOWER, UPPER)
71 call finalize()
72 end block
73 end subroutine
74
75end program benchmark

Example Unix compile command via Intel ifort compiler
1#!/usr/bin/env sh
2rm main.exe
3ifort -fpp -standard-semantics -O3 -Wl,-rpath,../../../lib -I../../../inc main.F90 ../../../lib/libparamonte* -o main.exe
4./main.exe

Example Windows Batch compile command via Intel ifort compiler
1del main.exe
2set PATH=..\..\..\lib;%PATH%
3ifort /fpp /standard-semantics /O3 /I:..\..\..\include main.F90 ..\..\..\lib\libparamonte*.lib /exe:main.exe
4main.exe

Example Unix / MinGW compile command via GNU gfortran compiler
1#!/usr/bin/env sh
2rm main.exe
3gfortran -cpp -ffree-line-length-none -O3 -Wl,-rpath,../../../lib -I../../../inc main.F90 ../../../lib/libparamonte* -o main.exe
4./main.exe

Postprocessing of the benchmark output
1#!/usr/bin/env python
2
3import matplotlib.pyplot as plt
4import pandas as pd
5import numpy as np
6
7fontsize = 14
8
9methods = ["setUnifCDF", "getUnifCDF"]
10
11df = pd.read_csv("main.out")
12
13
16
17ax = plt.figure(figsize = 1.25 * np.array([6.4,4.6]), dpi = 200)
18ax = plt.subplot()
19
20for method in methods:
21 plt.hist( np.log10(df[method].values)
22 , histtype = "stepfilled"
23 , density = True
24 , alpha = 0.7
25 , bins = 30
26 )
27
28plt.xticks(fontsize = fontsize)
29plt.yticks(fontsize = fontsize)
30ax.set_xlabel("$\log_{10}$ ( Runtime [ seconds ] )", fontsize = fontsize)
31ax.set_ylabel("Count", fontsize = fontsize)
32ax.set_title("Rank-0 Runtime:\ngetUnifCDF vs. setUnifCDF.\nLower is better.", fontsize = fontsize)
33#ax.set_xscale("log")
34#ax.set_yscale("log")
35plt.minorticks_on()
36plt.grid(visible = True, which = "both", axis = "both", color = "0.85", linestyle = "-")
37ax.tick_params(axis = "y", which = "minor")
38ax.tick_params(axis = "x", which = "minor")
39ax.legend ( methods
40 #, loc='center left'
41 #, bbox_to_anchor=(1, 0.5)
42 , fontsize = fontsize
43 )
44
45plt.tight_layout()
46plt.savefig("benchmark.getUnifCDF_vs_setUnifCDF_D0_D0.runtime.png")
47
48
51
52ax = plt.figure(figsize = 1.25 * np.array([6.4,4.6]), dpi = 200)
53ax = plt.subplot()
54
55plt.hist( np.log10(df["getUnifCDF"].values / df["setUnifCDF"].values)
56 , histtype = "stepfilled"
57 , density = True
58 , alpha = 0.7
59 , bins = 30
60 )
61
62plt.xticks(fontsize = fontsize)
63plt.yticks(fontsize = fontsize)
64ax.set_xlabel(r"$\log_{10}$ ( Runtime Ratio [ seconds ] )", fontsize = fontsize)
65ax.set_ylabel("Count", fontsize = fontsize)
66ax.set_title(r"""$\log_{10}$ ( Rank-0 Runtime Ratio ): getUnifCDF to setUnifCDF.
67A value < $\log_{10}(1)$ implies better performance of getUnifCDF.""", fontsize = fontsize)
68#ax.set_xscale("log")
69#ax.set_yscale("log")
70plt.minorticks_on()
71plt.grid(visible = True, which = "both", axis = "both", color = "0.85", linestyle = "-")
72ax.tick_params(axis = "y", which = "minor")
73ax.tick_params(axis = "x", which = "minor")
74
75plt.tight_layout()
76plt.savefig("benchmark.getUnifCDF_vs_setUnifCDF_D0_D0.runtime.ratio.png")

Visualization of the benchmark output

Benchmark moral
  1. The procedures under the generic interface getUnifCDF are elemental functions with optional arguments.
    In the presence of the optional arguments, the user-specified values are used.
    Therefore, the costs of computations in getUnifCDF are more comparable to the procedures under the generic interface setUnifCDF which are rank-specific and carefully designed to avoid redundant computations.


Benchmark :: The runtime performance of array getUnifCDF vs. setUnifCDF with bounds

1program benchmark
2
3 use pm_kind, only: IK, RK, SK
4 use pm_bench, only: bench_type
5
6 implicit none
7
8 integer(IK) :: i
9 integer(IK) :: iarr
10 integer(IK) :: fileUnit
11 integer(IK) , parameter :: NARR = 16_IK
12 integer(IK) , parameter :: NBENCH = 2_IK
13 integer(IK) :: arraySize(NARR)
14 real(RK) , parameter :: LOWER = -2._RK
15 real(RK) , parameter :: UPPER = +2._RK
16 real(RK) , allocatable :: cdf(:)
17 real(RK) , allocatable :: Point(:)
18 real(RK) :: dummy = 0._RK
19 type(bench_type) :: bench(NBENCH)
20
21 bench(1) = bench_type(name = SK_"getUnifCDF", exec = getUnifCDF, overhead = setOverhead)
22 bench(2) = bench_type(name = SK_"setUnifCDF", exec = setUnifCDF, overhead = setOverhead)
23
24 arraySize = [( 2_IK**iarr, iarr = 1_IK, NARR )]
25
26 write(*,"(*(g0,:,' '))")
27 write(*,"(*(g0,:,' '))") "getUnifCDF() vs. setUnifCDF()."
28 write(*,"(*(g0,:,' '))")
29
30 open(newunit = fileUnit, file = "main.out", status = "replace")
31
32 write(fileUnit, "(*(g0,:,','))") "arraySize", (bench(i)%name, i = 1, NBENCH)
33
34 loopOverMatrixSize: do iarr = 1, NARR
35
36 write(*,"(*(g0,:,' '))") "Benchmarking getUnifCDF() vs. setUnifCDF() with array size", arraySize(iarr)
37 allocate(cdf(arraySize(iarr)), Point(arraySize(iarr)))
38 call random_number(Point)
39
40 do i = 1, NBENCH
41 bench(i)%timing = bench(i)%getTiming()
42 end do
43
44 deallocate(cdf, Point)
45 write(fileUnit,"(*(g0,:,','))") arraySize(iarr), (bench(i)%timing%mean, i = 1, NBENCH)
46
47 end do loopOverMatrixSize
48 write(*,"(*(g0,:,' '))")
49
50 close(fileUnit)
51
52contains
53
54 !%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
55 ! procedure wrappers.
56 !%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
57
58 subroutine setOverhead()
59 call finalize()
60 end subroutine
61
62 subroutine finalize()
63 dummy = dummy + sum(cdf)
64 end subroutine
65
66 subroutine getUnifCDF()
67 block
68 use pm_distUnif, only: getUnifCDF
69 cdf(:) = getUnifCDF(Point, LOWER, UPPER)
70 call finalize()
71 end block
72 end subroutine
73
74 subroutine setUnifCDF()
75 block
76 use pm_distUnif, only: setUnifCDF
77 call setUnifCDF(cdf, Point, LOWER, UPPER)
78 call finalize()
79 end block
80 end subroutine
81
82end program benchmark

Example Unix compile command via Intel ifort compiler
1#!/usr/bin/env sh
2rm main.exe
3ifort -fpp -standard-semantics -O3 -Wl,-rpath,../../../lib -I../../../inc main.F90 ../../../lib/libparamonte* -o main.exe
4./main.exe

Example Windows Batch compile command via Intel ifort compiler
1del main.exe
2set PATH=..\..\..\lib;%PATH%
3ifort /fpp /standard-semantics /O3 /I:..\..\..\include main.F90 ..\..\..\lib\libparamonte*.lib /exe:main.exe
4main.exe

Example Unix / MinGW compile command via GNU gfortran compiler
1#!/usr/bin/env sh
2rm main.exe
3gfortran -cpp -ffree-line-length-none -O3 -Wl,-rpath,../../../lib -I../../../inc main.F90 ../../../lib/libparamonte* -o main.exe
4./main.exe

Postprocessing of the benchmark output
1#!/usr/bin/env python
2
3import matplotlib.pyplot as plt
4import pandas as pd
5import numpy as np
6
7fontsize = 14
8
9methods = ["setUnifCDF", "getUnifCDF"]
10
11df = pd.read_csv("main.out")
12
13
16
17ax = plt.figure(figsize = 1.25 * np.array([6.4,4.6]), dpi = 200)
18ax = plt.subplot()
19
20for method in methods:
21 plt.plot( df["arraySize"].values
22 , df[method].values
23 , linewidth = 2
24 )
25
26plt.xticks(fontsize = fontsize)
27plt.yticks(fontsize = fontsize)
28ax.set_xlabel("Array Size", fontsize = fontsize)
29ax.set_ylabel("Runtime [ seconds ]", fontsize = fontsize)
30ax.set_title("Rank-1 Runtime:\ngetUnifCDF vs. setUnifCDF.\nLower is better.", fontsize = fontsize)
31ax.set_xscale("log")
32ax.set_yscale("log")
33plt.minorticks_on()
34plt.grid(visible = True, which = "both", axis = "both", color = "0.85", linestyle = "-")
35ax.tick_params(axis = "y", which = "minor")
36ax.tick_params(axis = "x", which = "minor")
37ax.legend ( methods
38 #, loc='center left'
39 #, bbox_to_anchor=(1, 0.5)
40 , fontsize = fontsize
41 )
42
43plt.tight_layout()
44plt.savefig("benchmark.getUnifCDF_vs_setUnifCDF_D1_D0.runtime.png")
45
46
49
50ax = plt.figure(figsize = 1.25 * np.array([6.4,4.6]), dpi = 200)
51ax = plt.subplot()
52
53plt.plot( df["arraySize"].values
54 , np.ones( len(df["arraySize"].values) )
55 , linestyle = "-"
56 , linewidth = 2
57 #, color = "black"
58 )
59plt.plot( df["arraySize"].values
60 , df["getUnifCDF"].values / df["setUnifCDF"].values
61 , linewidth = 2
62 #, color = "r"
63 )
64
65plt.xticks(fontsize = fontsize)
66plt.yticks(fontsize = fontsize)
67ax.set_xlabel("Array Size", fontsize = fontsize)
68ax.set_ylabel("Runtime Ratio", fontsize = fontsize)
69ax.set_title("""Rank-1 Runtime Ratio: getUnifCDF to setUnifCDF.
70A value < 1 implies better performance of getUnifCDF.""", fontsize = fontsize)
71ax.set_xscale("log")
72#ax.set_yscale("log")
73plt.minorticks_on()
74plt.grid(visible = True, which = "both", axis = "both", color = "0.85", linestyle = "-")
75ax.tick_params(axis = "y", which = "minor")
76ax.tick_params(axis = "x", which = "minor")
77ax.legend ( ["setUnifCDF", "getUnifCDF"]
78 #, loc='center left'
79 #, bbox_to_anchor=(1, 0.5)
80 , fontsize = fontsize
81 )
82
83plt.tight_layout()
84plt.savefig("benchmark.getUnifCDF_vs_setUnifCDF_D1_D0.runtime.ratio.png")

Visualization of the benchmark output

Benchmark moral
  1. The procedures under the generic interface getUnifCDF are elemental functions with optional arguments.
    In the presence of the optional arguments, the user-specified values are used.
    Although the elemental functions incur a performance penalty for input array arguments due to internal looping performed by the compiler to call the function repeatedly for different array elements, the costs of computations in getUnifCDF become more comparable to the procedures under the generic interface setUnifCDF which are rank-specific and carefully designed to avoid redundant computations.


Benchmark :: The runtime performance of setUnifRand vs. Fortran intrinsic random_number().

1! Test the overhead of calling `setUnifRand()` vs. Fortran intrinsic procedure `random_number()`.
2program benchmark
3
4 use pm_kind, only: IK, RK, SK
5 use pm_bench, only: bench_type
7
8 implicit none
9
10 type(xoshiro256ssw_type) :: rngx
11 integer(IK) :: i
12 integer(IK) :: iarr
13 integer(IK) :: fileUnit
14 integer(IK) , parameter :: NARR = 11_IK
15 integer(IK) :: arraySize(NARR)
16 real(RK) , allocatable :: rand(:)
17 real(RK) :: dummy = 0._RK
18 type(bench_type), allocatable :: bench(:)
19
20 rngx = xoshiro256ssw_type()
21 bench = [ bench_type(name = SK_"random_number ", exec = random_number, overhead = setOverhead) &
22 , bench_type(name = SK_"setUnifRandRNGD", exec = setUnifRandRNGD, overhead = setOverhead) &
23 , bench_type(name = SK_"setUnifRandRNGX", exec = setUnifRandRNGX, overhead = setOverhead) &
24 ]
25
26 arraySize = [( 2_IK**iarr, iarr = 1_IK, NARR )]
27
28 write(*,"(*(g0,:,' '))")
29 write(*,"(*(g0,:,' '))") "setUnifRand() vs. random_number()."
30 write(*,"(*(g0,:,' '))")
31
32 open(newunit = fileUnit, file = "main.out", status = "replace")
33
34 write(fileUnit, "(*(g0,:,','))") "arraySize", (bench(i)%name, i = 1, size(bench))
35
36 loopOverMatrixSize: do iarr = 1, NARR
37
38 allocate(rand(arraySize(iarr)))
39 write(*,"(*(g0,:,' '))") "Benchmarking setUnifRand() vs. random_number() with array size", arraySize(iarr)
40
41 do i = 1, size(bench)
42 bench(i)%timing = bench(i)%getTiming()
43 end do
44
45 write(fileUnit,"(*(g0,:,','))") arraySize(iarr), (bench(i)%timing%mean, i = 1, size(bench))
46 deallocate(rand)
47
48 end do loopOverMatrixSize
49 write(*,"(*(g0,:,' '))")
50
51 close(fileUnit)
52
53contains
54
55 !%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
56 ! procedure wrappers.
57 !%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
58
59 subroutine setOverhead()
60 call getDummy()
61 end subroutine
62
63 subroutine getDummy()
64 dummy = dummy + rand(1)
65 end subroutine
66
67 subroutine setUnifRandRNGD()
68 block
69 call setUnifRand(rand)
70 call getDummy()
71 end block
72 end subroutine
73
74 subroutine setUnifRandRNGX()
75 block
76 call setUnifRand(rngx, rand)
77 call getDummy()
78 end block
79 end subroutine
80
81 subroutine random_number()
82 block
83 intrinsic :: random_number
84 call random_number(rand)
85 call getDummy()
86 end block
87 end subroutine
88
89end program benchmark
Return a uniform random scalar or contiguous array of arbitrary rank of randomly uniformly distribute...
This is the derived type for declaring and generating objects of type xoshiro256ssw_type containing a...

Example Unix compile command via Intel ifort compiler
1#!/usr/bin/env sh
2rm main.exe
3ifort -fpp -standard-semantics -O3 -Wl,-rpath,../../../lib -I../../../inc main.F90 ../../../lib/libparamonte* -o main.exe
4./main.exe

Example Windows Batch compile command via Intel ifort compiler
1del main.exe
2set PATH=..\..\..\lib;%PATH%
3ifort /fpp /standard-semantics /O3 /I:..\..\..\include main.F90 ..\..\..\lib\libparamonte*.lib /exe:main.exe
4main.exe

Example Unix / MinGW compile command via GNU gfortran compiler
1#!/usr/bin/env sh
2rm main.exe
3gfortran -cpp -ffree-line-length-none -O3 -Wl,-rpath,../../../lib -I../../../inc main.F90 ../../../lib/libparamonte* -o main.exe
4./main.exe

Postprocessing of the benchmark output
1#!/usr/bin/env python
2
3import matplotlib.pyplot as plt
4import pandas as pd
5import numpy as np
6
7fontsize = 14
8
9df = pd.read_csv("main.out")
10
11
14
15ax = plt.figure(figsize = 1.25 * np.array([6.4,4.6]), dpi = 200)
16ax = plt.subplot()
17
18plt.plot( df.values[:, 0]
19 , df.values[:,1:]
20 , linewidth = 2
21 )
22
23plt.xticks(fontsize = fontsize)
24plt.yticks(fontsize = fontsize)
25ax.set_xlabel("Array Size", fontsize = fontsize)
26ax.set_ylabel("Runtime [ seconds ]", fontsize = fontsize)
27ax.set_title("Runtime:\nsetUnifRand() vs. random_number().\nLower is better.", fontsize = fontsize)
28ax.set_xscale("log")
29ax.set_yscale("log")
30plt.minorticks_on()
31plt.grid(visible = True, which = "both", axis = "both", color = "0.85", linestyle = "-")
32ax.tick_params(axis = "y", which = "minor")
33ax.tick_params(axis = "x", which = "minor")
34ax.legend ( list(df.columns.values[1:])
35 #, loc='center left'
36 #, bbox_to_anchor=(1, 0.5)
37 , fontsize = fontsize
38 )
39
40plt.tight_layout()
41plt.savefig("benchmark.setUnifRand_vs_random_number.runtime.png")
42
43
46
47ax = plt.figure(figsize = 1.25 * np.array([6.4,4.6]), dpi = 200)
48ax = plt.subplot()
49
50# baseline
51
52plt.plot( df.values[:, 0]
53 , np.ones( len(df["arraySize"].values) )
54 , linestyle = "-"
55 , linewidth = 2
56 )
57for colname in df.columns.values[2:]:
58 plt.plot( df.values[:, 0]
59 , df[colname].values / df.values[:, 1]
60 , linewidth = 2
61 )
62
63plt.xticks(fontsize = fontsize)
64plt.yticks(fontsize = fontsize)
65ax.set_xlabel("Array Size", fontsize = fontsize)
66ax.set_ylabel("Runtime Ratio", fontsize = fontsize)
67ax.set_title("""Uniform RNG Runtime Ratio: setUnifRand() to random_number().
68A value < 1 implies better performance of setUnifRand().""", fontsize = fontsize)
69ax.set_xscale("log")
70#ax.set_yscale("log")
71plt.minorticks_on()
72plt.grid(visible = True, which = "both", axis = "both", color = "0.85", linestyle = "-")
73ax.tick_params(axis = "y", which = "minor")
74ax.tick_params(axis = "x", which = "minor")
75ax.legend ( list(df.columns.values[1:])
76 #, bbox_to_anchor=(1, 0.5)
77 #, loc='center left'
78 , fontsize = fontsize
79 )
80
81plt.tight_layout()
82plt.savefig("benchmark.setUnifRand_vs_random_number.runtime.ratio.png")

Visualization of the benchmark output

Benchmark moral
  1. The default RNG in the procedures under the generic interface setUnifRand are simply wrappers around the intrinsic random number generator of Fortran random_number().
    As such, setUnifRand for generating random real numbers has \(\ms{5-10%}\) overhead with respect to the intrinsic random_number().
    Note Fortran does not have integer, logical, complex, or character uniform random RNG whereas setUnifRand provides a unified API for random numbers of all types.
  2. The RNGX is an acronym for xoshiro256ssw_type in the procedures under the generic interface setUnifRand.
    This random number generator, although unsafe for cryptographic purposes, is quite competitive and performant, even compared to the intrinsic Fortran compiler RNGs.


Benchmark :: The runtime performance of intrinsic random_number() vs. splitmix64_type vs. xoshiro256ssw_type.

1program benchmark
2
3 use pm_kind, only: SK, IK, LK, RKG => RK, IKG => IK, LKG => LK
6 use pm_distUnif, only: setUnifRand
7 use pm_bench, only: bench_type
8
9 implicit none
10
11 integer(IK) :: i, j, fileUnit
12 integer(IK) , parameter :: NSIM = 100000_IK
13 logical(LKG) :: dumm_LK = .false._LKG
14 logical(LKG) :: rand_LK(NSIM)
15 integer(IKG) :: rand_IK(NSIM)
16 real(RKG) :: rand_RK(NSIM)
17 type(bench_type) , allocatable :: bench(:)
18 type(splitmix64_type) :: splitmix64
19 type(xoshiro256ssw_type) :: xoshiro256ssw
20 splitmix64 = splitmix64_type()
21 xoshiro256ssw = xoshiro256ssw_type()
22
23 bench = [ bench_type(name = SK_"random_number_LK", exec = random_number_LK, overhead = setOverhead_LK) &
24 , bench_type(name = SK_"splitmix64_type_LK", exec = splitmix64_type_LK, overhead = setOverhead_LK) &
25 , bench_type(name = SK_"xoshiro256ssw_type_LK", exec = xoshiro256ssw_type_LK, overhead = setOverhead_LK) &
26 , bench_type(name = SK_"random_number_IK", exec = random_number_IK, overhead = setOverhead_LK) &
27 , bench_type(name = SK_"splitmix64_type_IK", exec = splitmix64_type_IK, overhead = setOverhead_IK) &
28 , bench_type(name = SK_"xoshiro256ssw_type_IK", exec = xoshiro256ssw_type_IK, overhead = setOverhead_IK) &
29 , bench_type(name = SK_"random_number_RK", exec = random_number_RK, overhead = setOverhead_RK) &
30 , bench_type(name = SK_"splitmix64_type_RK", exec = splitmix64_type_RK, overhead = setOverhead_RK) &
31 , bench_type(name = SK_"xoshiro256ssw_type_RK", exec = xoshiro256ssw_type_RK, overhead = setOverhead_RK) &
32 ]
33
34 write(*,"(*(g0,:,' '))")
35 write(*,"(*(g0,:,' '))") "splitmix64_type() vs. xoshiro256ssw_type()."
36 write(*,"(*(g0,:,' '))")
37
38 open(newunit = fileUnit, file = "main.out", status = "replace")
39
40 write(fileUnit, "(*(g0,:,','))") (bench(i)%name, i = 1, size(bench))
41 do i = 1, size(bench)
42 bench(i)%timing = bench(i)%getTiming()
43 end do
44 do j = 1, minval([(size(bench(i)%timing%values), i = 1, size(bench))])
45 write(fileUnit,"(*(g0,:,','))") (bench(i)%timing%values(j) / NSIM, i = 1, size(bench))
46 end do
47 write(*,"(*(g0,:,' '))")
48
49 close(fileUnit)
50
51contains
52
53 !%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
54 ! procedure wrappers.
55 !%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
56
57 subroutine setOverhead_LK()
58 call getDummy_LK()
59 end subroutine
60
61 subroutine setOverhead_IK()
62 call getDummy_IK()
63 end subroutine
64
65 subroutine setOverhead_RK()
66 call getDummy_RK()
67 end subroutine
68
69 subroutine getDummy_LK()
70 dumm_LK = dumm_LK .or. count(rand_LK) == 0
71 end subroutine
72
73 subroutine getDummy_IK()
74 dumm_LK = dumm_LK .or. any(rand_IK == 0_IKG)
75 end subroutine
76
77 subroutine getDummy_RK()
78 dumm_LK = dumm_LK .or. any(rand_RK == 0._RKG)
79 end subroutine
80
81 subroutine random_number_LK()
82#if 1
83 call setUnifRand(rand_LK)
84#else
85 block
86 real :: rand
87 call random_number(rand)
88 rand_LK = logical(rand < 0.5, LKG)
89 call getDummy_LK()
90 end block
91#endif
92 end subroutine
93
94 subroutine splitmix64_type_LK()
95 call setUnifRand(splitmix64, rand_LK)
96 call getDummy_LK()
97 end subroutine
98
99 subroutine xoshiro256ssw_type_LK()
100 call setUnifRand(xoshiro256ssw, rand_LK)
101 call getDummy_LK()
102 end subroutine
103
104
105 subroutine random_number_IK()
106 call setUnifRand(rand_IK)
107 call getDummy_IK()
108 end subroutine
109
110 subroutine splitmix64_type_IK()
111 call setUnifRand(splitmix64, rand_IK)
112 call getDummy_IK()
113 end subroutine
114
115 subroutine xoshiro256ssw_type_IK()
116 call setUnifRand(xoshiro256ssw, rand_IK)
117 call getDummy_IK()
118 end subroutine
119
120
121 subroutine random_number_RK()
122 call setUnifRand(rand_RK)
123 call getDummy_RK()
124 end subroutine
125
126 subroutine splitmix64_type_RK()
127 call setUnifRand(splitmix64, rand_RK)
128 call getDummy_RK()
129 end subroutine
130
131 subroutine xoshiro256ssw_type_RK()
132 call setUnifRand(xoshiro256ssw, rand_RK)
133 call getDummy_RK()
134 end subroutine
135
136end program benchmark
integer, parameter LK
The default logical kind in the ParaMonte library: kind(.true.) in Fortran, kind(....
Definition: pm_kind.F90:541
This is the derived type for declaring and generating objects of type splitmix64_type containing a un...

Example Unix compile command via Intel ifort compiler
1#!/usr/bin/env sh
2rm main.exe
3ifort -fpp -standard-semantics -O3 -Wl,-rpath,../../../lib -I../../../inc main.F90 ../../../lib/libparamonte* -o main.exe
4./main.exe

Example Windows Batch compile command via Intel ifort compiler
1del main.exe
2set PATH=..\..\..\lib;%PATH%
3ifort /fpp /standard-semantics /O3 /I:..\..\..\include main.F90 ..\..\..\lib\libparamonte*.lib /exe:main.exe
4main.exe

Example Unix / MinGW compile command via GNU gfortran compiler
1#!/usr/bin/env sh
2rm main.exe
3gfortran -cpp -ffree-line-length-none -O3 -Wl,-rpath,../../../lib -I../../../inc main.F90 ../../../lib/libparamonte* -o main.exe
4./main.exe

Postprocessing of the benchmark output
1#!/usr/bin/env python
2
3import matplotlib.pyplot as plt
4import pandas as pd
5import numpy as np
6
7import os
8dirname = os.path.basename(os.getcwd())
9
10fontsize = 14
11
12df = pd.read_csv("main.out", delimiter = ",")
13colnames = list(df.columns.values)
14
15df = pd.read_csv("main.out")
16
17
20
21ax = plt.figure(figsize = 1.25 * np.array([6.4,4.6]), dpi = 200)
22ax = plt.subplot()
23
24for colname in colnames:
25 plt.hist( np.log10(df[colname].values)
26 , histtype = "stepfilled"
27 #, density = True
28 , alpha = 0.7
29 #, bins = 30
30 )
31
32plt.xticks(fontsize = fontsize)
33plt.yticks(fontsize = fontsize)
34ax.set_xlabel("$\log_{10}$ ( Runtime [ seconds ] )", fontsize = fontsize)
35ax.set_ylabel("Count", fontsize = fontsize)
36#ax.set_title(" vs. ".join(colnames[1:])+"\nLower is better.", fontsize = fontsize)
37#ax.set_xscale("log")
38#ax.set_yscale("log")
39plt.minorticks_on()
40plt.grid(visible = True, which = "both", axis = "both", color = "0.85", linestyle = "-")
41ax.tick_params(axis = "y", which = "minor")
42ax.tick_params(axis = "x", which = "minor")
43ax.legend ( colnames
44 #, loc='center left'
45 #, bbox_to_anchor=(1, 0.5)
46 , fontsize = fontsize
47 )
48
49plt.tight_layout()
50plt.savefig("benchmark." + dirname + ".runtime.png")
51
52
55
56ax = plt.figure(figsize = 1.25 * np.array([6.4,4.6]), dpi = 200)
57ax = plt.subplot()
58
59for colname in colnames[1:]:
60 plt.hist( np.log10(df[colname].values / df[colnames[0]].values)
61 , histtype = "stepfilled"
62 #, density = True
63 , alpha = 0.7
64 #, bins = 30
65 )
66ax.legend ( colnames[1:]
67 #, loc='center left'
68 #, bbox_to_anchor=(1, 0.5)
69 , fontsize = fontsize
70 )
71
72plt.xticks(fontsize = fontsize)
73plt.yticks(fontsize = fontsize)
74ax.set_title("Runtime Ratio Comparison. Lower means faster.\nLower than 1 means faster than {}().".format(colnames[0]), fontsize = fontsize)
75ax.set_xlabel(r"$\log_{{10}}$ ( Runtime Ratio ) w.r.t. {}".format(colnames[0]), fontsize = fontsize)
76ax.set_ylabel("Count", fontsize = fontsize)
77plt.minorticks_on()
78plt.grid(visible = True, which = "both", axis = "both", color = "0.85", linestyle = "-")
79ax.tick_params(axis = "y", which = "minor")
80ax.tick_params(axis = "x", which = "minor")
81
82plt.tight_layout()
83plt.savefig("benchmark." + dirname + ".runtime.ratio.png")

Visualization of the benchmark output

Benchmark moral
  1. The xoshiro256ssg_type RNG greedily attempts to use as many randomly generated bits as possible in the output random values.
  2. The xoshiro256ssw_type RNG takes a wasteful approach of using at least one or more chunks of 64 randomly generated bits in the output random values.
  3. This fundamental difference between the two RNG types generally leads to faster random logical value generations with the greedy approach, because 64bits chunks translate to 64 logical values without updating the RNG state.
  4. However, the greedy approach leads to generally slower runtimes for real random value generation.
  5. Both greedy and wasteful RNGs appear to be much faster than the ParaMonte library wrappers for the implementations offered by GNU Fortran Compiler gfortran and Intel Classic Fortran Compiler ifort.
  6. Moral: If your application requires many logical random number generation, use the greedy xoshiro256ssg_type RNG.
    Conversely, if your application requires a mixture of random number generations of various types and kinds, use the wasteful xoshiro256ssw_type RNG.
Test:
test_pm_distUnif


Final Remarks


If you believe this algorithm or its documentation can be improved, we appreciate your contribution and help to edit this page's documentation and source file on GitHub.
For details on the naming abbreviations, see this page.
For details on the naming conventions, see this page.
This software is distributed under the MIT license with additional terms outlined below.

  1. If you use any parts or concepts from this library to any extent, please acknowledge the usage by citing the relevant publications of the ParaMonte library.
  2. If you regenerate any parts/ideas from this library in a programming environment other than those currently supported by this ParaMonte library (i.e., other than C, C++, Fortran, MATLAB, Python, R), please also ask the end users to cite this original ParaMonte library.

This software is available to the public under a highly permissive license.
Help us justify its continued development and maintenance by acknowledging its benefit to society, distributing it, and contributing to it.

Author:
Amir Shahmoradi, Oct 16, 2009, 11:14 AM, Michigan

Variable Documentation

◆ MODULE_NAME

character(*, SK), parameter pm_distUnif::MODULE_NAME = "@pm_distUnif"

Definition at line 284 of file pm_distUnif.F90.

◆ rngf

type(rngf_type) pm_distUnif::rngf

The scalar constant object of type rngf_type whose presence signified the use of the Fortran intrinsic random number generator (RNGF).

This constant is merely a convenience for making easier calls to routines that require a default RNGF.


Possible calling interfaces

type(rngf_type) :: rng = rngf
type(rngf_type) rngf
The scalar constant object of type rngf_type whose presence signified the use of the Fortran intrinsi...
This is a concrete derived type whose instances can be used to define/request the default uniform ran...
See also
rngf
isHead
getUnifCDF
getUnifRand
setUnifRand
getUnifRandState
setUnifRandState
rngu_type
rngf_type
splitmix64_type
xoshiro256ssw_type
getUnifRandStateSize
Bug:

Status: Unresolved
Source: Intel Classic Fortran Compiler ifort version 2021.8.0 20221119
Description: Intel Classic Fortran Compiler ifort cannot handle the creation of a module constant of type rngf_type as done for this object, yielding the following error.
error #9066: A generic function reference is not permitted in a constant expression. [CONSTRUCTFRNG]

GNU compiler compiles and runs the code without complaining.

Remedy (as of ParaMonte Library version 2.0.0): For now, the parameter attribute is removed from the declaration of rngf.


Final Remarks


If you believe this algorithm or its documentation can be improved, we appreciate your contribution and help to edit this page's documentation and source file on GitHub.
For details on the naming abbreviations, see this page.
For details on the naming conventions, see this page.
This software is distributed under the MIT license with additional terms outlined below.

  1. If you use any parts or concepts from this library to any extent, please acknowledge the usage by citing the relevant publications of the ParaMonte library.
  2. If you regenerate any parts/ideas from this library in a programming environment other than those currently supported by this ParaMonte library (i.e., other than C, C++, Fortran, MATLAB, Python, R), please also ask the end users to cite this original ParaMonte library.

This software is available to the public under a highly permissive license.
Help us justify its continued development and maintenance by acknowledging its benefit to society, distributing it, and contributing to it.

Author:
Amir Shahmoradi, September 1, 2017, 12:00 AM, Institute for Computational Engineering and Sciences (ICES), The University of Texas Austin

Definition at line 2886 of file pm_distUnif.F90.

◆ xoshiro256ssJump128

integer(IK64), dimension(xoshiro256ssStateSize), parameter pm_distUnif::xoshiro256ssJump128 = [ +1733541517147835066_IK64 , -3051731464161248980_IK64 , -6244198995065845334_IK64 , +4155657270789760540_IK64 ]

The constant vector of size xoshiro256ssStateSize of type integer of kind IK64 containing the state jump for the Xoshiro256** random number generator.

This state jump can be passed to the constructor of xoshiro256ssw_type to request an RNG whose state starts at imageID * 2**128 steps (i.e., random number generations) ahead of the RNG constructed with imageID = 1.
Using this jump, one can generate 2**128 independent RNG sequences each of which has a period of 2**128 in parallel applications.
For more information see the documentation of xoshiro256ssg_type and xoshiro256ssw_type.

The elements of this constant vector are obtained by transferring the following unsigned integers to signed values.

integer(IK64) , parameter :: xoshiro256ssJump128(xoshiro256ssStateSize) = [ transfer(Z"180ec6d33cfd0aba", 0_IK64) &
, transfer(Z"d5a61266f0c9392c", 0_IK64) &
, transfer(Z"a9582618e03fc9aa", 0_IK64) &
, transfer(Z"39abdc4529b1661c", 0_IK64) ]
See also
xoshiro256ssw_type
xoshiro256ssJump128
xoshiro256ssJump192
xoshiro256ssw_typer
xoshiro256ssStateSize


Final Remarks


If you believe this algorithm or its documentation can be improved, we appreciate your contribution and help to edit this page's documentation and source file on GitHub.
For details on the naming abbreviations, see this page.
For details on the naming conventions, see this page.
This software is distributed under the MIT license with additional terms outlined below.

  1. If you use any parts or concepts from this library to any extent, please acknowledge the usage by citing the relevant publications of the ParaMonte library.
  2. If you regenerate any parts/ideas from this library in a programming environment other than those currently supported by this ParaMonte library (i.e., other than C, C++, Fortran, MATLAB, Python, R), please also ask the end users to cite this original ParaMonte library.

This software is available to the public under a highly permissive license.
Help us justify its continued development and maintenance by acknowledging its benefit to society, distributing it, and contributing to it.

Author:
Fatemeh Bagheri, Wednesday 12:20 AM, October 13, 2021, Dallas, TX

Definition at line 2675 of file pm_distUnif.F90.

◆ xoshiro256ssJump192

integer(IK64), dimension(xoshiro256ssStateSize), parameter pm_distUnif::xoshiro256ssJump192 = [ +8566230491382795199_IK64 , -4251311993797857357_IK64 , +8606660816089834049_IK64 , +4111957640723818037_IK64 ]

The constant vector of size xoshiro256ssStateSize of type integer of kind IK64 containing the state jump for the Xoshiro256** random number generator.

This state jump can be passed to the constructor of xoshiro256ssw_type to request an RNG whose state starts at imageID * 2**192 steps (i.e., random number generations) ahead of the RNG constructed with imageID = 1.
Using this jump, one can generate 2**64 independent RNG sequences each of which has a period of 2**192 in parallel applications.
For more information see the documentation of xoshiro256ssg_type and xoshiro256ssw_type.

The elements of this constant vector are obtained by transferring the following unsigned integers to signed values.

integer(IK64) , parameter :: xoshiro256ssJump192(xoshiro256ssStateSize) = [ transfer(Z"76e15d3efefdcbbf", 0_IK64) &
, transfer(Z"c5004e441c522fb3", 0_IK64) &
, transfer(Z"77710069854ee241", 0_IK64) &
, transfer(Z"39109bb02acbe635", 0_IK64) ]
See also
xoshiro256ssw_type
xoshiro256ssJump128
xoshiro256ssJump192
xoshiro256ssw_typer
xoshiro256ssStateSize


Final Remarks


If you believe this algorithm or its documentation can be improved, we appreciate your contribution and help to edit this page's documentation and source file on GitHub.
For details on the naming abbreviations, see this page.
For details on the naming conventions, see this page.
This software is distributed under the MIT license with additional terms outlined below.

  1. If you use any parts or concepts from this library to any extent, please acknowledge the usage by citing the relevant publications of the ParaMonte library.
  2. If you regenerate any parts/ideas from this library in a programming environment other than those currently supported by this ParaMonte library (i.e., other than C, C++, Fortran, MATLAB, Python, R), please also ask the end users to cite this original ParaMonte library.

This software is available to the public under a highly permissive license.
Help us justify its continued development and maintenance by acknowledging its benefit to society, distributing it, and contributing to it.

Author:
Fatemeh Bagheri, Wednesday 12:20 AM, October 13, 2021, Dallas, TX

Definition at line 2715 of file pm_distUnif.F90.

◆ xoshiro256ssStateSize

integer(IK), parameter pm_distUnif::xoshiro256ssStateSize = 4_IK

The constant scalar of type integer of default kind IK containing the size of the state vector of Xoshiro256** random number generator.

For more information see the documentation of xoshiro256ssg_type and xoshiro256ssw_type.

See also
xoshiro256ssw_type
xoshiro256ssJump128
xoshiro256ssJump192
xoshiro256ssw_typer
xoshiro256ssStateSize


Final Remarks


If you believe this algorithm or its documentation can be improved, we appreciate your contribution and help to edit this page's documentation and source file on GitHub.
For details on the naming abbreviations, see this page.
For details on the naming conventions, see this page.
This software is distributed under the MIT license with additional terms outlined below.

  1. If you use any parts or concepts from this library to any extent, please acknowledge the usage by citing the relevant publications of the ParaMonte library.
  2. If you regenerate any parts/ideas from this library in a programming environment other than those currently supported by this ParaMonte library (i.e., other than C, C++, Fortran, MATLAB, Python, R), please also ask the end users to cite this original ParaMonte library.

This software is available to the public under a highly permissive license.
Help us justify its continued development and maintenance by acknowledging its benefit to society, distributing it, and contributing to it.

Author:
Fatemeh Bagheri, Wednesday 12:20 AM, October 13, 2021, Dallas, TX

Definition at line 2641 of file pm_distUnif.F90.

◆ xoshiro256ssStreamBitSize

integer(IK), parameter pm_distUnif::xoshiro256ssStreamBitSize = int(bit_size(0_IK64), IK)

The constant scalar of type integer of default kind containing the number of binary digits of the stream component Xoshiro256** random number generator.

By definition, this number is 64, because the type kind parameter of stream is IK64.

See also
xoshiro256ssw_type
xoshiro256ssJump128
xoshiro256ssJump192
xoshiro256ssw_typer
xoshiro256ssStateSize


Final Remarks


If you believe this algorithm or its documentation can be improved, we appreciate your contribution and help to edit this page's documentation and source file on GitHub.
For details on the naming abbreviations, see this page.
For details on the naming conventions, see this page.
This software is distributed under the MIT license with additional terms outlined below.

  1. If you use any parts or concepts from this library to any extent, please acknowledge the usage by citing the relevant publications of the ParaMonte library.
  2. If you regenerate any parts/ideas from this library in a programming environment other than those currently supported by this ParaMonte library (i.e., other than C, C++, Fortran, MATLAB, Python, R), please also ask the end users to cite this original ParaMonte library.

This software is available to the public under a highly permissive license.
Help us justify its continued development and maintenance by acknowledging its benefit to society, distributing it, and contributing to it.

Author:
Fatemeh Bagheri, Wednesday 12:20 AM, October 13, 2021, Dallas, TX

Definition at line 2619 of file pm_distUnif.F90.