class_docstr: Wrap another tf optimizer with BoltOn privacy protocol.
  
  BoltOn optimizer wraps another tf optimizer to be used
  as the visible optimizer to the tf model. No matter the optimizer
  passed, "BoltOn" enables the bolt-on model to control the learning rate
  based on the strongly convex loss.
  
  To use the BoltOn method, you must:
  1. instantiate it with an instantiated tf optimizer and StrongConvexLoss.
  2. use it as a context manager around your .fit method internals.
  
  This can be accomplished by the following:
  optimizer = tf.optimizers.SGD()
  loss = privacy.bolt_on.losses.StrongConvexBinaryCrossentropy()
  bolton = BoltOn(optimizer, loss)
  with bolton(*args) as _:
    model.fit()
  The args required for the context manager can be found in the __call__
  method.
  
  For more details on the strong convexity requirements, see:
  Bolt-on Differential Privacy for Scalable Stochastic Gradient
  Descent-based Analytics by Xi Wu et. al.
docstr: Exit call from with statement.
  
  Used to:
  1.reset the model and fit parameters passed to the optimizer
    to enable the BoltOn Privacy guarantees. These are reset to ensure
    that any future calls to fit with the same instance of the optimizer
    will properly error out.
  
  2.call post-fit methods normalizing/projecting the model weights and
    adding noise to the weights.
  
  Args:
    *args: encompasses the type, value, and traceback values which are unused.
docstr_tok: ["Exit","call","from","with","statement",".","Used","to",":","1.reset","the","model","and","fit","parameters","passed","to","the","optimizer","to","enable","the","BoltOn","Privacy","guarantees",".","These","are","reset","to","ensure","that","any","future","calls","to","fit","with","the","same","instance","of","the","optimizer","will","properly","error","out",".","2.call","post-fit","methods","normalizing\/projecting","the","model","weights","and","adding","noise","to","the","weights",".","Args",":","*","args",":","encompasses","the","type",",","value",",","and","traceback","values","which","are","unused","."]
code: def __exit__(self, *args):
      """Exit call from with statement.
  
      Used to:
      1.reset the model and fit parameters passed to the optimizer
        to enable the BoltOn Privacy guarantees. These are reset to ensure
        that any future calls to fit with the same instance of the optimizer
        will properly error out.
  
      2.call post-fit methods normalizing/projecting the model weights and
        adding noise to the weights.
  
      Args:
        *args: encompasses the type, value, and traceback values which are unused.
      """
      self.project_weights_to_r(True)
      for layer in self.layers:
          input_dim = layer.kernel.shape[0]
          output_dim = layer.units
          noise = self.get_noise(
              input_dim,
              output_dim,
          )
          layer.kernel = tf.math.add(layer.kernel, noise)
      self.noise_distribution = None
      self.learning_rate.de_initialize()
      self.epsilon = -1
      self.batch_size = -1
      self.class_weights = None
      self.n_samples = None
      self.input_dim = None
      self.layers = None
      self._is_init = False
  
imports: import tensorflow.compat.v1
  from tensorflow.compat.v1.python.keras.optimizer_v2 import optimizer_v2
  from tensorflow.compat.v1.python.ops import math_ops
  from tensorflow_privacy.privacy.bolt_on.losses import StrongConvexMixin
label_desc: Use image node_id 17 for calling the BoltOn obj's underlying member method code with example usage: obj.__exit__() without return types