class_docstr: Encoding stage performing uniform quantization per channel in conv layers.
  
  This encoding stage will first reshape the input `x` to `(-1, dim)`, where
  `dim` is the size of the last dimension of `x`. Then each of the `dim`
  vectors will be quantized independently, using PRNG uniform quantization.
  
  PRNG uniform quantization uses a single integer seed as the same source of
  randomness in encode and decode methods, in order to, in the decode method,
  differently estimate the original values before encoding was applied.
  
  This encoding stage does not require the shape of the input `x` to the
  `encode` method to be statically known.
  
  In particular, given a floating point input `x` to the `encode` method, the
  output will have the same `dtype` as `x`, with values being "floating point
  integers" in the range `[0, 2**bits-1]`.
  
  The extreme points of the quantized interval will correspond to the min and
  max values of the input `x`.
  
  The output of the `decode` method is an unbiased estimation of the original
  values before quantization, but note that it is possible for some of the
  decoded values to be outside of the original range.
docstr: See base class.
code: def decode(
      self,
      encoded_tensors,
      decode_params,
      num_summands=None,
      shape=None,
  ):
      """See base class."""
      del num_summands  # Unused.
      quantized_x = encoded_tensors[self.ENCODED_VALUES_KEY]
      random_seed = encoded_tensors[self.SEED_PARAMS_KEY]
      min_max = encoded_tensors[self.MIN_MAX_VALUES_KEY]
      min_x, max_x = min_max[0], min_max[1]
      max_value = tf.cast(
          decode_params[self.MAX_INT_VALUE_PARAMS_KEY],
          quantized_x.dtype,
      )
  
      num_elements = tf.reduce_prod(tf.shape(quantized_x))
      # The rounding_floats are identical to those used in the encode method.
      rounding_floats = tf.reshape(
          self._random_floats(num_elements, random_seed, min_x.dtype),
          tf.shape(quantized_x),
      )
  
      # Regenerating the random values used in encode, enables us to determine a
      # narrower range of possible original values, before quantization was
      # applied. We shift the quantized values into the middle of this range,
      # corresponding to the intersection of
      # [quantized_x - 1 + rounding_floats, quantized_x + rounding_floats]
      # in the quantized range. This shifted value can be out of the range
      # [0, max_value] and therefore the decoded value can be out of the range
      # [min_x, max_x], which is impossible, but it ensures that the decoded x
      # is an unbiased estimator of the original values before quantization.
      q_shifted = quantized_x + rounding_floats - 0.5
  
      x = q_shifted / max_value * (max_x - min_x) + min_x
  
      x = tf.reshape(x, shape)
  
      return x
  
imports: from __future__ import absolute_import
  from __future__ import division
  from __future__ import print_function
  import collections
  import tensorflow
  from tensorflow_model_optimization.python.core.internal.tensor_encoding.core import encoding_stage
  from tensorflow_model_optimization.python.core.internal.tensor_encoding.utils import tf_utils
label_desc: Use image node_id 8 for calling the PerChannelPRNGUniformQuantizationEncodingStage obj's underlying member method code with example usage: obj.decode(encoded_tensors, decode_params, num_summands, shape) and returns: x