code: def log_ndtr(x, series_order=3, name="log_ndtr"):
      """Log Normal distribution function.
  
      For details of the Normal distribution function see `ndtr`.
  
      This function calculates `(log o ndtr)(x)` by either calling `log(ndtr(x))` or
      using an asymptotic series. Specifically:
      - For `x > upper_segment`, use the approximation `-ndtr(-x)` based on
        `log(1-x) ~= -x, x << 1`.
      - For `lower_segment < x <= upper_segment`, use the existing `ndtr` technique
        and take a log.
      - For `x <= lower_segment`, we use the series approximation of erf to compute
        the log CDF directly.
  
      The `lower_segment` is set based on the precision of the input:
  
      ```
      lower_segment = { -20,  x.dtype=float64
                      { -10,  x.dtype=float32
      upper_segment = {   8,  x.dtype=float64
                      {   5,  x.dtype=float32
      ```
  
      When `x < lower_segment`, the `ndtr` asymptotic series approximation is:
  
      ```
         ndtr(x) = scale * (1 + sum) + R_N
         scale   = exp(-0.5 x**2) / (-x sqrt(2 pi))
         sum     = Sum{(-1)^n (2n-1)!! / (x**2)^n, n=1:N}
         R_N     = O(exp(-0.5 x**2) (2N+1)!! / |x|^{2N+3})
      ```
  
      where `(2n-1)!! = (2n-1) (2n-3) (2n-5) ...  (3) (1)` is a
      [double-factorial](https://en.wikipedia.org/wiki/Double_factorial).
  
  
      Args:
        x: `Tensor` of type `float32`, `float64`.
        series_order: Positive Python `integer`. Maximum depth to
          evaluate the asymptotic expansion. This is the `N` above.
        name: Python string. A name for the operation (default="log_ndtr").
  
      Returns:
        log_ndtr: `Tensor` with `dtype=x.dtype`.
  
      Raises:
        TypeError: if `x.dtype` is not handled.
        TypeError: if `series_order` is a not Python `integer.`
        ValueError:  if `series_order` is not in `[0, 30]`.
      """
      if not isinstance(series_order, int):
          raise TypeError("series_order must be a Python integer.")
      if series_order < 0:
          raise ValueError("series_order must be non-negative.")
      if series_order > 30:
          raise ValueError("series_order must be <= 30.")
  
      with tf.name_scope(name):
          x = tf.convert_to_tensor(x, name="x")
  
          if dtype_util.base_equal(x.dtype, tf.float64):
              lower_segment = np.array(
                  LOGNDTR_FLOAT64_LOWER, dtype=np.float64
              )
              upper_segment = np.array(
                  LOGNDTR_FLOAT64_UPPER, dtype=np.float64
              )
          elif dtype_util.base_equal(x.dtype, tf.float32):
              lower_segment = np.array(
                  LOGNDTR_FLOAT32_LOWER, dtype=np.float32
              )
              upper_segment = np.array(
                  LOGNDTR_FLOAT32_UPPER, dtype=np.float32
              )
          else:
              raise TypeError("x.dtype=%s is not supported." % x.dtype)
  
          # The basic idea here was ported from:
          #   https://root.cern.ch/doc/v608/SpecFuncCephesInv_8cxx_source.html
          # We copy the main idea, with a few changes
          # * For x >> 1, and X ~ Normal(0, 1),
          #     Log[P[X < x]] = Log[1 - P[X < -x]] approx -P[X < -x],
          #     which extends the range of validity of this function.
          # * We use one fixed series_order for all of 'x', rather than adaptive.
          # * Our docstring properly reflects that this is an asymptotic series, not a
          #   Taylor series. We also provided a correct bound on the remainder.
          # * We need to use the max/min in the _log_ndtr_lower arg to avoid nan when
          #   x=0. This happens even though the branch is unchosen because when x=0
          #   the gradient of a select involves the calculation 1*dy+0*(-inf)=nan
          #   regardless of whether dy is finite. Note that the minimum is a NOP if
          #   the branch is chosen.
          return tf.where(
              x > upper_segment,
              -_ndtr(-x),  # log(1-x) ~= -x, x << 1
              tf.where(
                  x > lower_segment,
                  tf.math.log(_ndtr(tf.maximum(x, lower_segment))),
                  _log_ndtr_lower(
                      tf.minimum(x, lower_segment), series_order
                  ),
              ),
          )
  
file_path: probability/tensorflow_probability/python/internal/special_math.py
imports: import numpy
  import tensorflow.compat.v2
  from tensorflow_probability.python.internal import dtype_util
label_desc: Use image node_id 3 for calling a global function with example usage: log_ndtr(x, series_order, name) and returns: tf