code: def _observation_particles_cov_linop(
      predicted_observation_particles,
      ensemble_mean_observations,
      observation_cov,
  ):
      """LinearOperatorLowRankUpdate holding observation noise covariance.
  
      All arguments can be derived from `observation_particles_dist`. We pass them
      as arguments to have a simpler graph, and encourage calling `.sample` once.
  
      Args:
        predicted_observation_particles: Ensemble of state particles fed through the
          observation function.  `observation_particles_dist.mean()`
        ensemble_mean_observations: Ensemble mean (mean across `axis=0`) of
          `predicted_observation_particles`.
        observation_cov: `LinearOperator` defining the observation noise covariance.
          `_linop_covariance(observation_particles_dist)`.
  
      Returns:
        LinearOperatorLowRankUpdate with covariance the sum of `observation_cov`
          and the ensemble covariance of `predicted_observation_particles`.
      """
      # In our usual docstring notation, let B be a batch shape, X be the ensemble
      # of states, and G(X) the deterministic observation transformation of X. Then,
      # predicted_observations_particles = G(X)  (an ensemble)
      #                  shape = [n_ensemble] + B + [n_observations]
      # ensemble_mean_observations =
      #    tf.reduce_mean(predicted_observations, axis=0)  # Ensemble mean
  
      # Create matrix U with shape B + [n_observations, n_ensemble] so that, with
      # Cov the ensemble covariance, Cov(G(X)) = UUᵀ.
      centered_observations = (
          predicted_observation_particles - ensemble_mean_observations
      )
      n_ensemble = tf.cast(
          tf.shape(centered_observations)[0],
          centered_observations.dtype,
      )
      u = distribution_util.rotate_transpose(
          centered_observations / tf.sqrt(n_ensemble), -1
      )
  
      # cov_operator ~ Γ + Cov(G(X))
      return tf.linalg.LinearOperatorLowRankUpdate(
          base_operator=observation_cov,  # = Γ
          u=u,  # UUᵀ = Cov(G(X))
          is_self_adjoint=True,
          is_positive_definite=True,
      )
  
file_path: probability/tensorflow_probability/python/experimental/sequential/ensemble_kalman_filter.py
imports: import collections
  import tensorflow.compat.v2
  from tensorflow_probability.python.distributions import mvn_linear_operator
  from tensorflow_probability.python.distributions import mvn_low_rank_update_linear_operator_covariance
  from tensorflow_probability.python.distributions import mvn_tril
  from tensorflow_probability.python.internal import distribution_util
  from tensorflow_probability.python.internal import dtype_util
label_desc: Use image node_id 7 for calling a global function with example usage: _observation_particles_cov_linop(predicted_observation_particles, ensemble_mean_observations, observation_cov) and returns: tf