class_docstr: ``torch.optim.Optimizer`` wrapper that adds additional functionality to clip per
  sample gradients and add Gaussian noise.
  
  Can be used with any ``torch.optim.Optimizer`` subclass as an underlying optimizer.
  ``DPOptimzer`` assumes that parameters over which it performs optimization belong
  to GradSampleModule and therefore have the ``grad_sample`` attribute.
  
  On a high level ``DPOptimizer``'s step looks like this:
  1) Aggregate ``p.grad_sample`` over all parameters to calculate per sample norms
  2) Clip ``p.grad_sample`` so that per sample norm is not above threshold
  3) Aggregate clipped per sample gradients into ``p.grad``
  4) Add Gaussian noise to ``p.grad`` calibrated to a given noise multiplier and
  max grad norm limit (``std = noise_multiplier * max_grad_norm``).
  5) Call underlying optimizer to perform optimization step
  
  Examples:
      >>> module = MyCustomModel()
      >>> optimizer = torch.optim.SGD(module.parameters(), lr=0.1)
      >>> dp_optimizer = DPOptimizer(
      ...     optimizer=optimizer,
      ...     noise_multiplier=1.0,
      ...     max_grad_norm=1.0,
      ...     expected_batch_size=4,
      ... )
docstr: Perform actions specific to ``DPOptimizer`` before calling
  underlying  ``optimizer.step()``
  
  Args:
      closure: A closure that reevaluates the model and
          returns the loss. Optional for most optimizers.
docstr_tok: ["Perform","actions","specific","to","``","DPOptimizer","``","before","calling","underlying","``","optimizer.step","(",")","``","Args",":","closure",":","A","closure","that","reevaluates","the","model","and","returns","the","loss",".","Optional","for","most","optimizers","."]
code: def pre_step(
      self, closure: Optional[Callable[[], float]] = None
  ) -> Optional[float]:
      """
      Perform actions specific to ``DPOptimizer`` before calling
      underlying  ``optimizer.step()``
  
      Args:
          closure: A closure that reevaluates the model and
              returns the loss. Optional for most optimizers.
      """
      self.clip_and_accumulate()
      if self._check_skip_next_step():
          self._is_last_step_skipped = True
          return False
  
      self.add_noise()
      self.scale_grad()
  
      if self.step_hook:
          self.step_hook(self)
  
      self._is_last_step_skipped = False
      return True
  
imports: from __future__ import annotations
  import logging
  from typing import Callable, List, Optional, Union
  import torch
  from opacus.optimizers.utils import params
  from opt_einsum.contract import contract
  from torch import nn
  from torch.optim import Optimizer
label_desc: Use image node_id 13 for calling the DPOptimizer obj's underlying member method code with example usage: obj.pre_step(closure) and returns: True, False