class_docstr: This likelihood, which operates on groups of real-valued scalar observations, is obtained by
  integrating out a latent mean for each group. Both the prior on each latent mean as well as the
  observation likelihood for each data point are univariate Normal distributions.
  The prior means are controlled by `prior_loc` and `prior_scale`. The observation noise of the
  Normal likelihood is controlled by `obs_scale`, which is allowed to vary from observation to
  observation. The tensor of indices `group_idx` connects each observation to one of the groups
  specified by `prior_loc` and `prior_scale`.
  
  See e.g. Eqn. (55) in ref. [1] for relevant expressions in a simpler case with scalar `obs_scale`.
  
  Example:
  
  >>> num_groups = 3
  >>> num_data = 4
  >>> prior_loc = torch.randn(num_groups)
  >>> prior_scale = torch.rand(num_groups)
  >>> obs_scale = torch.rand(num_data)
  >>> group_idx = torch.tensor([1, 0, 2, 1]).long()
  >>> values = torch.randn(num_data)
  >>> gnn = GroupedNormalNormal(prior_loc, prior_scale, obs_scale, group_idx)
  >>> assert gnn.log_prob(values).shape == ()
  
  References:
  [1] "Conjugate Bayesian analysis of the Gaussian distribution," Kevin P. Murphy.
  
  :param torch.Tensor prior_loc: Tensor of shape `(num_groups,)` specifying the prior mean of the latent
      of each group.
  :param torch.Tensor prior_scale: Tensor of shape `(num_groups,)` specifying the prior scale of the latent
      of each group.
  :param torch.Tensor obs_scale: Tensor of shape `(num_data,)` specifying the scale of the observation noise
      of each observation.
  :param torch.LongTensor group_idx: Tensor of indices of shape `(num_data,)` linking each observation to one
      of the `num_groups` groups that are specified in `prior_loc` and `prior_scale`.
docstr: Get a `pyro.distributions.Normal` distribution that encodes the posterior distribution
  over the vector of latents specified by `prior_loc` and `prior_scale` conditioned on the
  observed data specified by `value`.
code: def get_posterior(self, value):
      """
      Get a `pyro.distributions.Normal` distribution that encodes the posterior distribution
      over the vector of latents specified by `prior_loc` and `prior_scale` conditioned on the
      observed data specified by `value`.
      """
      if value.shape != self.group_idx.shape:
          raise ValueError(
              "GroupedNormalNormal.get_posterior only supports values that have the same shape as group_idx."
          )
  
      obs_scale_sq_inv = self.obs_scale.pow(-2)
      prior_scale_sq_inv = self.prior_scale.pow(-2)
  
      obs_scale_sq_inv_sum = torch.zeros_like(
          self.prior_loc
      ).scatter_add(0, self.group_idx, obs_scale_sq_inv)
      precision = prior_scale_sq_inv + obs_scale_sq_inv_sum
      scaled_value_sum = torch.zeros_like(self.prior_loc).scatter_add(
          0, self.group_idx, value * obs_scale_sq_inv
      )
  
      loc = (
          scaled_value_sum + self.prior_loc * prior_scale_sq_inv
      ) / precision
      scale = precision.rsqrt()
  
      return Normal(loc=loc, scale=scale)
  
file_path: pyro/pyro/distributions/grouped_normal_normal.py
imports: import math
  import torch
  from torch.distributions.utils import broadcast_all
  from pyro.distributions import Normal, constraints
  from pyro.distributions.torch_distribution import TorchDistribution
label_desc: Use image node_id 4 for calling the GroupedNormalNormal obj's underlying member method code with example usage: obj.get_posterior(value) and returns: Normal