text: """Courtesy of OpenAI Baselines, distributions.py"""
  
  import numpy as np
  import tensorflow as tf
  
  EPS = 1e-9  # to prevent numerical problems such as ln(0)
  
  
  class DiagGaussianPd:
      def __init__(self, mean, logstd):
          self.mean = mean
          self.logstd = logstd
          self.std = tf.exp(logstd)
  
      def neglogp(self, x):
          return 0.5 * tf.reduce_sum(tf.square((x - self.mean) / (self.std + EPS)), axis=-1) \
                 + 0.5 * np.log(2.0 * np.pi + EPS) * tf.to_float(tf.shape(x)[-1]) \
                 + tf.reduce_sum(self.logstd, axis=-1)
  
      def kl(self, other):
          assert isinstance(other, DiagGaussianPd)
          return tf.reduce_sum(other.logstd - self.logstd + (tf.square(self.std) + tf.square(self.mean - other.mean)) / (
                  2.0 * tf.square(other.std)) - 0.5, axis=-1)
  
      def entropy(self):
          return tf.reduce_sum(self.logstd + .5 * np.log(2.0 * np.pi * np.e + EPS), axis=-1)
  
      def sample(self):
          return self.mean + self.std * tf.random_normal(tf.shape(self.mean))
  
  
  class CategoricalProbabilityDistribution:
      def __init__(self, logits):
          self.logits = logits
  
      def entropy(self):
          a0 = self.logits - tf.reduce_max(self.logits, axis=-1, keepdims=True)
          ea0 = tf.exp(a0)
          z0 = tf.reduce_sum(ea0, axis=-1, keepdims=True)
          p0 = ea0 / z0
          return tf.reduce_sum(p0 * (tf.log(z0 + EPS) - a0), axis=-1)
  
      def sample(self):
          u = tf.random_uniform(tf.shape(self.logits))
          return tf.argmax(self.logits - tf.log(-tf.log(u + EPS)), axis=-1)
  