smoothutils

This Cython module implements the performance-critical functions of the Python module smoothtools.

MAX_LOG_FLOAT = 700

The natural logarithm of the highest possible float value.

The exact value depends on the actual system. So an automated estimation of this value would be advisable.

smooth_logistic1

Smoothing kernel based on the logistic function.

\(f_{log1}(x, c) = 1-\frac{1}{1+exp(x/c)}\)

The following example shows the typical shape of the logistic function for four different smoothing parameters:

>>> from hydpy.cythons import smoothutils
>>> from hydpy import round_
>>> for value in range(-5, 6):
...     if value == -5:
...         round_("value, par=10.0,  par=1.0,  par=0.1,  par=0.0")
...     round_(value, width=5, lfill=" ", end=", ")
...     for idx, parameter in enumerate([10.0, 1.0, 0.1, 0.0]):
...         round_(smoothutils.smooth_logistic1(value, parameter),
...                width=8, rfill="0", end="")
...         if idx < 3:
...             round_("", end=", ")
...         else:
...             round_("")
value, par=10.0,  par=1.0,  par=0.1,  par=0.0
   -5, 0.377541, 0.006693, 0.000000, 0.000000
   -4, 0.401312, 0.017986, 0.000000, 0.000000
   -3, 0.425557, 0.047426, 0.000000, 0.000000
   -2, 0.450166, 0.119203, 0.000000, 0.000000
   -1, 0.475021, 0.268941, 0.000045, 0.000000
    0, 0.500000, 0.500000, 0.500000, 0.500000
    1, 0.524979, 0.731059, 0.999955, 1.000000
    2, 0.549834, 0.880797, 1.000000, 1.000000
    3, 0.574443, 0.952574, 1.000000, 1.000000
    4, 0.598688, 0.982014, 1.000000, 1.000000
    5, 0.622459, 0.993307, 1.000000, 1.000000

With the highest value of the smoothing parameter (10.0), the result approximates a straight line. With the lowest smoothing parameter (0.0), the result is identical with a discontinuous first-order step function.

Function smooth_logistic1 protects itself against numerical overflow. Hence even extremely high value/parameter ratios are allowed:

>>> round_(smoothutils.smooth_logistic1(1000., .1))
1.0

smooth_logistic1_derivative2

Calculate the derivative of the function smooth_logistic1 with respect to the given value.

\(\frac{d}{dx}f_{log1}(x, c) = \frac{exp(x/c)}{(exp(x/c)+1)^2}\)

The following example shows the derivates for four different smoothing parameters:

>>> import numpy
>>> for value in range(-5, 6):
...     if value == -5:
...         round_("value,  par=3.0,  par=1.0,  par=0.1,  par=0.0")
...     round_(value, width=5, lfill=" ", end=", ")
...     for idx, parameter in enumerate([3.0, 1.0, 0.1, 0.0]):
...         result = smoothutils.smooth_logistic1_derivative2(value, parameter)
...         if numpy.isinf(result):
...             print(result, end="")
...         else:
...             round_(result, width=8, rfill="0", end="")
...         if idx < 3:
...             round_("", end=", ")
...         else:
...             round_("")
value,  par=3.0,  par=1.0,  par=0.1,  par=0.0
   -5, 0.044543, 0.006648, 0.000000, 0.000000
   -4, 0.055030, 0.017663, 0.000000, 0.000000
   -3, 0.065537, 0.045177, 0.000000, 0.000000
   -2, 0.074719, 0.104994, 0.000000, 0.000000
   -1, 0.081061, 0.196612, 0.000454, 0.000000
    0, 0.083333, 0.250000, 2.500000, inf
    1, 0.081061, 0.196612, 0.000454, 0.000000
    2, 0.074719, 0.104994, 0.000000, 0.000000
    3, 0.065537, 0.045177, 0.000000, 0.000000
    4, 0.055030, 0.017663, 0.000000, 0.000000
    5, 0.044543, 0.006648, 0.000000, 0.000000

We validate the calculated derivatives by comparing them with sufficiently accurate numerical approximations:

