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

This module contains the procedures and interfaces for evaluating the relative or absolute proximity of two numeric values.
More...

Data Types

interface  isClose
 Generate and return .true. if the two input values are sufficiently close to each other within the specified tolerances.
More...
 
type  mean_type
 This is an empty derived type that is exclusively used to differentiate the procedures within the generic interface isClose().
More...
 
type  reference_type
 This is an empty derived type that is exclusively used to differentiate the procedures within the generic interface isClose().
More...
 
type  strong_type
 This is an empty derived type that is exclusively used to differentiate the procedures within the generic interface isClose().
More...
 
type  weak_type
 This is an empty derived type that is exclusively used to differentiate the procedures within the generic interface isClose().
More...
 

Variables

character(*, SK), parameter MODULE_NAME = "@pm_mathCompare"
 
type(reference_type), parameter REFERENCE = reference_type()
 This is a convenience constant object of type reference_type() that is exclusively provided to distinguish and facilitate the use of the procedures under the generic interface isClose().
More...
 
type(strong_type), parameter STRONG = strong_type()
 This is a convenience constant object of type strong_type() that is exclusively provided to distinguish and facilitate the use of the procedures under the generic interface isClose().
More...
 
type(weak_type), parameter WEAK = weak_type()
 This is a convenience constant object of type weak_type() that is exclusively provided to distinguish and facilitate the use of the procedures under the generic interface isClose().
More...
 
type(mean_type), parameter MEAN = mean_type()
 This is a convenience constant object of type mean_type() that is exclusively provided to distinguish and facilitate the use of the procedures under the generic interface isClose().
More...
 

Detailed Description

This module contains the procedures and interfaces for evaluating the relative or absolute proximity of two numeric values.

Floating point values contain limited precision.
As such, an accurate equality comparison of real or complex values is frequently impossible or insufficient.
Instead, one frequently needs to determine whether a computed value is close enough to an expected value, without requiring them to be exactly equal.

The generic interfaces of this module implement different methods of determining whether two numeric values is sufficiently close to each other,

isClose(x, y, reltol = epsilon(x), abstol = tiny(x))
isClose(x, y, method, reltol = epsilon(x), abstol = tiny(x))

where,

  • x and y are the two values to be tested for relative closeness,
  • method is a scalar parameter that determines the method of computing the relative error,
  • reltol is the relative tolerance for the default or the specified choice of method,
  • abstol is a minimum absolute tolerance level, useful for comparisons near zero.
Note
  • The IEEE 754 special values of NaN, inf, and -inf are handled according to IEEE rules.
  • Specifically, NaN is not considered close to any other value, including NaN.
  • Two +inf values are considered close to each other at all times.
  • Two -inf values are considered close to each other at all times.
  • Although the generic interfaces of this module only accept real and complex values, values integer type can be also compared with each other by first converting them to real values.
  • For complex values, the relative and absolute tolerances are specified as real and the absolute value of the complex values will be used for scaling and comparison.
  • If complex tolerances are desired, simply pass the absolute values (abs(reltol) and abs(abstol)) of the complex tolerances.

Behavior near zero
Relative comparison is problematic if either value is zero.
Mathematically speaking, no value is small relative to zero.
Computationally speaking, if either value is zero, the difference is the absolute value of the other non-zero value, and the computed absolute tolerance will be reltol times that value.
When reltol < 1, the difference will never be less than the tolerance.
Nevertheless, there are many instances where there is a need to know if a computed value is close to zero.
This calls for an absolute tolerance test.

Behavior for small values straddling zero
There is a similar issue if the two values to be compared straddle zero.
If x is approximately equal to -y, then x and y will never be computed as close.
To handle such cases, the optional parameter, abstol can be used to set a minimum tolerance used in the case of very small or zero computed relative tolerance.
That is, the values will be always be considered close if the difference between them is less than abstol.
If the input reltol argument is set 0., then only the absolute tolerance will effect the result.
Although trivial, this allows the generic interfaces to be also used for purely absolute tolerance checks.

