class_docstr: History methods specific to PyTorch
docstr: Add distribution statistics on a tensor's elements to the current History entry
docstr_tok: ["Add","distribution","statistics","on","a","tensor","'s","elements","to","the","current","History","entry"]
code: def log_tensor_stats(self, tensor, name):
      """Add distribution statistics on a tensor's elements to the current History entry"""
      # TODO Handle the case of duplicate names.
      if isinstance(tensor, (tuple, list)):
          while isinstance(tensor, (tuple, list)) and isinstance(
              tensor[0], (tuple, list)
          ):
              tensor = [item for sublist in tensor for item in sublist]
          tensor = torch.cat(
              [t.detach().clone().reshape(-1) for t in tensor]
          )
  
      tensor = tensor.detach().clone()
      # checking for inheritance from _TensorBase didn't work for some reason
      if not hasattr(tensor, "shape"):
          cls = type(tensor)
          raise TypeError(
              f"Expected Tensor, not {cls.__module__}.{cls.__name__}"
          )
  
      # Sparse tensors have a bunch of implicit zeros. In order to histo them correctly,
      # we have to count them up and add them to the histo ourselves.
      sparse_zeros = None
      if tensor.is_sparse:
          # Have to call this on a sparse tensor before most other ops.
          tensor = tensor.cpu().coalesce()
  
          backing_values = tensor._values()
          sparse_zeros = tensor.numel() - backing_values.numel()
          tensor = backing_values
  
      flat = tensor.reshape(-1)
  
      if flat.is_cuda:
          if self._is_cuda_histc_supported is None:
              try:
                  flat.histc(bins=self._num_bins)
              except RuntimeError:
                  self._is_cuda_histc_supported = False
              else:
                  self._is_cuda_histc_supported = True
  
          # As of torch 1.0.1.post2+nightly, float16 cuda summary ops are not supported (convert to float32)
          if not self._is_cuda_histc_supported:
              flat = flat.cpu()
          elif not isinstance(
              flat, (torch.cuda.FloatTensor, torch.cuda.DoubleTensor)
          ):
              flat = flat.type(torch.cuda.FloatTensor)
  
      # Since we use histc, we need to make sure that torch supports the operation on CPU,
      # otherwise we'll get a runtime error. Hence, we need to upcast to float32.
      if not flat.is_cuda and not isinstance(
          flat, (torch.FloatTensor, torch.DoubleTensor)
      ):
          flat = flat.type(torch.FloatTensor)
  
      # Skip logging if all values are nan or inf or the tensor is empty.
      if self._no_finite_values(flat):
          return
  
      # Remove nans and infs if present. There's no good way to represent that in histograms.
      flat = self._remove_infs_nans(flat)
  
      tmin = flat.min().item()
      tmax = flat.max().item()
      if sparse_zeros:
          # If we've got zeros to add in, make sure zero is in the hist range.
          tmin = 0 if tmin > 0 else tmin
          tmax = 0 if tmax < 0 else tmax
      # Anecdotally, this can somehow happen sometimes. Maybe a precision error
      # in min()/max() above. Swap here to prevent a runtime error.
      # If all values are equal, just return a single bin.
      if tmin > tmax:
          tmin, tmax = tmax, tmin
      if tmin == tmax:
          tensor = torch.Tensor([flat.numel()])
          tensor = tensor.cpu().clone().detach()
          bins = torch.Tensor([tmin, tmax])
      else:
          tensor = flat.histc(bins=self._num_bins, min=tmin, max=tmax)
          tensor = tensor.cpu().detach().clone()
          bins = torch.linspace(tmin, tmax, steps=self._num_bins + 1)
  
      # Add back zeroes from a sparse tensor.
      if sparse_zeros:
          bins_np = bins.numpy()
          tensor_np = tensor.numpy()
          bin_idx = 0
          num_buckets = len(bins_np) - 1
          for i in range(num_buckets):
              start = bins_np[i]
              end = bins_np[i + 1]
              # There are 3 cases to consider here, all of which mean we've found the right bucket
              # 1. The bucket range contains zero.
              # 2. The bucket range lower bound *is* zero.
              # 3. This is the last bucket and the bucket range upper bound is zero.
              if (start <= 0 and end > 0) or (
                  i == num_buckets - 1 and end == 0
              ):
                  bin_idx = i
                  break
  
          tensor_np[bin_idx] += sparse_zeros
          tensor = torch.Tensor(tensor_np)
          bins = torch.Tensor(bins_np)
  
      wandb.run._log(
          {
              name: wandb.Histogram(
                  np_histogram=(tensor.tolist(), bins.tolist())
              )
          },
          commit=False,
      )
  
imports: import itertools
  from functools import reduce
  from operator import mul
  from typing import List
  import wandb
  from wandb import util
  from wandb.data_types import Node
label_desc: Use image node_id 4 for calling the TorchHistory obj's underlying member method code with example usage: obj.log_tensor_stats(tensor, name) without return types