class_docstr: Generates a tfp.distribution.Normal by predicting a mean and std.
  
  Note: By default this network uses `tanh_squash_to_spec` to normalize its
  output. Due to the nature of the `tanh` function, values near the spec bounds
  cannot be returned.
  
  Note: The standard deviations are independent of the input.
class_docstr_tok: ["Generates","a","tfp.distribution.Normal","by","predicting","a","mean","and","std",".","Note",":","By","default","this","network","uses","`","tanh_squash_to_spec","`","to","normalize","its","output",".","Due","to","the","nature","of","the","`","tanh","`","function",",","values","near","the","spec","bounds","can","not","be","returned",".","Note",":","The","standard","deviations","are","independent","of","the","input","."]
code: def call(self, inputs, outer_rank, training=False, mask=None):
      if inputs.dtype != self._sample_spec.dtype:
          raise ValueError(
              "Inputs to NormalProjectionNetwork must match the sample_spec.dtype."
          )
  
      if mask is not None:
          raise NotImplementedError(
              "NormalProjectionNetwork does not yet implement action masking; got "
              "mask={}".format(mask)
          )
  
      # outer_rank is needed because the projection is not done on the raw
      # observations so getting the outer rank is hard as there is no spec to
      # compare to.
      batch_squash = network_utils.BatchSquash(outer_rank)
      inputs = batch_squash.flatten(inputs)
  
      means = self._means_projection_layer(inputs, training=training)
      means = tf.reshape(
          means, [-1] + self._sample_spec.shape.as_list()
      )
  
      # If scaling the distribution later, use a normalized mean.
      if (
          not self._scale_distribution
          and self._mean_transform is not None
      ):
          means = self._mean_transform(means, self._sample_spec)
      means = tf.cast(means, self._sample_spec.dtype)
  
      if self._state_dependent_std:
          stds = self._stddev_projection_layer(
              inputs, training=training
          )
      else:
          stds = self._bias(tf.zeros_like(means), training=training)
          stds = tf.reshape(
              stds, [-1] + self._sample_spec.shape.as_list()
          )
  
      if self._std_transform is not None:
          stds = self._std_transform(stds)
      stds = tf.cast(stds, self._sample_spec.dtype)
  
      means = batch_squash.unflatten(means)
      stds = batch_squash.unflatten(stds)
  
      return (
          self.output_spec.build_distribution(loc=means, scale=stds),
          (),
      )
  
file_path: agents/tf_agents/networks/normal_projection_network.py
imports: from __future__ import absolute_import
  from __future__ import division
  from __future__ import print_function
  import sys
  import gin
  import tensorflow
  import tensorflow_probability
  from tf_agents.distributions import utils
  from tf_agents.keras_layers import bias_layer
  from tf_agents.networks import network
  from tf_agents.networks import utils
  from tf_agents.specs import distribution_spec
  from tf_agents.specs import tensor_spec
label_desc: Use image node_id 3 for calling the NormalProjectionNetwork obj's underlying member method code with example usage: obj.call(inputs, outer_rank, training, mask) and returns: self,  