Relative vs. Absolute Difference
There are two ways to define the proximity of two numeric values:

  1. Absolute difference: abs(x - y),
  2. Relative difference: abs(x - y) / sigma, where sigma is called the scale factor.
    Usually, the scale factor is some function of the values under consideration, for instance:
    1. Reference Scaling (asymmetric) : The absolute value of one of the input values.
    2. Weak Scaling (symmetric) : The maximum absolute value of the two.
    3. Strong Scaling (symmetric) : The minimum absolute value of the two.
    4. mean Scaling (symmetric) : The absolute value of the arithmetic mean of the two.
    These leads to the following possibilities for determining if two values, x and y, are close to each other.
    1. Reference Scaling : abs(x - y) <= reltol * abs(x).
    2. Weak Scaling : abs(x - y) <= reltol * max(abs(x), abs(y)).
    3. Strong Scaling : abs(x - y) <= reltol * min(abs(x), abs(y)).
    4. mean Scaling : abs(x - y) <= reltol * (x + y) / 2.
    The naming convention for the weak vs. strong scaling stems from the following facts:
    • The weak scaling test can be also written as: (abs(x - y) <= abs(reltol * x)) .or. (abs(x - y) <= abs(reltol * y)).
    • The strong scaling test can be also written as: (abs(x - y) <= abs(reltol * x)) .and. (abs(x - y) <= abs(reltol * y)).
    Each of the above proximity testing methods can lead to slightly different results.
    However, if reltol is small compared to the two input values, the differences between the methods are negligible, frequently less than available floating point precision.
    How significantly different are the relative-tolerance testing methods?
    When selecting a method to determine closeness, one might want to know how much of a difference it could make to use one test or the other.
    How many values are there (or what range of values) that will pass one test, but not the other?
    The largest difference is between the weak and strong scaling methods above where the tolerance is scaled by either the larger or smaller of the values.
    Suppose delta is the difference between the allowable absolute tolerance defined by the larger value and that defined by the smaller value.
    That is, delta is the amount that the two input values need to be different in order to get a different result from the two weak and strong scaling methods.
    Without loss of generality, suppose x is the larger value and that both x and y are positive. Therefore,
    delta = reltol * (x - y)
    delta / reltol = (x - y)
    The largest absolute difference that would pass the test, (x - y), equals the tolerance times the larger value.
    (x - y) = reltol * x
    Substituting into the expression for delta, one gets,
    delta / reltol = reltol * x
    delta = reltol**2 * x
    For example, if x = 10; y = 9; reltol = 0.1, then,
    • The maximum tolerance is reltol * x == 0.1 * 10 == 1.0.
    • The minimum tolerance is reltol * y == 0.1 * 9.0 == 0.9.
    Therefore, delta = (1.0 - 0.9) = 0.1 or reltol**2 * x = 0.1**2 * 10 = .1.
    The absolute difference between the maximum and minimum tolerance tests in this case could be therefore, substantial.
    However, the primary use case for the generic interfaces of this module is testing the results of computations.
    In such cases, a relative tolerance is likely to be selected of much smaller magnitudes.
    For example, a relative tolerance of 1e-8 is about half the precision available in a real64.
    Therefore, the difference between the two tests would be 1e-8**2 * x or 1e-16 * x, which is close to the limit of precision of real64.
    As such, a relative tolerance of reltol = 1e-9 (or smaller) would eliminate the difference between the two tests down to the limits of a real64 precision.
    That is, each of the four methods will yield exactly the same results for all x and y values.
    The symmetry of Relative Proximity
    A relative comparison can be either symmetric or asymmetric.
    • A symmetric relative comparison isClose(x, y, reltol, abstol) always yields the same result as isClose(y, x).
    • An asymmetric relative comparison is one uses only one of the values (as with the Reference comparison above).
      For an asymmetric comparison , isClose(x, y, reltol, abstol) is not necessarily the same as isClose(y,x).
    The suitability of symmetric vs. asymmetric methods depends on the question is being asked.
    Symmetric tests are the most appropriate when,
    • the question is: Are the two numbers close to each other? or,
    • there is no obvious ordering between the two numbers.
    Asymmetric tests are the most appropriate when,
    • the question is: Is the computed value y within x percentage of this known value x? or,
    • there is a reference value against which another value must be compared.
    The symmetric approach provides an appealing consistency.
    It mirrors the symmetry of equality, and is less confusing.
    A symmetric test also eliminates the need for thinking about the order of the two input arguments (x, b).
    Conversely, when one needs to know that a value y is within a particular range of a known value x, then the following test can be implemented directly,
    if (abs(x - y) <= reltol * x) then
    ...
    end if
    Which symmetric comparison test is the most appropriate?
    There are three possible symmetric tests as mentioned above:
    • Weak Scaling : The maximum absolute value of the two.
      This approach yields the same result as the Strong scaling in most practical cases for small relative tolerance values.
    • Strong Scaling : The minimum absolute value of the two.
      This approach yields the same result as the Strong scaling in most practical cases for small relative tolerance values.
    • mean Scaling : The absolute value of the arithmetic mean of the two.
      This approach can potentially lead to the following runtime complications:
      • An arithmetic overflow can occur if the two values are large and are added together before dividing by two.
      • An arithmetic underflow can occur if the two values are tiny and are divided by two before being added together.
    Despite the similarities of the Weak and Strong Scaling methods, the Weak Scaling approach provides a more useful result for very large relative tolerances.
    This happens when one needs to test if two fairly disparate values are within a particular range of each other.
    For example: *Is x within 200% (reltol = 2.0) of y?
    The weak testing would use the larger (non-zero) value for the test, and thus return .true. if one value is zero.
    However, the Strong Scaling test would never indicate that two values are within that range of each other if one of them is zero.
    For better illustration consider the following question: Is 0 within 200% of 10?
    Note that 200% of 10 is 20. Therefore, the range within 200% of 10 is -10 to +30.
    Therefore, zero falls within the range and the Weak Scaling test will return .true..

