code: def secant2(
      value_and_gradients_function,
      val_0,
      search_interval,
      f_lim,
      sufficient_decrease_param=0.1,
      curvature_param=0.9,
      name=None,
  ):
      """Performs the secant square procedure of Hager Zhang.
  
      Given an interval that brackets a root, this procedure performs an update of
      both end points using two intermediate points generated using the secant
      interpolation. For details see the steps S1-S4 in [Hager and Zhang (2006)][2].
  
      The interval [a, b] must satisfy the opposite slope conditions described in
      the documentation for `update`.
  
      Args:
        value_and_gradients_function: A Python callable that accepts a real scalar
          tensor and returns an object that can be converted to a namedtuple.
          The namedtuple should have fields 'f' and 'df' that correspond to scalar
          tensors of real dtype containing the value of the function and its
          derivative at that point. The other namedtuple fields, if present,
          should be tensors or sequences (possibly nested) of tensors.
          In usual optimization application, this function would be generated by
          projecting the multivariate objective function along some specific
          direction. The direction is determined by some other procedure but should
          be a descent direction (i.e. the derivative of the projected univariate
          function must be negative at 0.).
          Alternatively, the function may represent the batching of `n` such line
          functions (e.g. projecting a single multivariate objective function along
          `n` distinct directions at once) accepting n points as input, i.e. a
          tensor of shape [n], and the fields 'f' and 'df' in the returned
          namedtuple should each be a tensor of shape [n], with the corresponding
          function values and derivatives at the input points.
        val_0: A namedtuple, as returned by value_and_gradients_function evaluated
          at `0.`.
        search_interval: A namedtuple describing the current search interval,
          must include the fields:
          - converged: Boolean `Tensor` of shape [n], indicating batch members
              where search has already converged. Interval for these batch members
              won't be modified.
          - failed: Boolean `Tensor` of shape [n], indicating batch members
              where search has already failed. Interval for these batch members
              wont be modified.
          - iterations: int32 `Tensor` of shape [n]. Number of line search
              iterations so far.
          - func_evals: Scalar int32 `Tensor`. Number of function evaluations
              so far.
          - left: A namedtuple, as returned by value_and_gradients_function,
              of the left end point of the current search interval.
          - right: A namedtuple, as returned by value_and_gradients_function,
              of the right end point of the current search interval.
        f_lim: Scalar `Tensor` of real dtype. The function value threshold for
          the approximate Wolfe conditions to be checked.
        sufficient_decrease_param: Positive scalar `Tensor` of real dtype.
          Bounded above by the curvature param. Corresponds to 'delta' in the
          terminology of [Hager and Zhang (2006)][2].
        curvature_param: Positive scalar `Tensor` of real dtype. Bounded above
          by `1.`. Corresponds to 'sigma' in the terminology of
          [Hager and Zhang (2006)][2].
        name: (Optional) Python str. The name prefixed to the ops created by this
          function. If not supplied, the default name 'secant2' is used.
  
      Returns:
        A namedtuple containing the following fields.
          active: A boolean `Tensor` of shape [n]. Used internally by the procedure
            to indicate batch members on which there is work left to do.
          converged: A boolean `Tensor` of shape [n]. Indicates whether a point
            satisfying the Wolfe conditions has been found. If this is True, the
            interval will be degenerate (i.e. `left` and `right` below will be
            identical).
          failed: A boolean `Tensor` of shape [n]. Indicates if invalid function or
            gradient values were encountered (i.e. infinity or NaNs).
          num_evals: A scalar int32 `Tensor`. The total number of function
            evaluations made.
          left: Return value of value_and_gradients_function at the updated left
            end point of the interval.
          right: Return value of value_and_gradients_function at the updated right
            end point of the interval.
      """
      with tf.name_scope(name or "secant2"):
          # This will always be s.t. left <= c <= right
          val_c = value_and_gradients_function(
              _secant(search_interval.left, search_interval.right)
          )
          finished = _is_negative_inf(val_c.f)
          failed = ~search_interval.converged & (
              search_interval.failed | (~finished & ~is_finite(val_c))
          )
          converged = (
              search_interval.converged
              | finished
              | (
                  ~failed
                  & _satisfies_wolfe(
                      val_0,
                      val_c,
                      f_lim,
                      sufficient_decrease_param,
                      curvature_param,
                  )
              )
          )
          new_converged = converged & ~search_interval.converged
          val_left = val_where(
              new_converged, val_c, search_interval.left
          )
          val_right = val_where(
              new_converged, val_c, search_interval.right
          )
  
          initial_args = _Secant2Result(
              active=~failed & ~converged,
              converged=converged,
              failed=failed,
              num_evals=search_interval.func_evals + 1,
              left=val_left,
              right=val_right,
          )
  
          def _apply_secant2_inner():
              return _secant2_inner(
                  value_and_gradients_function,
                  initial_args,
                  val_0,
                  val_c,
                  f_lim,
                  sufficient_decrease_param,
                  curvature_param,
              )
  
          return prefer_static.cond(
              tf.reduce_any(initial_args.active),
              _apply_secant2_inner,
              lambda: initial_args,
          )
  
file_path: probability/tensorflow_probability/python/optimizer/linesearch/internal/hager_zhang_lib.py
imports: import collections
  import tensorflow.compat.v2
  from tensorflow_probability.python.internal import broadcast_util
  from tensorflow_probability.python.internal import prefer_static
label_desc: Use image node_id 2 for calling a global function with example usage: secant2(value_and_gradients_function, val_0, search_interval, f_lim, sufficient_decrease_param, curvature_param, name) and returns: prefer_static, _secant2_inner