(l-onnx-doc-Log1p)= # Log1p (l-onnx-op-log1p-29)= ## Log1p - 29 ### Version - **name**: [Log1p (GitHub)](https://github.com/onnx/onnx/blob/main/docs/Operators.md#Log1p) - **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 ```python 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 ```python 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 ```python 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 ```python 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 ```python 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") ```