>>> dx = 1e-7
>>> for value in range(-5, 6):
...     if value == -5:
...         round_("value,  par=3.0,  par=1.0,  par=0.1,  par=0.0")
...     round_(value, width=5, lfill=" ", end=", ")
...     for idx, parameter in enumerate([3.0, 1.0, 0.1, 0.0]):
...         est = (smoothutils.smooth_logistic1(value+dx, parameter) -
...                smoothutils.smooth_logistic1(value, parameter))/dx
...         round_(est, width=8, rfill="0", end="")
...         if idx < 3:
...             round_("", end=", ")
...         else:
...             round_("")
value,  par=3.0,  par=1.0,  par=0.1,  par=0.0
   -5, 0.044543, 0.006648, 0.000000, 0.000000
   -4, 0.055030, 0.017663, 0.000000, 0.000000
   -3, 0.065537, 0.045177, 0.000000, 0.000000
   -2, 0.074719, 0.104994, 0.000000, 0.000000
   -1, 0.081061, 0.196612, 0.000454, 0.000000
    0, 0.083333, 0.250000, 2.500000, 5000000.0
    1, 0.081061, 0.196612, 0.000454, 0.000000
    2, 0.074719, 0.104994, 0.000000, 0.000000
    3, 0.065537, 0.045177, 0.000000, 0.000000
    4, 0.055030, 0.017663, 0.000000, 0.000000
    5, 0.044543, 0.006648, 0.000000, 0.000000

Function smooth_logistic2_derivative1 protects itself against numerical overflow. Hence even extremely high value/parameter ratios are allowed:

>>> round_(smoothutils.smooth_logistic1_derivative2(1000.0, 0.1))
0.0

smooth_logistic2

Smoothing kernel based on the integral of the logistic function.

\(f_{log2}(x, c) = c \cdot ln(1+exp(x/c))\)

The following example shows the shape of the integral of the logistic function for four different smoothing parameters:

>>> for value in range(-5, 6):
...     if value == -5:
...         round_("value, par=3.0,  par=1.0,  par=0.1,  par=0.0")
...     round_(value, width=5, lfill=" ", end=", ")
...     for idx, parameter in enumerate([3.0, 1.0, 0.1, 0.0]):
...         round_(smoothutils.smooth_logistic2(value, parameter),
...                width=8, rfill="0", end="")
...         if idx < 3:
...             round_("", end=", ")
...         else:
...             round_("")
value, par=3.0,  par=1.0,  par=0.1,  par=0.0
   -5, 0.519024, 0.006715, 0.000000, 0.000000
   -4, 0.701888, 0.018150, 0.000000, 0.000000
   -3, 0.939785, 0.048587, 0.000000, 0.000000
   -2, 1.243110, 0.126928, 0.000000, 0.000000
   -1, 1.620917, 0.313262, 0.000005, 0.000000
    0, 2.079442, 0.693147, 0.069315, 0.000000
    1, 2.620917, 1.313262, 1.000005, 1.000000
    2, 3.243110, 2.126928, 2.000000, 2.000000
    3, 3.939785, 3.048587, 3.000000, 3.000000
    4, 4.701888, 4.018150, 4.000000, 4.000000
    5, 5.519024, 5.006715, 5.000000, 5.000000

With the highest value of the smoothing parameter (3.0), the resulting line is relatively straight. With the lowest smoothing parameter (0.0), the result is identical with a second-order discontinuous step function.

Function smooth_logistic2 protects itself against numerical overflow. Hence even extremely high value/parameter ratios are allowed:

>>> round_(smoothutils.smooth_logistic2(1000.0, 0.1))
1000.0

smooth_logistic2_derivative1

Calculate the derivative of the function smooth_logistic2 with respect to its smoothing parameter.

\(\frac{d}{dc}f_{log2}(x, c) = \frac{x}{c \cdot exp(x/c)+c} \cdot ln(exp(-x/c)+1)\)

The following example shows the derivates for four different smoothing parameters:

>>> for value in range(-5, 6):
...     if value == -5:
...         round_("value,  par=3.0,  par=1.0,  par=0.1,  par=0.0")
...     round_(value, width=5, lfill=" ", end=", ")
...     for idx, parameter in enumerate([3.0, 1.0, 0.1, 0.0]):
...         round_(smoothutils.smooth_logistic2_derivative1(value, parameter),
...                width=8, rfill="0", end="")
...         if idx < 3:
...             round_("", end=", ")
...         else:
...             round_("")
value,  par=3.0,  par=1.0,  par=0.1,  par=0.0
   -5, 0.437790, 0.040180, 0.000000, 0.000000
   -4, 0.512107, 0.090095, 0.000000, 0.000000
   -3, 0.582203, 0.190865, 0.000000, 0.000000
   -2, 0.640533, 0.365334, 0.000000, 0.000000
   -1, 0.679449, 0.582203, 0.000499, 0.000000
    0, 0.693147, 0.693147, 0.693147, 0.693147
    1, 0.679449, 0.582203, 0.000499, 0.000000
    2, 0.640533, 0.365334, 0.000000, 0.000000
    3, 0.582203, 0.190865, 0.000000, 0.000000
    4, 0.512107, 0.090095, 0.000000, 0.000000
    5, 0.437790, 0.040180, 0.000000, 0.000000

We validate the calculated derivatives by comparing them with sufficiently accurate numerical approximations.

>>> dc = 1e-6
>>> for value in range(-5, 6):
...     if value == -5:
...         round_("value,  par=3.0,  par=1.0,  par=0.1,  par=0.0")
...     round_(value, width=5, lfill=" ", end=", ")
...     for idx, parameter in enumerate([3.0, 1.0, 0.1, 0.0]):
...         est = (smoothutils.smooth_logistic2(value, parameter+dc) -
...                smoothutils.smooth_logistic2(value, parameter))/dc
...         round_(est, width=8, rfill="0", end="")
...         if idx < 3:
...             round_("", end=", ")
...         else:
...             round_("")
value,  par=3.0,  par=1.0,  par=0.1,  par=0.0
   -5, 0.437790, 0.040180, 0.000000, 0.000000
   -4, 0.512107, 0.090095, 0.000000, 0.000000
   -3, 0.582203, 0.190865, 0.000000, 0.000000
   -2, 0.640533, 0.365334, 0.000000, 0.000000
   -1, 0.679449, 0.582203, 0.000499, 0.000000
    0, 0.693147, 0.693147, 0.693147, 0.693147
    1, 0.679449, 0.582203, 0.000499, 0.000000
    2, 0.640533, 0.365334, 0.000000, 0.000000
    3, 0.582203, 0.190865, 0.000000, 0.000000
    4, 0.512107, 0.090095, 0.000000, 0.000000
    5, 0.437790, 0.040180, 0.000000, 0.000000

Function smooth_logistic2_derivative1 protects itself against numerical overflow. Hence even extremely high negative value/parameter ratios are allowed:

>>> round_(smoothutils.smooth_logistic2_derivative1(-1000.0, 0.1))
0.0

smooth_logistic2_derivative2

Calculate the derivative of the function smooth_logistic2 with respect to the given value.

\(\frac{d}{dx}f_{log2}(x, c) = \frac{exp(x/c)}{exp(x/c)+1}\)

The following example shows the derivates for four different smoothing parameters:

>>> for value in range(-5, 6):
...     if value == -5:
...         round_("value,  par=3.0,  par=1.0,  par=0.1,  par=0.0")
...     round_(value, width=5, lfill=" ", end=", ")
...     for idx, parameter in enumerate([3.0, 1.0, 0.1, 0.0]):
...         round_(smoothutils.smooth_logistic2_derivative2(value, parameter),
...                width=8, rfill="0", end="")
...         if idx < 3:
...             round_("", end=", ")
...         else:
...             round_("")
value,  par=3.0,  par=1.0,  par=0.1,  par=0.0
   -5, 0.158869, 0.006693, 0.000000, 0.000000
   -4, 0.208609, 0.017986, 0.000000, 0.000000
   -3, 0.268941, 0.047426, 0.000000, 0.000000
   -2, 0.339244, 0.119203, 0.000000, 0.000000
   -1, 0.417430, 0.268941, 0.000045, 0.000000
    0, 0.500000, 0.500000, 0.500000, 1.000000
    1, 0.582570, 0.731059, 0.999955, 1.000000
    2, 0.660756, 0.880797, 1.000000, 1.000000
    3, 0.731059, 0.952574, 1.000000, 1.000000
    4, 0.791391, 0.982014, 1.000000, 1.000000
    5, 0.841131, 0.993307, 1.000000, 1.000000

We validate the calculated derivatives by comparing them with sufficiently accurate numerical approximations.

>>> dx = 1e-7
>>> for value in range(-5, 6):
...     if value == -5:
...         round_("value,  par=3.0,  par=1.0,  par=0.1,  par=0.0")
...     round_(value, width=5, lfill=" ", end=", ")
...     for idx, parameter in enumerate([3.0, 1.0, 0.1, 0.0]):
...         est = (smoothutils.smooth_logistic2(value+dx, parameter) -
...                smoothutils.smooth_logistic2(value, parameter))/dx
...         round_(est, width=8, rfill="0", end="")
...         if idx < 3:
...             round_("", end=", ")
...         else:
...             round_("")
value,  par=3.0,  par=1.0,  par=0.1,  par=0.0
   -5, 0.158869, 0.006693, 0.000000, 0.000000
   -4, 0.208609, 0.017986, 0.000000, 0.000000
   -3, 0.268941, 0.047426, 0.000000, 0.000000
   -2, 0.339244, 0.119203, 0.000000, 0.000000
   -1, 0.417430, 0.268941, 0.000045, 0.000000
    0, 0.500000, 0.500000, 0.500000, 1.000000
    1, 0.582570, 0.731059, 0.999955, 1.000000
    2, 0.660756, 0.880797, 1.000000, 1.000000
    3, 0.731059, 0.952574, 1.000000, 1.000000
    4, 0.791391, 0.982014, 1.000000, 1.000000
    5, 0.841131, 0.993307, 1.000000, 1.000000

Function smooth_logistic2_derivative2 protects itself against numerical overflow. Hence even extremely high value/parameter ratios are allowed:

>>> round_(smoothutils.smooth_logistic2_derivative2(1000.0, 0.1))
1.0

smooth_logistic3

Smoothing kernel which combines smooth_logistic1 and smooth_logistic2 for the regularization of functions containing two second-order discontinuities.

\(f_{log3}(x, c) = (1-w) \cdot f_{log2}(x, c) + w \cdot (1-f_{log2}(x, c))\)

\(w = f_{log2}(x-1/2, d)\)

\(d = max(0.54 \cdot c^{1.17}, 0.025)\)

The following example shows the shape of this combined smoothing function for four different smoothing parameters:

>>> from numpy import arange
>>> for value in arange(-5.5, 6):
...     if value == -5.5:
...         round_("value,  par=3.0,  par=1.0,  par=0.1,  par=0.0")
...     round_(value, width=5, lfill=" ", end=", ")
...     for idx, parameter in enumerate([3.0, 1.0, 0.1, 0.0]):
...         round_(smoothutils.smooth_logistic3(value, parameter),
...                width=8, rfill="0", end="")
...         if idx < 3:
...             round_("", end=", ")
...         else:
...             round_("")
value,  par=3.0,  par=1.0,  par=0.1,  par=0.0
 -5.5, 0.167513, 0.003996, 0.000000, 0.000000
 -4.5, 0.206271, 0.010618, 0.000000, 0.000000
 -3.5, 0.251687, 0.027603, 0.000000, 0.000000
 -2.5, 0.304292, 0.068844, 0.000000, 0.000000
 -1.5, 0.364136, 0.158615, 0.000000, 0.000000
 -0.5, 0.430211, 0.314615, 0.000672, 0.000000
  0.5, 0.500000, 0.500000, 0.500000, 0.500000
  1.5, 0.569789, 0.685385, 0.999328, 1.000000
  2.5, 0.635864, 0.841385, 1.000000, 1.000000
  3.5, 0.695708, 0.931156, 1.000000, 1.000000
  4.5, 0.748313, 0.972397, 1.000000, 1.000000
  5.5, 0.793729, 0.989382, 1.000000, 1.000000

With the highest value of the smoothing parameter (3.0), the resulting line is relatively straight. With the lowest smoothing parameter (0.0), the result is identical with a function with two second-order discontinuities.

smooth_max1

Smoothing kernel for approximating the maximum function for two values based on the “LogSumExp” function.

\(f_{max}(x, y, c) = c \cdot ln(exp(x/c)+exp(y/c))\)

The following example shows the different degree of approximation of the maximum function for four different smoothing parameters:

>>> for value in range(11):
...     if value == 0:
...         round_("y_value, par=3.0,  par=1.0,  par=0.3,  par=0.0")
...     round_(value, width=7, lfill=" ", end=", ")
...     for idx, parameter in enumerate([3.0, 1.0, 0.3, 0.0]):
...         round_(smoothutils.smooth_max1(5.0, value, parameter),
...                width=8, rfill="0", end="")
...         if idx < 3:
...             round_("", end=", ")
...         else:
...             round_("")
y_value, par=3.0,  par=1.0,  par=0.3,  par=0.0
      0, 5.519024, 5.006715, 5.000000, 5.000000
      1, 5.701888, 5.018150, 5.000000, 5.000000
      2, 5.939785, 5.048587, 5.000014, 5.000000
      3, 6.243110, 5.126928, 5.000382, 5.000000
      4, 6.620917, 5.313262, 5.010516, 5.000000
      5, 7.079442, 5.693147, 5.207944, 5.000000
      6, 7.620917, 6.313262, 6.010516, 6.000000
      7, 8.243110, 7.126928, 7.000382, 7.000000
      8, 8.939785, 8.048587, 8.000014, 8.000000
      9, 9.701888, 9.018150, 9.000000, 9.000000
     10, 10.519024, 10.006715, 10.00000, 10.00000

With the highest value of the smoothing parameter (3.0), the resulting line is relatively straight. With the lowest smoothing parameter (0.0), the result is identical with the usual (discontinuous) maximum function.

Function smooth_max1 protects itself against numerical underflow and overflow. In the following example, extreme values are added to both the x and the y value of 5 and 6, respectively. The degree of smoothing is always identical:

>>> for test in ["-1e8", "0.0", "1e8"]:
...     round_(test, end=", ")
...     test = float(test)
...     round_(smoothutils.smooth_max1(test+5.0, test+6.0, 1.0)-test)
-1e8, 6.313262
0.0, 6.313262
1e8, 6.313262

smooth_min1

Smoothing kernel for approximating the minimum function for two values based on the LogSumExp function.

\(f_{max}(x, y, c) = -c \cdot ln(exp(x/-c)+exp(y/-c))\)

The following example shows the different degree of approximation of the minimum function for four different smoothing parameters:

>>> for value in range(11):
...     if value == 0:
...         round_("y_value, par=3.0,  par=1.0,  par=0.3,  par=0.0")
...     round_(value, width=7, lfill=" ", end=", ")
...     for idx, parameter in enumerate([3.0, 1.0, 0.3, 0.0]):
...         round_(smoothutils.smooth_min1(5.0, value, parameter),
...                width=8, rfill="0", end="")
...         if idx < 3:
...             round_("", end=", ")
...         else:
...             round_("")
y_value, par=3.0,  par=1.0,  par=0.3,  par=0.0
      0, -0.519024, -0.006715, 0.000000, 0.000000
      1, 0.298112, 0.981850, 1.000000, 1.000000
      2, 1.060215, 1.951413, 1.999986, 2.000000
      3, 1.756890, 2.873072, 2.999618, 3.000000
      4, 2.379083, 3.686738, 3.989484, 4.000000
      5, 2.920558, 4.306853, 4.792056, 5.000000
      6, 3.379083, 4.686738, 4.989484, 5.000000
      7, 3.756890, 4.873072, 4.999618, 5.000000
      8, 4.060215, 4.951413, 4.999986, 5.000000
      9, 4.298112, 4.981850, 5.000000, 5.000000
     10, 4.480976, 4.993285, 5.000000, 5.000000

With the highest value of the smoothing parameter (3.0), the resulting line is relatively straight. With the lowest smoothing parameter (0.0), the result is identical with the usual (discontinuous) minimum function.

Function smooth_min1 protects itself against numerical underflow and overflow. In the following example, extreme values are added to both the x and the y value of 5 and 6, respectively. The degree of smoothing is always identical:

>>> for test in ["-1e8", " 0.0", " 1e8"]:
...     round_(test, end=", ")
...     test = float(test)
...     round_(smoothutils.smooth_min1(test+5.0, test+6.0, 1.0)-test)
-1e8, 4.686738
 0.0, 4.686738
 1e8, 4.686738

smooth_max2

Smoothing kernel for approximating the maximum function for three values based on the “LogSumExp” function.

\(f_{max}(x, y, z, c) = c \cdot ln(exp(x/c)+exp(y/c)+exp(z/c))\)

The following example shows the different degree of approximation of the maximum function for four different smoothing parameters:

