class_docstr: Creates a banana-shaped distribution.
  
  This distribution was first described in [1]. The distribution is constructed
  by transforming a 2-D normal distribution with scale [10, 1] by shifting the
  second dimension by `curvature * (x0**2 - 100)` where `x0` is the value of
  the first dimension. If N > 2 dimensions are requested, the remaining
  dimensions are distributed as a standard normal.
  
  ```none
  x[0] ~ Normal(loc=0, scale=10)
  x[1] ~ Normal(loc=curvature * (x[0]**2 - 100), scale=1)
  for i in range(2, ndims):
    x[i] ~ Normal(loc=0, scale=1)
  ```
  
  This distribution is notable for having relatively narrow tails, while being
  derived from a simple, volume-preserving transformation of a normal
  distribution. Despite this simplicity, some inference algorithms have trouble
  sampling from this distribution.
  
  #### References
  
  1. Haario, H., Saksman, E., & Tamminen, J. (1999). Adaptive proposal
     distribution for random walk Metropolis algorithm. Computational
     Statistics, 14(3), 375-396.
docstr: Construct the banana model.
  
  Args:
    ndims: Python integer. Dimensionality of the distribution. Must be at
      least 2.
    curvature: Python float. Controls the strength of the curvature of
      the distribution.
    name: Python `str` name prefixed to Ops created by this class.
    pretty_name: A Python `str`. The pretty name of this model.
  
  Raises:
    ValueError: If ndims < 2.
docstr_tok: ["Construct","the","banana","model",".","Args",":","ndims",":","Python","integer",".","Dimensionality","of","the","distribution",".","Must","be","at","least","2.","curvature",":","Python","float",".","Controls","the","strength","of","the","curvature","of","the","distribution",".","name",":","Python","`","str","`","name","prefixed","to","Ops","created","by","this","class",".","pretty_name",":","A","Python","`","str","`",".","The","pretty","name","of","this","model",".","Raises",":","ValueError",":","If","ndims","<","2","."]
code: def __init__(
      self,
      ndims=2,
      curvature=0.03,
      name="banana",
      pretty_name="Banana",
  ):
      """Construct the banana model.
  
      Args:
        ndims: Python integer. Dimensionality of the distribution. Must be at
          least 2.
        curvature: Python float. Controls the strength of the curvature of
          the distribution.
        name: Python `str` name prefixed to Ops created by this class.
        pretty_name: A Python `str`. The pretty name of this model.
  
      Raises:
        ValueError: If ndims < 2.
      """
      if ndims < 2:
          raise ValueError(
              "ndims must be at least 2, saw: {}".format(ndims)
          )
  
      with tf.name_scope(name):
  
          def bijector_fn(x):
              """Banana transform."""
              batch_shape = ps.shape(x)[:-1]
              shift = tf.concat(
                  [
                      tf.zeros(ps.concat([batch_shape, [1]], axis=0)),
                      curvature * (tf.square(x[..., :1]) - 100),
                      tf.zeros(
                          ps.concat([batch_shape, [ndims - 2]], axis=0)
                      ),
                  ],
                  axis=-1,
              )
              return tfb.Shift(shift)
  
          mg = tfd.MultivariateNormalDiag(
              loc=tf.zeros(ndims),
              scale_diag=[10.0] + [1.0] * (ndims - 1),
          )
          banana = tfd.TransformedDistribution(
              mg,
              bijector=tfb.MaskedAutoregressiveFlow(
                  bijector_fn=bijector_fn
              ),
          )
  
          sample_transformations = {
              "identity": model.Model.SampleTransformation(
                  fn=lambda params: params,
                  pretty_name="Identity",
                  # The second dimension is a sum of scaled Chi2 and normal
                  # distribution.
                  # Mean of Chi2 with one degree of freedom is 1, but since the
                  # first element has variance of 100, it cancels with the shift
                  # (which is why the shift is there).
                  ground_truth_mean=np.zeros(ndims),
                  # Variance of Chi2 with one degree of freedom is 2.
                  ground_truth_standard_deviation=np.array(
                      [10.0]
                      + [np.sqrt(1.0 + 2 * curvature**2 * 10.0**4)]
                      + [1.0] * (ndims - 2)
                  ),
              )
          }
  
      self._banana = banana
  
      super(Banana, self).__init__(
          default_event_space_bijector=tfb.Identity(),
          event_shape=banana.event_shape,
          dtype=banana.dtype,
          name=name,
          pretty_name=pretty_name,
          sample_transformations=sample_transformations,
      )
  
file_path: probability/spinoffs/inference_gym/inference_gym/targets/banana.py
imports: import numpy
  import tensorflow.compat.v2
  import tensorflow_probability
  from tensorflow_probability.python.internal import prefer_static
  from inference_gym.targets import model
label_desc: Use image node_id 1 to create a new Banana object from inherited base classes: model with example: obj = Banana(ndims, curvature, name, pretty_name)