ParaMonte Fortran 2.0.0
Parallel Monte Carlo and Machine Learning Library
See the latest version documentation.
pm_mathLogSumExp::getLogSumExp Interface Reference

Generate and return the natural logarithm of the sum of the exponential of the input array robustly (without numerical overflow). More...

Detailed Description

Generate and return the natural logarithm of the sum of the exponential of the input array robustly (without numerical overflow).

Parameters
[in]array: The input contiguous array of shape (:) of either,
  • type real of kind any supported by the processor (e.g., RK, RK32, RK64, or RK128) or,
  • type complex of kind any supported by the processor (e.g., CK, CK32, CK64, or CK128),
whose natural logarithm of sum of the exponential of its elements will be generated and returned.
[in]maxArray: The input scalar of the same type and kind as the input array representing the maximum value in array (i.e., maxArray = maxval(array)).
If the input array is of complex type, then only real component must be considered in the computation of the maximum value.
Specifying this argument, if known a priori, will significantly aid the runtime performance.
(optional. It must be present if the input argument control is also present.)
control: The input scalar object that can be,
  1. the constant selection or equivalently, an object of type selection_type.
    Specifying this option enables the runtime checks for underflow occurrence via branching and dynamic dispatch.
    Enabling this option can aid runtime efficiency when the division of a significant number of elements of array (for example, half or more) by maxArray causes underflow.
    This option avoids the computation of an exponentiation term, leading to better runtime efficiency.
    Note that exponentiation is highly expensive (on the order of ~200 CPU cycles).
    See the relevant benchmark here.
The presence of this argument is merely for compile-time resolution of the procedures of this generic interface.
(optional. If missing, a sequence control flow will be assumed without dynamic dispatch.)
Returns
logSumExp : The output scalar of the same type and kind as the input array containing the natural logarithm of the sum of the exponential of the input array robustly (without numerical overflow).


Possible calling interfaces

