code: def _bfgs_inv_hessian_update(
      grad_delta,
      position_delta,
      normalization_factor,
      inv_hessian_estimate,
  ):
      """Applies the BFGS update to the inverse Hessian estimate.
  
      The BFGS update rule is (note A^T denotes the transpose of a vector/matrix A).
  
      ```None
        rho = 1/(grad_delta^T * position_delta)
        U = (I - rho * position_delta * grad_delta^T)
        H_1 =  U * H_0 * U^T + rho * position_delta * position_delta^T
      ```
  
      Here, `H_0` is the inverse Hessian estimate at the previous iteration and
      `H_1` is the next estimate. Note that `*` should be interpreted as the
      matrix multiplication (with the understanding that matrix multiplication for
      scalars is usual multiplication and for matrix with vector is the action of
      the matrix on the vector.).
  
      The implementation below utilizes an expanded version of the above formula
      to avoid the matrix multiplications that would be needed otherwise. By
      expansion it is easy to see that one only needs matrix-vector or
      vector-vector operations. The expanded version is:
  
      ```None
        f = 1 + rho * (grad_delta^T * H_0 * grad_delta)
        H_1 - H_0 = - rho * [position_delta * (H_0 * grad_delta)^T +
                            (H_0 * grad_delta) * position_delta^T] +
                      rho * f * [position_delta * position_delta^T]
      ```
  
      All the terms in square brackets are matrices and are constructed using
      vector outer products. All the other terms on the right hand side are scalars.
      Also worth noting that the first and second lines are both rank 1 updates
      applied to the current inverse Hessian estimate.
  
      Args:
        grad_delta: Real `Tensor` of shape `[..., n]`. The difference between the
          gradient at the new position and the old position.
        position_delta: Real `Tensor` of shape `[..., n]`. The change in position
          from the previous iteration to the current one.
        normalization_factor: Real `Tensor` of shape `[...]`. Should be equal to
          `grad_delta^T * position_delta`, i.e. `1/rho` as defined above.
        inv_hessian_estimate: Real `Tensor` of shape `[..., n, n]`. The previous
          estimate of the inverse Hessian. Should be positive definite and
          symmetric.
  
      Returns:
        A tuple containing the following fields
          is_valid: A Boolean `Tensor` of shape `[...]` indicating batch members
            where the update succeeded. The update can fail if the position change
            becomes orthogonal to the gradient change.
          next_inv_hessian_estimate: A `Tensor` of shape `[..., n, n]`. The next
            Hessian estimate updated using the BFGS update scheme. If the
            `inv_hessian_estimate` is symmetric and positive definite, the
            `next_inv_hessian_estimate` is guaranteed to satisfy the same
            conditions.
      """
      # The quadratic form: y^T.H.y; where H is the inverse Hessian and y is the
      # gradient change.
      conditioned_grad_delta = tf.linalg.matvec(
          inv_hessian_estimate, grad_delta
      )
      conditioned_grad_delta_norm = tf.reduce_sum(
          conditioned_grad_delta * grad_delta, axis=-1
      )
  
      # The first rank 1 update term requires the outer product: s.y^T.
      cross_term = _tensor_product(
          position_delta, conditioned_grad_delta
      )
  
      def _expand_scalar(s):
          # Expand dimensions of a batch of scalars to multiply or divide a matrix.
          return s[..., tf.newaxis, tf.newaxis]
  
      # Symmetrize
      cross_term += _tensor_product(
          conditioned_grad_delta, position_delta
      )
      position_term = _tensor_product(position_delta, position_delta)
      with tf.control_dependencies([position_term]):
          position_term *= _expand_scalar(
              1 + conditioned_grad_delta_norm / normalization_factor
          )
  
      return inv_hessian_estimate + (
          position_term - cross_term
      ) / _expand_scalar(normalization_factor)
  
file_path: probability/tensorflow_probability/python/optimizer/bfgs.py
imports: import collections
  import tensorflow.compat.v2
  from tensorflow_probability.python.internal import distribution_util
  from tensorflow_probability.python.internal import dtype_util
  from tensorflow_probability.python.internal import prefer_static
  from tensorflow_probability.python.optimizer import bfgs_utils
label_desc: Use image node_id 5 for calling a global function with example usage: _bfgs_inv_hessian_update(grad_delta, position_delta, normalization_factor, inv_hessian_estimate) and returns: unknown, s