class_docstr: ### Description
      This predicts the mean and logvariance parameters,
      then generates an approximate sample from the posterior.
  
  ### Diagram
  
  ### Constraints
class_docstr_tok: ["#","#","#","Description","This","predicts","the","mean","and","logvariance","parameters",",","then","generates","an","approximate","sample","from","the","posterior",".","#","#","#","Diagram","#","#","#","Constraints"]
docstr: :param codes: tensor, input latent space
  :param is_training: boolean, True if network is in training mode
  :return: samples from posterior distribution, means and log variances of the posterior distribution
docstr_tok: [":","param","codes",":","tensor",",","input","latent","space",":","param","is_training",":","boolean",",","True","if","network","is","in","training","mode",":","return",":","samples","from","posterior","distribution",",","means","and","log","variances","of","the","posterior","distribution"]
code: def layer_op(self, codes, is_training):
      """
  
      :param codes: tensor, input latent space
      :param is_training: boolean, True if network is in training mode
      :return: samples from posterior distribution, means and log variances of the posterior distribution
      """
  
      def clip(input):
          # This is for clipping logvars,
          # so that variances = exp(logvars) behaves well
          output = tf.maximum(input, self.logvars_lower_bound)
          output = tf.minimum(output, self.logvars_upper_bound)
          return output
  
      encoder_means = FullyConnectedLayer(
          n_output_chns=self.number_of_latent_variables,
          feature_normalization=None,
          acti_func=None,
          w_initializer=self.initializers["w"],
          w_regularizer=self.regularizers["w"],
          name="encoder_fc_means_{}".format(
              self.number_of_latent_variables
          ),
      )
      print(encoder_means)
  
      encoder_logvars = FullyConnectedLayer(
          n_output_chns=self.number_of_latent_variables,
          feature_normalization=None,
          acti_func=None,
          w_initializer=self.initializers["w"],
          w_regularizer=self.regularizers["w"],
          name="encoder_fc_logvars_{}".format(
              self.number_of_latent_variables
          ),
      )
      print(encoder_logvars)
  
      # Predict the posterior distribution's parameters
      posterior_means = encoder_means(codes, is_training)
      posterior_logvars = clip(encoder_logvars(codes, is_training))
  
      if self.number_of_samples == 1:
          noise_sample = tf.random_normal(
              tf.shape(posterior_means), 0.0, 1.0
          )
      else:
          sample_shape = tf.concat(
              [
                  tf.constant(
                      self.number_of_samples,
                      shape=[
                          1,
                      ],
                  ),
                  tf.shape(posterior_means),
              ],
              axis=0,
          )
          noise_sample = tf.reduce_mean(
              tf.random_normal(sample_shape, 0.0, 1.0), axis=0
          )
  
      return [
          posterior_means
          + tf.exp(0.5 * posterior_logvars) * noise_sample,
          posterior_means,
          posterior_logvars,
      ]
  
imports: from __future__ import absolute_import, print_function, division
  import numpy
  import tensorflow
  from niftynet.layer.base_layer import TrainableLayer
  from niftynet.layer.convolution import ConvolutionalLayer
  from niftynet.layer.deconvolution import DeconvolutionalLayer
  from niftynet.layer.fully_connected import FullyConnectedLayer
  from niftynet.layer.upsample import UpSampleLayer
label_desc: Use image node_id 2 for calling the GaussianSampler obj's underlying member method code with example usage: obj.layer_op(codes, is_training) and returns: list, output