class_docstr: Extends SGD in PyTorch with LARS scaling from the paper "Large batch training of
  Convolutional Networks" [0].
  
  Implementation from PyTorch Lightning Bolts [1].
  
  - [0]: https://arxiv.org/pdf/1708.03888.pdf
  - [1]: https://github.com/Lightning-Universe/lightning-bolts/blob/2dfe45a4cf050f120d10981c45cfa2c785a1d5e6/pl_bolts/optimizers/lars.py#L1
  
  Args:
      params: 
          Iterable of parameters to optimize or dicts defining parameter groups.
      lr:
          Learning rate
      momentum:
          Momentum factor.
      weight_decay:
          Weight decay (L2 penalty).
      dampening:
          Dampening for momentum.
      nesterov:
          Enables Nesterov momentum.
      trust_coefficient:
          Trust coefficient for computing learning rate.
      eps:
          Eps for division denominator.
  
  Example:
      >>> model = torch.nn.Linear(10, 1)
      >>> input = torch.Tensor(10)
      >>> target = torch.Tensor([1.])
      >>> loss_fn = lambda input, target: (input - target) ** 2
      >>> #
      >>> optimizer = LARS(model.parameters(), lr=0.1, momentum=0.9)
      >>> optimizer.zero_grad()
      >>> loss_fn(model(input), target).backward()
      >>> optimizer.step()
  
  .. note::
      The application of momentum in the SGD part is modified according to
      the PyTorch standards. LARS scaling fits into the equation in the
      following fashion.
  
      .. math::
          egin{aligned}
              g_{t+1} & =     ext{lars_lr} * (eta * p_{t} + g_{t+1}), \
              v_{t+1} & = \mu * v_{t} + g_{t+1}, \
              p_{t+1} & = p_{t} -     ext{lr} * v_{t+1},
          \end{aligned}
  
      where :math:`p`, :math:`g`, :math:`v`, :math:`\mu` and :math:`eta` denote the
      parameters, gradient, velocity, momentum, and weight decay respectively.
      The :math:`lars_lr` is defined by Eq. 6 in the paper.
      The Nesterov version is analogously modified.
  
  .. warning::
      Parameters with weight decay set to 0 will automatically be excluded from
      layer-wise LR scaling. This is to ensure consistency with papers like SimCLR
      and BYOL.
docstr: Performs a single optimization step.
  
  Args:
      closure (callable, optional): A closure that reevaluates the model
          and returns the loss.
code: def step(
      self, closure: Optional[Callable[[], float]] = None
  ) -> Optional[float]:
      """Performs a single optimization step.
  
      Args:
          closure (callable, optional): A closure that reevaluates the model
              and returns the loss.
      """
      loss = None
      if closure is not None:
          with torch.enable_grad():
              loss = closure()
  
      # exclude scaling for params with 0 weight decay
      for group in self.param_groups:
          weight_decay = group["weight_decay"]
          momentum = group["momentum"]
          dampening = group["dampening"]
          nesterov = group["nesterov"]
  
          for p in group["params"]:
              if p.grad is None:
                  continue
  
              d_p = p.grad
              p_norm = torch.norm(p.data)
              g_norm = torch.norm(p.grad.data)
  
              # lars scaling + weight decay part
              if weight_decay != 0:
                  if p_norm != 0 and g_norm != 0:
                      lars_lr = p_norm / (
                          g_norm + p_norm * weight_decay + group["eps"]
                      )
                      lars_lr *= group["trust_coefficient"]
  
                      d_p = d_p.add(p, alpha=weight_decay)
                      d_p *= lars_lr
  
              # sgd part
              if momentum != 0:
                  param_state = self.state[p]
                  if "momentum_buffer" not in param_state:
                      buf = param_state[
                          "momentum_buffer"
                      ] = torch.clone(d_p).detach()
                  else:
                      buf = param_state["momentum_buffer"]
                      buf.mul_(momentum).add_(d_p, alpha=1 - dampening)
                  if nesterov:
                      d_p = d_p.add(buf, alpha=momentum)
                  else:
                      d_p = buf
  
              p.add_(d_p, alpha=-group["lr"])
  
      return loss
  
file_path: lightly/lightly/utils/lars.py
imports: from typing import Any, Callable, Dict, Optional, Union
  import torch
  from torch import Tensor
  from torch.optim.optimizer import Optimizer, required
label_desc: Use image node_id 3 for calling the LARS obj's underlying member method code with example usage: obj.step(closure) and returns: loss