class_docstr: Keras' layer implementation for DynamicNormalization.
  
  Similar to Batch Normalization, DynamicNormalization normalizes the
  activations of the previous layer for each input.
  
  The normalization formula:
    ((|mean|^2 + p(mean)) / |x|^2 - 2 * g(mean)) / sqrt(variance)
  where
    p(mean) = prior_scale * mean + prior_offset
    g(mean) = mean_scale * mean + mean_offset.
  Here (prior_scale, prior_offset, mean_scale, mean_offset) are learnable
  parameters.
code: def build(self, input_shape):
      input_shape = tf.TensorShape(input_shape)
      if not input_shape.ndims:
          raise ValueError("Input has undefined rank:", input_shape)
      ndims = len(input_shape)
  
      # Convert axis to list and resolve negatives
      if isinstance(self.axis, int):
          self.axis = [self.axis]
  
      for idx, x in enumerate(self.axis):
          if x < 0:
              self.axis[idx] = ndims + x
  
      # Validate axes
      for x in self.axis:
          if x < 0 or x >= ndims:
              raise ValueError("Invalid axis: %d" % x)
      if len(self.axis) != len(set(self.axis)):
          raise ValueError("Duplicate axis: %s" % self.axis)
  
      axis_to_dim = {x: input_shape.dims[x].value for x in self.axis}
      for x in axis_to_dim:
          if axis_to_dim[x] is None:
              raise ValueError(
                  "Input has undefined `axis` dimension. Input shape: ",
                  input_shape,
              )
      self.input_spec = tf.keras.layers.InputSpec(
          ndim=ndims, axes=axis_to_dim
      )
  
      if len(axis_to_dim) == 1:
          # Single axis batch norm (most common/default use-case)
          param_shape = (list(axis_to_dim.values())[0],)
      else:
          # Parameter shape is the original shape but with 1 in all non-axis dims
          param_shape = [
              axis_to_dim[i] if i in axis_to_dim else 1
              for i in range(ndims)
          ]
  
      self.mean_offset = self._add_offset("mean_offset", param_shape)
      self.mean_scale = self._add_scale("mean_scale", param_shape)
  
      if not self.use_batch_normalization:
          self.prior_offset = self._add_offset(
              "prior_offset", param_shape
          )
          self.prior_scale = self._add_scale("prior_scale", param_shape)
  
      self.built = True
  
file_path: neural-structured-learning/research/carls/dynamic_normalization.py
imports: import typing
  from research.carls import dynamic_embedding_config_pb2
  from research.carls import dynamic_memory_ops
  import tensorflow
label_desc: Use image node_id 7 for calling the DynamicNormalization obj's underlying member method code with example usage: obj.build(input_shape) without return types