use pm_mathLogSumExp, only: getLogSumExp, selection
logSumExp = getLogSumExp(array(1:np), maxArray)
logSumExp = getLogSumExp(array(1:np), maxArray, control)
Generate and return the natural logarithm of the sum of the exponential of the input array robustly (...
This module contains the procedures and interfaces for computing the natural logarithm of the sum of ...
Warning
The condition real(maxArray) == maxval(real(array)) must hold.
This condition is verified only if the library is built with the preprocessor macro CHECK_ENABLED=1.
The pure procedure(s) documented herein become impure when the ParaMonte library is compiled with preprocessor macro CHECK_ENABLED=1.
By default, these procedures are pure in release build and impure in debug and testing builds.
See also
getLog1p
get1mexp
getMinMax
setMinMax
getLogAddExp
getLogSubExp
getLogSumExp
getCumPropExp
setCumPropExp
getCumSum
setCumSum


Example usage

1program example
2
3 use pm_kind, only: SK
4 use pm_io, only: display_type
5 use pm_kind, only: IK, CKS, CKD, CKH, RKS, RKD, RKH
7
8 implicit none
9
10 type(display_type) :: disp
11 disp = display_type(file = "main.out.F90")
12
13 call disp%skip()
14 call disp%show("!%%%%%%%%%%")
15 call disp%show("32-bit real")
16 call disp%show("!%%%%%%%%%%")
17 call disp%skip()
18
19 call disp%skip()
20 call disp%show("exp( getLogSumExp( array = log([1._RKS, 2._RKS, 3._RKS, 4._RKS]), maxArray = log(4._RKS) ) )")
21 call disp%show( exp( getLogSumExp( array = log([1._RKS, 2._RKS, 3._RKS, 4._RKS]), maxArray = log(4._RKS) ) ) )
22 call disp%skip()
23
24 call disp%skip()
25 call disp%show("!%%%%%%%%%%")
26 call disp%show("64-bit real")
27 call disp%show("!%%%%%%%%%%")
28 call disp%skip()
29
30 call disp%skip()
31 call disp%show("exp( getLogSumExp( array = log([1._RKD, 2._RKD, 3._RKD, 4._RKD]), maxArray = log(4._RKD) ) )")
32 call disp%show( exp( getLogSumExp( array = log([1._RKD, 2._RKD, 3._RKD, 4._RKD]), maxArray = log(4._RKD) ) ) )
33 call disp%skip()
34
35 call disp%skip()
36 call disp%show("!%%%%%%%%%%%")
37 call disp%show("128-bit real")
38 call disp%show("!%%%%%%%%%%%")
39 call disp%skip()
40
41 call disp%skip()
42 call disp%show("exp( getLogSumExp( array = log([1._RKH, 2._RKH, 3._RKH, 4._RKH]), maxArray = log(4._RKH) ) )")
43 call disp%show( exp( getLogSumExp( array = log([1._RKH, 2._RKH, 3._RKH, 4._RKH]), maxArray = log(4._RKH) ) ) )
44 call disp%skip()
45
46 call disp%skip()
47 call disp%show("!%%%%%%%%%%%%%")
48 call disp%show("32-bit complex")
49 call disp%show("!%%%%%%%%%%%%%")
50 call disp%skip()
51
52 call disp%skip()
53 call disp%show("exp( getLogSumExp( array = cmplx(log([(1._CKS,-1._CKS), (2._CKS,-2._CKS), (3._CKS,-3._CKS)]), kind = CKS), maxArray = log((3._CKS,-3._CKS)) ) )")
54 call disp%show( exp( getLogSumExp( array = cmplx(log([(1._CKS,-1._CKS), (2._CKS,-2._CKS), (3._CKS,-3._CKS)]), kind = CKS), maxArray = log((3._CKS,-3._CKS)) ) ) )
55 call disp%skip()
56
57 call disp%skip()
58 call disp%show("!%%%%%%%%%%%%%")
59 call disp%show("64-bit complex")
60 call disp%show("!%%%%%%%%%%%%%")
61 call disp%skip()
62
63 call disp%skip()
64 call disp%show("exp( getLogSumExp( array = cmplx(log([(1._CKD,-1._CKD), (2._CKD,-2._CKD), (3._CKD,-3._CKD)]), kind = CKD), maxArray = log((3._CKD,-3._CKD)) ) )")
65 call disp%show( exp( getLogSumExp( array = cmplx(log([(1._CKD,-1._CKD), (2._CKD,-2._CKD), (3._CKD,-3._CKD)]), kind = CKD), maxArray = log((3._CKD,-3._CKD)) ) ) )
66 call disp%skip()
67
68 call disp%skip()
69 call disp%show("!%%%%%%%%%%%%%%")
70 call disp%show("128-bit complex")
71 call disp%show("!%%%%%%%%%%%%%%")
72 call disp%skip()
73
74 call disp%skip()
75 call disp%show("exp( getLogSumExp( array = cmplx(log([(1._CKH,-1._CKH), (2._CKH,-2._CKH), (3._CKH,-3._CKH)]), kind = CKH), maxArray = log((3._CKH,-3._CKH)) ) )")
76 call disp%show( exp( getLogSumExp( array = cmplx(log([(1._CKH,-1._CKH), (2._CKH,-2._CKH), (3._CKH,-3._CKH)]), kind = CKH), maxArray = log((3._CKH,-3._CKH)) ) ) )
77 call disp%skip()
78
79end program example
This is a generic method of the derived type display_type with pass attribute.
Definition: pm_io.F90:11726
This is a generic method of the derived type display_type with pass attribute.
Definition: pm_io.F90:11508
This module contains classes and procedures for input/output (IO) or generic display operations on st...
Definition: pm_io.F90:252
type(display_type) disp
This is a scalar module variable an object of type display_type for general display.
Definition: pm_io.F90:11393
This module defines the relevant Fortran kind type-parameters frequently used in the ParaMonte librar...
Definition: pm_kind.F90:268
integer, parameter CKH
The scalar integer constant of intrinsic default kind, representing the highest-precision complex kin...
Definition: pm_kind.F90:843
integer, parameter CKS
The single-precision complex kind in Fortran mode. On most platforms, this is a 32-bit real kind.
Definition: pm_kind.F90:570
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 CKD
The double precision complex kind in Fortran mode. On most platforms, this is a 64-bit real kind.
Definition: pm_kind.F90:571
integer, parameter RKD
The double precision real kind in Fortran mode. On most platforms, this is an 64-bit real kind.
Definition: pm_kind.F90:568
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
integer, parameter RKH
The scalar integer constant of intrinsic default kind, representing the highest-precision real kind t...
Definition: pm_kind.F90:858
integer, parameter RKS
The single-precision real kind in Fortran mode. On most platforms, this is an 32-bit real kind.
Definition: pm_kind.F90:567
Generate and return an object of type display_type.
Definition: pm_io.F90:10282

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

Example output
1
2!%%%%%%%%%%
332-bit real
4!%%%%%%%%%%
5
6
7exp( getLogSumExp( array = log([1._RKS, 2._RKS, 3._RKS, 4._RKS]), maxArray = log(4._RKS) ) )
8+10.0000000
9
10
11!%%%%%%%%%%
1264-bit real
13!%%%%%%%%%%
14
15
16exp( getLogSumExp( array = log([1._RKD, 2._RKD, 3._RKD, 4._RKD]), maxArray = log(4._RKD) ) )
17+10.000000000000002
18
19
20!%%%%%%%%%%%
21128-bit real
22!%%%%%%%%%%%
23
24
25exp( getLogSumExp( array = log([1._RKH, 2._RKH, 3._RKH, 4._RKH]), maxArray = log(4._RKH) ) )
26+10.0000000000000000000000000000000000
27
28
29!%%%%%%%%%%%%%
3032-bit complex
31!%%%%%%%%%%%%%
32
33
34exp( getLogSumExp( array = cmplx(log([(1._CKS,-1._CKS), (2._CKS,-2._CKS), (3._CKS,-3._CKS)]), kind = CKS), maxArray = log((3._CKS,-3._CKS)) ) )
35(+6.00000048, -6.00000048)
36
37
38!%%%%%%%%%%%%%
3964-bit complex
40!%%%%%%%%%%%%%
41
42
43exp( getLogSumExp( array = cmplx(log([(1._CKD,-1._CKD), (2._CKD,-2._CKD), (3._CKD,-3._CKD)]), kind = CKD), maxArray = log((3._CKD,-3._CKD)) ) )
44(+6.0000000000000000, -5.9999999999999991)
45
46
47!%%%%%%%%%%%%%%
48128-bit complex
49!%%%%%%%%%%%%%%
50
51
52exp( getLogSumExp( array = cmplx(log([(1._CKH,-1._CKH), (2._CKH,-2._CKH), (3._CKH,-3._CKH)]), kind = CKH), maxArray = log((3._CKH,-3._CKH)) ) )
53(+6.00000000000000000000000000000000000, -5.99999999999999999999999999999999923)
54
55
Benchmarks:


Benchmark :: The effects of control on runtime efficiency

The following program compares the runtime performance of getLogSumExp algorithm with and without checking for underflows.
1! Test the performance of `getLogSumExp()` with and without underflow check vs. direct computation of `logSumExp`.
2program benchmark
3
4 use iso_fortran_env, only: error_unit
5 use pm_kind, only: IK, LK, RK, SK
6 use pm_bench, only: bench_type
7
8 implicit none
9
10 integer(IK) :: i
11 integer(IK) :: iarr
12 integer(IK) :: fileUnitN
13 integer(IK) :: fileUnitU
14 integer(IK) , parameter :: NARR = 11_IK
15 integer(IK) , parameter :: NBENCH = 3_IK
16 integer(IK) :: arraySize(NARR)
17 real(RK) :: dummySum = 0._RK
18 real(RK) :: maxArray
19 real(RK) , allocatable :: array(:)
20 real(RK) :: logSumExp
21 type(bench_type) :: bench(2,NBENCH)
22 logical(LK) :: underflowEnabled
23
24 bench(1:2,1) = bench_type(name = SK_"getLogSumExpMaxSequence", exec = getLogSumExpMaxSequence , overhead = setOverhead)
25 bench(1:2,2) = bench_type(name = SK_"getLogSumExpMaxSelection", exec = getLogSumExpMaxSelection , overhead = setOverhead)
26 bench(1:2,3) = bench_type(name = SK_"getLogSumExpDirect", exec = getLogSumExpDirect, overhead = setOverhead)
27
28 arraySize = [( 2_IK**iarr, iarr = 1_IK, NARR )]
29
30 write(*,"(*(g0,:,' '))")
31 write(*,"(*(g0,:,' '))") "getLogSumExp(...) vs. getLogSumExp(..., control = selection) vs. direct method."
32 write(*,"(*(g0,:,' '))")
33
34 open(newunit = fileUnitN, file = "main.normal.out", status = "replace")
35 open(newunit = fileUnitU, file = "main.underflow.out", status = "replace")
36
37 write(fileUnitN, "(*(g0,:,','))") "arraySize", (bench(1,i)%name, i = 1, NBENCH)
38 write(fileUnitU, "(*(g0,:,','))") "arraySize", (bench(2,i)%name, i = 1, NBENCH)
39
40 loopOverArraySize: do iarr = 1, NARR
41
42 allocate(array(arraySize(iarr)))
43 write(*,"(*(g0,:,' '))") "Benchmarking with array size", arraySize(iarr)
44
45 underflowEnabled = .false._LK
46 do i = 1, NBENCH
47 !do i = NBENCH, 1, -1
48 bench(1,i)%timing = bench(1,i)%getTiming(minsec = 0.07_RK)
49 end do
50 write(fileUnitN,"(*(g0,:,','))") arraySize(iarr), (bench(1,i)%timing%mean, i = 1, NBENCH)
51
52 underflowEnabled = .true._LK
53 do i = 1, NBENCH
54 bench(2,i)%timing = bench(2,i)%getTiming(minsec = 0.07_RK)
55 end do
56 write(fileUnitU,"(*(g0,:,','))") arraySize(iarr), (bench(2,i)%timing%mean, i = 1, NBENCH)
57
58 deallocate(array)
59
60 end do loopOverArraySize
61 write(*,"(*(g0,:,' '))") dummySum
62 write(*,"(*(g0,:,' '))")
63
64 close(fileUnitN)
65 close(fileUnitU)
66
67contains
68
69 !%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
70 ! procedure wrappers.
71 !%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
72
73 subroutine setOverhead()
74 call setArray()
75 call getDummy()
76 end subroutine
77
78 subroutine setArray()
79 call random_number(array)
80 if (underflowEnabled) array = array * (maxexponent(0._RK) - minexponent(0._RK)) + minexponent(0._RK)
81 maxArray = maxval(array)
82 end subroutine
83
84 subroutine getDummy()
85 dummySum = dummySum + logSumExp
86 end subroutine
87
88 subroutine getLogSumExpMaxSequence()
90 call setArray()
91 logSumExp = getLogSumExp(array, maxArray)
92 call getDummy()
93 end subroutine
94
95 subroutine getLogSumExpMaxSelection()
96 use pm_mathLogSumExp, only: getLogSumExp, selection
97 call setArray()
98 logSumExp = getLogSumExp(array, maxArray, selection)
99 call getDummy()
100 end subroutine
101
102 subroutine getLogSumExpDirect()
103 call setArray()
104 logSumExp = maxArray + log(sum(exp(array - maxArray)))
105 call getDummy()
106 end subroutine
107
108end program benchmark
Generate and return an object of type timing_type containing the benchmark timing information and sta...
Definition: pm_bench.F90:574
This module contains abstract interfaces and types that facilitate benchmarking of different procedur...
Definition: pm_bench.F90:41
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 LK
The default logical kind in the ParaMonte library: kind(.true.) in Fortran, kind(....
Definition: pm_kind.F90:541
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
7import os
8dirname = os.path.basename(os.getcwd())
9
10fontsize = 14
11
12
15
16df = pd.read_csv("main.normal.out")
17colnames = list(df.columns.values)
18
19ax = plt.figure(figsize = 1.25 * np.array([6.4,4.6]), dpi = 200)
20ax = plt.subplot()
21
22for colname in colnames[1:]:
23 plt.plot( df[colnames[0]].values
24 #, np.ones(len(df[colnames[0]].values))
25 , df[colname].values / df[colnames[1]].values
26 , linewidth = 2
27 #, linestyle = "-"
28 )
29
30plt.xticks(fontsize = fontsize)
31plt.yticks(fontsize = fontsize)
32ax.set_xlabel("Array Size", fontsize = fontsize)
33ax.set_ylabel("Runtime Ratio ( W.R.T. cenabled = .false. )", fontsize = fontsize)
34ax.set_title("Performance when the input arrays\ncause no underflow instances.\nLower is better.", fontsize = fontsize)
35ax.set_xscale("log")
36#ax.set_yscale("log")
37plt.minorticks_on()
38plt.grid(visible = True, which = "both", axis = "both", color = "0.85", linestyle = "-")
39ax.tick_params(axis = "y", which = "minor")
40ax.tick_params(axis = "x", which = "minor")
41ax.legend ( colnames[1:]
42 #, loc='center left'
43 #, bbox_to_anchor=(1, 0.5)
44 , fontsize = fontsize
45 )
46
47plt.tight_layout()
48plt.savefig("benchmark." + dirname + ".normal.png")
49
50
53
54df = pd.read_csv("main.underflow.out")
55colnames = list(df.columns.values)
56
57ax = plt.figure(figsize = 1.25 * np.array([6.4,4.6]), dpi = 200)
58ax = plt.subplot()
59
60for colname in colnames[1:]:
61 plt.plot( df[colnames[0]].values
62 #, np.ones(len(df[colnames[0]].values))
63 , df[colname].values / df[colnames[1]].values
64 , linewidth = 2
65 #, linestyle = "-"
66 )
67
68plt.xticks(fontsize = fontsize)
69plt.yticks(fontsize = fontsize)
70ax.set_xlabel("Array Size", fontsize = fontsize)
71ax.set_ylabel("Runtime Ratio W.R.T. {})".format(colnames[1]), fontsize = fontsize)
72ax.set_title("Performance when the input arrays\ncause many underflow instances.\nLower is better.", fontsize = fontsize)
73ax.set_xscale("log")
74#ax.set_yscale("log")
75plt.minorticks_on()
76plt.grid(visible = True, which = "both", axis = "both", color = "0.85", linestyle = "-")
77ax.tick_params(axis = "y", which = "minor")
78ax.tick_params(axis = "x", which = "minor")
79ax.legend ( colnames[1:]
80 #, loc='center left'
81 #, bbox_to_anchor=(1, 0.5)
82 , fontsize = fontsize
83 )
84
85plt.tight_layout()
86plt.savefig("benchmark." + dirname + ".underflow.png")

Visualization of the benchmark output

Benchmark moral
  1. If the input array has many (half the size of array or more) elements whose division by the maxval(array) causes underflow, then setting control = selection to allow branching will likely result in a faster runtime.
    Conversely, if the divisions are not expected to cause any or too many underflows, then removing the input argument control when calling getLogSumExp will lead to a better runtime performance (at the expense of occasional expensive but redundant exponentiation operations).
  2. Note the performance of manual computation against getLogSumExp with or without setting the optional argument control.
Test:
test_pm_mathLogSumExp
Todo:
Normal Priority: This generic interface can be expanded to include input arrays with Weights.


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, Tuesday 08:49 PM, August 10, 2021, Dallas, TX

Definition at line 147 of file pm_mathLogSumExp.F90.


The documentation for this interface was generated from the following file: