Log1p

Log1p - 29

Version

  • name: Log1p (GitHub)

  • domain: main

  • since_version: 29

  • function: False

  • support_level: SupportType.COMMON

  • shape inference: True

This version of the operator has been available since version 29.

Summary

Computes y = ln(1 + x) element-wise, where ln is the natural logarithm. The output has the same shape and element type as the input, including for scalar and empty inputs.

For finite x > -1, the result is a floating-point approximation of ln(1 + x), with 1 + x evaluated as an exact real sum. Small nonzero results representable in the output type must be preserved even when floating-point addition would round 1 + x to 1. For example, a FLOAT input of 1e-8 produces approximately 1e-8.

Special values are defined element-wise as follows:

Input x

Output y

Positive zero

Positive zero

Negative zero

Negative zero

-1

Negative infinity

Finite x < -1

NaN

Negative infinity

NaN

Positive infinity

Positive infinity

NaN

NaN

The sign and payload of an output NaN are unspecified.

Inputs

  • input (heterogeneous) - T:

    Input tensor

Outputs

  • output (heterogeneous) - T:

    The natural logarithm of one plus the input, computed element-wise

Type Constraints

  • T in ( tensor(bfloat16), tensor(double), tensor(float), tensor(float16) ):

    Constrain input and output types to float tensors.

Examples

_log1p

import numpy as np
import onnx

node = onnx.helper.make_node("Log1p", ["x"], ["y"])
for dtype in [np.float16, ml_dtypes.bfloat16, np.float32, np.float64]:
    x = np.array(
        [[-0.75, -0.5, 0.0, 0.25], [0.5, 1.0, 3.0, 1000.0]], dtype=dtype
    )
    y = np.array([math.log1p(float(v)) for v in x.flat], dtype=dtype).reshape(
        x.shape
    )
    expect(
        node,
        inputs=[x],
        outputs=[y],
        name=f"test_log1p_{np.dtype(dtype).name}",
        rtol=1e-2 if dtype == ml_dtypes.bfloat16 else 1e-3,
    )

_log1p_small_inputs

import numpy as np
import onnx

node = onnx.helper.make_node("Log1p", ["x"], ["y"])
for dtype, tiny in [
    (np.float16, 2.0**-14),
    (ml_dtypes.bfloat16, 2.0**-10),
    (np.float32, 1e-8),
    (np.float64, 1e-20),
]:
    x = np.array([-tiny, -0.0, 0.0, tiny], dtype=dtype)
    y = np.array([math.log1p(float(v)) for v in x], dtype=dtype)
    # Relative error allows backend variation, while atol=0 catches
    # an incorrect zero caused by rounding the intermediate 1 + x.
    expect(
        node,
        inputs=[x],
        outputs=[y],
        name=f"test_log1p_small_inputs_{np.dtype(dtype).name}",
        rtol=1e-2 if dtype == ml_dtypes.bfloat16 else 1e-3,
        atol=0,
    )

_log1p_near_negative_one

import numpy as np
import onnx

node = onnx.helper.make_node("Log1p", ["x"], ["y"])
for dtype in [np.float16, ml_dtypes.bfloat16, np.float32, np.float64]:
    x = np.nextafter(
        np.array([-1.0], dtype=dtype), np.array([0.0], dtype=dtype)
    )
    y = np.array([math.log1p(float(x[0]))], dtype=dtype)
    expect(
        node,
        inputs=[x],
        outputs=[y],
        name=f"test_log1p_near_negative_one_{np.dtype(dtype).name}",
        rtol=1e-2 if dtype == ml_dtypes.bfloat16 else 1e-3,
    )

_log1p_special_values

import numpy as np
import onnx

node = onnx.helper.make_node("Log1p", ["x"], ["y"])
for dtype in [np.float16, ml_dtypes.bfloat16, np.float32, np.float64]:
    x = np.array([-0.0, 0.0, -1.0, -2.0, -np.inf, np.inf, np.nan], dtype=dtype)
    y = np.array(
        [-0.0, 0.0, -np.inf, np.nan, np.nan, np.inf, np.nan], dtype=dtype
    )
    expect(
        node,
        inputs=[x],
        outputs=[y],
        name=f"test_log1p_special_values_{np.dtype(dtype).name}",
    )

_log1p_scalar_and_empty

import numpy as np
import onnx

node = onnx.helper.make_node("Log1p", ["x"], ["y"])
expect(
    node,
    inputs=[np.array(1.0, dtype=np.float32)],
    outputs=[np.array(math.log(2), dtype=np.float32)],
    name="test_log1p_scalar",
)
x = np.empty((0, 3), dtype=np.float32)
expect(node, inputs=[x], outputs=[x], name="test_log1p_empty")