code: def _brent_loop_body(state, params, constants):
      """Performs one iteration of the Brent root-finding algorithm.
  
      Args:
        state: A Python `_BrentSearchState` namedtuple.
        params: A Python `_BrentSearchParams` namedtuple.
        constants: A Python `_BrentSearchConstants` namedtuple.
  
      Returns:
        The `Tensor`s to use for the next iteration of the algorithm.
      """
  
      best_estimate = state.best_estimate
      last_estimate = state.last_estimate
      contrapoint = state.contrapoint
      value_at_best_estimate = state.value_at_best_estimate
      value_at_last_estimate = state.value_at_last_estimate
      value_at_contrapoint = state.value_at_contrapoint
      step_to_best_estimate = state.step_to_best_estimate
      step_to_last_estimate = state.step_to_last_estimate
      num_iterations = state.num_iterations
      finished = state.finished
  
      # If the root is between the last two estimates, use the worst of the two
      # as new contrapoint. Adjust step sizes accordingly.
      replace_contrapoint = ~finished & (
          value_at_last_estimate * value_at_best_estimate
          < constants.zero_value
      )
  
      contrapoint = tf.where(
          replace_contrapoint, last_estimate, contrapoint
      )
      value_at_contrapoint = tf.where(
          replace_contrapoint,
          value_at_last_estimate,
          value_at_contrapoint,
      )
  
      step_to_last_estimate = tf.where(
          replace_contrapoint,
          best_estimate - last_estimate,
          step_to_last_estimate,
      )
      step_to_best_estimate = tf.where(
          replace_contrapoint,
          step_to_last_estimate,
          step_to_best_estimate,
      )
  
      # If the contrapoint is a better guess than the current root estimate, swap
      # them. Also, replace the worst of the two with the current contrapoint.
      replace_best_estimate = tf.where(
          finished,
          constants.false,
          tf.math.abs(value_at_contrapoint)
          < tf.math.abs(value_at_best_estimate),
      )
  
      last_estimate = tf.where(
          replace_best_estimate, best_estimate, last_estimate
      )
      best_estimate = tf.where(
          replace_best_estimate, contrapoint, best_estimate
      )
      contrapoint = tf.where(
          replace_best_estimate, last_estimate, contrapoint
      )
  
      value_at_last_estimate = tf.where(
          replace_best_estimate,
          value_at_best_estimate,
          value_at_last_estimate,
      )
      value_at_best_estimate = tf.where(
          replace_best_estimate,
          value_at_contrapoint,
          value_at_best_estimate,
      )
      value_at_contrapoint = tf.where(
          replace_best_estimate,
          value_at_last_estimate,
          value_at_contrapoint,
      )
  
      # Compute the tolerance used to control root search at the current position
      # and the step size corresponding to the bisection method.
      root_tolerance = 0.5 * (
          params.absolute_root_tolerance
          + params.relative_root_tolerance * tf.math.abs(best_estimate)
      )
      bisection_step = 0.5 * (contrapoint - best_estimate)
  
      # Mark the search as finished if either:
      # 1. the maximum number of iterations has been reached;
      # 2. the desired tolerance has been reached (even if no root was found);
      # 3. the current root estimate is good enough.
      # Using zero as `function_tolerance` will check for exact roots and match
      # both Brent's original algorithm and the SciPy implementation.
      finished |= (
          (num_iterations >= params.max_iterations)
          | (tf.math.abs(bisection_step) < root_tolerance)
          | (~tf.math.is_finite(value_at_best_estimate))
          | (
              tf.math.abs(value_at_best_estimate)
              <= params.function_tolerance
          )
      )
  
      # Determine whether interpolation or extrapolation are worth performing at
      # the current position.
      compute_short_step = tf.where(
          finished,
          constants.false,
          (root_tolerance < tf.math.abs(step_to_last_estimate))
          & (
              tf.math.abs(value_at_best_estimate)
              < tf.math.abs(value_at_last_estimate)
          ),
      )
  
      short_step = tf.where(
          compute_short_step,
          tf.where(
              # The contrapoint cannot be equal to the current root estimate since
              # they have opposite signs. However, it may be equal to the previous
              # estimate.
              tf.equal(last_estimate, contrapoint),
              # If so, use the secant method to avoid a division by zero which
              # would occur if using extrapolation.
              _secant_step(
                  best_estimate,
                  last_estimate,
                  value_at_best_estimate,
                  value_at_last_estimate,
              ),
              # Pass values of the objective function as x values, and root
              # estimates as y values in order to perform *inverse* extrapolation.
              _quadratic_interpolation_step(
                  value_at_best_estimate,
                  value_at_last_estimate,
                  value_at_contrapoint,
                  best_estimate,
                  last_estimate,
                  contrapoint,
              ),
          ),
          # Default to zero if using bisection.
          constants.zero,
      )
  
      # Use the step calculated above if both:
      # 1. step size < |previous step size|
      # 2. step size < 3/4 * |contrapoint - current root estimate|
      # Ensure that `short_step` was calculated by guarding the calculation with
      # `compute_short_step`.
      use_short_step = tf.where(
          compute_short_step,
          2 * tf.math.abs(short_step)
          < tf.minimum(
              3 * tf.math.abs(bisection_step) - root_tolerance,
              tf.math.abs(step_to_last_estimate),
          ),
          constants.false,
      )
  
      # Revert to bisection when not using `short_step`.
      step_to_last_estimate = tf.where(
          use_short_step, step_to_best_estimate, bisection_step
      )
      step_to_best_estimate = tf.where(
          finished,
          constants.zero,
          tf.where(use_short_step, short_step, bisection_step),
      )
  
      # Update the previous and current root estimates.
      last_estimate = tf.where(finished, last_estimate, best_estimate)
      best_estimate += tf.where(
          finished,
          constants.zero,
          tf.where(
              root_tolerance < tf.math.abs(step_to_best_estimate),
              step_to_best_estimate,
              tf.where(
                  bisection_step > 0, root_tolerance, -root_tolerance
              ),
          ),
      )
  
      value_at_last_estimate = tf.where(
          finished, value_at_last_estimate, value_at_best_estimate
      )
      value_at_best_estimate = tf.where(
          finished,
          value_at_best_estimate,
          params.objective_fn(best_estimate),
      )
  
      num_iterations = tf.where(
          finished, num_iterations, num_iterations + 1
      )
  
      return [
          _BrentSearchState(
              best_estimate=best_estimate,
              last_estimate=last_estimate,
              contrapoint=contrapoint,
              value_at_best_estimate=value_at_best_estimate,
              value_at_last_estimate=value_at_last_estimate,
              value_at_contrapoint=value_at_contrapoint,
              step_to_best_estimate=step_to_best_estimate,
              step_to_last_estimate=step_to_last_estimate,
              num_iterations=num_iterations,
              finished=finished,
          )
      ]
  
file_path: tf-quant-finance/tf_quant_finance/math/root_search/brent.py
imports: from typing import Callable
  import tensorflow.compat.v2
  from tf_quant_finance import types
  from tf_quant_finance import utils
  from tf_quant_finance.math.root_search import utils
label_desc: Use image node_id 5 for calling a global function with example usage: _brent_loop_body(state, params, constants) and returns: list