>>> for value in range(11):
...     if value == 0:
...         round_("z_value, par=3.0,  par=1.0,  par=0.3,  par=0.0")
...     round_(value, width=7, lfill=" ", end=", ")
...     for idx, parameter in enumerate([3.0, 1.0, 0.3, 0.0]):
...         round_(smoothutils.smooth_max2(-50.0, 5.0, value, parameter),
...                width=8, rfill="0", end="")
...         if idx < 3:
...             round_("", end=", ")
...         else:
...             round_("")
z_value, par=3.0,  par=1.0,  par=0.3,  par=0.0
      0, 5.519024, 5.006715, 5.000000, 5.000000
      1, 5.701888, 5.018150, 5.000000, 5.000000
      2, 5.939785, 5.048587, 5.000014, 5.000000
      3, 6.243110, 5.126928, 5.000382, 5.000000
      4, 6.620917, 5.313262, 5.010516, 5.000000
      5, 7.079442, 5.693147, 5.207944, 5.000000
      6, 7.620917, 6.313262, 6.010516, 6.000000
      7, 8.243110, 7.126928, 7.000382, 7.000000
      8, 8.939785, 8.048587, 8.000014, 8.000000
      9, 9.701888, 9.018150, 9.000000, 9.000000
     10, 10.519024, 10.006715, 10.00000, 10.00000

With the highest value of the smoothing parameter (3.0), the resulting line is relatively straight. With the lowest smoothing parameter (0.0), the result is identical with the usual (discontinuous) maximum function.

Function smooth_max2 protects itself against numerical underflow and overflow. In the following example, extreme values are added to the x, the y, and the z value of 5, 6, and 7, respectively. The degree of smoothing is always identical:

>>> for test in ["-1e8", "0.0", "1e8"]:
...     round_(test, end=", ")
...     test = float(test)
...     round_(smoothutils.smooth_max2(test+5.0, test+6.0, test+7.0, 1.0)-test)
-1e8, 7.407606
0.0, 7.407606
1e8, 7.407606

smooth_min2

Smoothing kernel for approximating the minimum function for two values based on the LogSumExp function.

\(f_{max}(x, y, z, c) = -c \cdot ln(exp(x/-c)+exp(y/-c)+exp(z/-c))\)

The following example shows the different degree of approximation of the minimum function for four different smoothing parameters:

>>> for value in range(11):
...     if value == 0:
...         round_("z_value, par=3.0,  par=1.0,  par=0.3,  par=0.0")
...     round_(value, width=7, lfill=" ", end=", ")
...     for idx, parameter in enumerate([3.0, 1.0, 0.3, 0.0]):
...         round_(smoothutils.smooth_min2(60.0, 5.0, value, parameter),
...                width=8, rfill="0", end="")
...         if idx < 3:
...             round_("", end=", ")
...         else:
...             round_("")
z_value, par=3.0,  par=1.0,  par=0.3,  par=0.0
      0, -0.519024, -0.006715, 0.000000, 0.000000
      1, 0.298112, 0.981850, 1.000000, 1.000000
      2, 1.060215, 1.951413, 1.999986, 2.000000
      3, 1.756890, 2.873072, 2.999618, 3.000000
      4, 2.379083, 3.686738, 3.989484, 4.000000
      5, 2.920558, 4.306853, 4.792056, 5.000000
      6, 3.379083, 4.686738, 4.989484, 5.000000
      7, 3.756890, 4.873072, 4.999618, 5.000000
      8, 4.060215, 4.951413, 4.999986, 5.000000
      9, 4.298112, 4.981850, 5.000000, 5.000000
     10, 4.480976, 4.993285, 5.000000, 5.000000

With the highest value of the smoothing parameter (3.0), the resulting line is relatively straight. With the lowest smoothing parameter (0.0), the result is identical with the usual (discontinuous) minimum function.

Function smooth_min2 protects itself against numerical underflow and overflow. In the following example, extreme values are added to the x, the y, and the z value of 5, 6, and 7, respectively. The degree of smoothing is always identical:

>>> for test in ["-1e8", " 0.0", " 1e8"]:
...     round_(test, end=", ")
...     test = float(test)
...     round_(smoothutils.smooth_min2(test+5.0, test+6.0, test+7.0, 1.0)-test)
-1e8, 4.592394
 0.0, 4.592394
 1e8, 4.592394