docstr: Calculate the log probability of data given the current distribution.
  
  See http://www.notenoughthoughts.net/posts/normal-log-likelihood-gradient.html
  and https://discuss.mxnet.io/t/multivariate-gaussian-log-density-operator/1169/7
  
  :param x: input data,
      of shape (num_var) or
      of shape (batch_size, num_var) or
      of shape (batch_size, time_step, num_var).
  :return: log_probability,
      of shape (1) or
      of shape (batch_size) or
      of shape (batch_size, time_step).
docstr_tok: ["Calculate","the","log","probability","of","data","given","the","current","distribution",".","See","http",":","\/\/www.notenoughthoughts.net\/posts\/normal-log-likelihood-gradient.html","and","https",":","\/\/discuss.mxnet.io\/t\/multivariate-gaussian-log-density-operator\/1169\/7",":","param","x",":","input","data",",","of","shape","(","num_var",")","or","of","shape","(","batch_size",",","num_var",")","or","of","shape","(","batch_size",",","time_step",",","num_var",")",".",":","return",":","log_probability",",","of","shape","(","1",")","or","of","shape","(","batch_size",")","or","of","shape","(","batch_size",",","time_step",")","."]
code: def log_prob(self, x: nd_sym_type) -> nd_sym_type:
      """
      Calculate the log probability of data given the current distribution.
  
      See http://www.notenoughthoughts.net/posts/normal-log-likelihood-gradient.html
      and https://discuss.mxnet.io/t/multivariate-gaussian-log-density-operator/1169/7
  
      :param x: input data,
          of shape (num_var) or
          of shape (batch_size, num_var) or
          of shape (batch_size, time_step, num_var).
      :return: log_probability,
          of shape (1) or
          of shape (batch_size) or
          of shape (batch_size, time_step).
      """
      a = (self.num_var / 2) * math.log(2 * math.pi)
      log_det_sigma = self.log_det(self.sigma)
      b = (1 / 2) * log_det_sigma
      sigma_inv = self.inverse_using_cholesky(self.sigma)
      # deviation from mean, and dev_t is equivalent to transpose on last two dims.
      dev = (x - self.mean).expand_dims(-1)
      dev_t = (x - self.mean).expand_dims(-2)
  
      # since batch_dot only works with ndarrays with ndim of 3,
      # and we could have ndarrays with ndim of 4,
      # we flatten batch_size and time_step into single dim.
      dev_flat = dev.reshape(shape=(-1, 0, 0), reverse=1)
      sigma_inv_flat = sigma_inv.reshape(shape=(-1, 0, 0), reverse=1)
      dev_t_flat = dev_t.reshape(shape=(-1, 0, 0), reverse=1)
      c = (1 / 2) * self.F.batch_dot(
          self.F.batch_dot(dev_t_flat, sigma_inv_flat), dev_flat
      )
      # and now reshape back to (batch_size, time_step) if required.
      c = c.reshape_like(b)
  
      log_likelihood = -a - b - c
      return log_likelihood
  
imports: from typing import List, Tuple, Union
  from types import ModuleType
  import math
  import mxnet
  from mxnet.gluon import nn
  from rl_coach.base_parameters import AgentParameters
  from rl_coach.core_types import ActionProbabilities
  from rl_coach.spaces import SpacesDefinition, BoxActionSpace, DiscreteActionSpace
  from rl_coach.utils import eps
  from rl_coach.architectures.mxnet_components.heads.head import Head, HeadLoss, LossInputSchema, NormalizedRSSInitializer
  from rl_coach.architectures.mxnet_components.heads.head import LOSS_OUT_TYPE_LOSS, LOSS_OUT_TYPE_REGULARIZATION
  from rl_coach.architectures.mxnet_components.utils import hybrid_clip, broadcast_like
label_desc: Use image node_id 4 for calling the MultivariateNormalDist obj's underlying member method code with example usage: obj.log_prob(x) and returns: log_likelihood