code: def make_gaussian_query_optimizer_class(cls):
      """Returns a differentially private optimizer using the `GaussianSumQuery`.
  
      Args:
        cls: `DPOptimizerClass`, the output of `make_keras_optimizer_class`.
  
      Returns:
        A DP-SGD subclass of `cls` using the `GaussianQuery`, the canonical DP-SGD
        implementation.
      """
  
      def return_gaussian_query_optimizer(
          l2_norm_clip: float,
          noise_multiplier: float,
          num_microbatches: Optional[int] = None,
          gradient_accumulation_steps: int = 1,
          *args,  # pylint: disable=keyword-arg-before-vararg, g-doc-args
          **kwargs,
      ):
          """Returns a `DPOptimizerClass` `cls` using the `GaussianSumQuery`.
  
          This function is a thin wrapper around
          `make_keras_optimizer_class.<locals>.DPOptimizerClass` which can be used to
          apply a `GaussianSumQuery` to any `DPOptimizerClass`.
  
          When combined with stochastic gradient descent, this creates the canonical
          DP-SGD algorithm of "Deep Learning with Differential Privacy"
          (see https://arxiv.org/abs/1607.00133).
  
          When instantiating this optimizer, you need to supply several
          DP-related arguments followed by the standard arguments for
          `{short_base_class}`.
  
          As an example, see the below or the documentation of the DPOptimizerClass.
  
          ```python
          # Create optimizer.
          opt = {dp_keras_class}(l2_norm_clip=1.0, noise_multiplier=0.5,
              num_microbatches=1, <standard arguments>)
          ```
  
          Args:
            l2_norm_clip: Clipping norm (max L2 norm of per microbatch gradients).
            noise_multiplier: Ratio of the standard deviation to the clipping norm.
            num_microbatches: Number of microbatches into which each minibatch is
              split. Default is `None` which means that number of microbatches is
              equal to batch size (i.e. each microbatch contains exactly one example).
              If `gradient_accumulation_steps` is greater than 1 and
              `num_microbatches` is not `None` then the effective number of
              microbatches is equal to `num_microbatches *
              gradient_accumulation_steps`.
            gradient_accumulation_steps: If greater than 1 then optimizer will be
              accumulating gradients for this number of optimizer steps before
              applying them to update model weights. If this argument is set to 1 then
              updates will be applied on each optimizer step.
            *args: These will be passed on to the base class `__init__` method.
            **kwargs: These will be passed on to the base class `__init__` method.
          """
          dp_sum_query = gaussian_query.GaussianSumQuery(
              l2_norm_clip, l2_norm_clip * noise_multiplier
          )
          return cls(
              dp_sum_query=dp_sum_query,
              num_microbatches=num_microbatches,
              gradient_accumulation_steps=gradient_accumulation_steps,
              *args,
              **kwargs,
          )
  
      return return_gaussian_query_optimizer
  
file_path: privacy/tensorflow_privacy/privacy/optimizers/dp_optimizer_keras.py
imports: from typing import List, Optional, Type, Union
  import warnings
  import tensorflow
  from tensorflow_privacy.privacy.dp_query import dp_query
  from tensorflow_privacy.privacy.dp_query import gaussian_query
  from tensorflow_privacy.privacy.dp_query import restart_query
  from tensorflow_privacy.privacy.dp_query import tree_aggregation_query
label_desc: Use image node_id 3 for calling a global function with example usage: make_gaussian_query_optimizer_class(cls) and returns: return_gaussian_query_optimizer, cls