The logic behind the specific choices of absolute and relative tolerances.

  • The relative tolerance required for two values to be considered close is entirely use-case dependent.
    Nevertheless, the relative tolerance needs to be greater than epsilon(x).
    This default tolerance is rather conservative knowing that the reltol = sqrt(epsilon(x)) is typically the largest relative tolerance for which the various possible testing methods above will yield the similar results.
    Also, good numerical algorithms are generally expected to not lose more than about half of available digits of accuracy due to errors.
  • The absolute tolerance value is primarily used for comparing to zero.
    The absolute tolerance required to determine if a value is close to zero is entirely use-case dependent.
    There is also essentially no bounds to the useful range. Thus a default of tiny(x) seems most appropriate.
    This way, if a comparison with zero is to be made, the test is guaranteed to fail the first time, prompting the user to select an appropriate value subsequently.
See also
math.isclose()
numpy.isclose()
Floating-point comparison algorithms
Python PEP 485
Test:
test_pm_mathCompare


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, Thursday 12:45 AM, August 20, 2021, Dallas, TX

Variable Documentation

◆ MEAN

type(mean_type), parameter pm_mathCompare::MEAN = mean_type()

This is a convenience constant object of type mean_type() that is exclusively provided to distinguish and facilitate the use of the procedures under the generic interface isClose().

Passing this constant object to the procedures of the generic interface isClose() is equivalent to requesting the mean Scaling method of testing the proximity of two numbers.

See the documentation of pm_mathCompare for extensive details of this and other possible testing modes.
See the documentation of isClose() for example usage.


Possible calling interfaces

use pm_mathCompare, only: MEAN
This module contains the procedures and interfaces for evaluating the relative or absolute proximity ...
type(mean_type), parameter MEAN
This is a convenience constant object of type mean_type() that is exclusively provided to distinguish...
See also
isClose()


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 447 of file pm_mathCompare.F90.

◆ MODULE_NAME

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

Definition at line 210 of file pm_mathCompare.F90.

◆ REFERENCE

type(reference_type), parameter pm_mathCompare::REFERENCE = reference_type()

This is a convenience constant object of type reference_type() that is exclusively provided to distinguish and facilitate the use of the procedures under the generic interface isClose().

Passing this constant object to the procedures of the generic interface isClose() is equivalent to requesting the Reference Scaling method of testing the proximity of two numbers.

See the documentation of pm_mathCompare for extensive details of this and other possible testing modes.
See the documentation of isClose() for example usage.


Possible calling interfaces

type(reference_type), parameter REFERENCE
This is a convenience constant object of type reference_type() that is exclusively provided to distin...
See also
isClose()


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 267 of file pm_mathCompare.F90.

◆ STRONG

type(strong_type), parameter pm_mathCompare::STRONG = strong_type()

This is a convenience constant object of type strong_type() that is exclusively provided to distinguish and facilitate the use of the procedures under the generic interface isClose().

Passing this constant object to the procedures of the generic interface isClose() is equivalent to requesting the Strong Scaling method of testing the proximity of two numbers.

See the documentation of pm_mathCompare for extensive details of this and other possible testing modes.
See the documentation of isClose() for example usage.


Possible calling interfaces

type(strong_type), parameter STRONG
This is a convenience constant object of type strong_type() that is exclusively provided to distingui...
See also
isClose()


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 327 of file pm_mathCompare.F90.

◆ WEAK

type(weak_type), parameter pm_mathCompare::WEAK = weak_type()

This is a convenience constant object of type weak_type() that is exclusively provided to distinguish and facilitate the use of the procedures under the generic interface isClose().

Passing this constant object to the procedures of the generic interface isClose() is equivalent to requesting the Weak Scaling method of testing the proximity of two numbers.

See the documentation of pm_mathCompare for extensive details of this and other possible testing modes.
See the documentation of isClose() for example usage.


Possible calling interfaces

use pm_mathCompare, only: WEAK
type(weak_type), parameter WEAK
This is a convenience constant object of type weak_type() that is exclusively provided to distinguish...
See also
isClose()


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 387 of file pm_mathCompare.F90.