class_docstr: Diffusion gradient information
  
  Parameters
  ----------
  gradients : array_like (N, 3)
      Diffusion gradients. The direction of each of these vectors corresponds
      to the b-vector, and the length corresponds to the b-value.
  b0_threshold : float
      Gradients with b-value less than or equal to `b0_threshold` are
      considered as b0s i.e. without diffusion weighting.
  
  Attributes
  ----------
  gradients : (N,3) ndarray
      diffusion gradients
  bvals : (N,) ndarray
      The b-value, or magnitude, of each gradient direction.
  qvals: (N,) ndarray
      The q-value for each gradient direction. Needs big and small
      delta.
  bvecs : (N,3) ndarray
      The direction, represented as a unit vector, of each gradient.
  b0s_mask : (N,) ndarray
      Boolean array indicating which gradients have no diffusion
      weighting, ie b-value is close to 0.
  b0_threshold : float
      Gradients with b-value less than or equal to `b0_threshold` are
      considered to not have diffusion weighting.
  btens : (N,3,3) ndarray
      The b-tensor of each gradient direction.
  
  See Also
  --------
  gradient_table
  
  Notes
  -----
  The GradientTable object is immutable. Do NOT assign attributes.
  If you have your gradient table in a bval & bvec format, we recommend
  using the factory function gradient_table
class_docstr_tok: ["Diffusion","gradient","information","Parameters","--","--","--","--","--","gradients",":","array_like","(","N",",","3",")","Diffusion","gradients",".","The","direction","of","each","of","these","vectors","corresponds","to","the","b-vector",",","and","the","length","corresponds","to","the","b-value",".","b0_threshold",":","float","Gradients","with","b-value","less","than","or","equal","to","`","b0_threshold","`","are","considered","as","b0s","i.e",".","without","diffusion","weighting",".","Attributes","--","--","--","--","--","gradients",":","(","N,3",")","ndarray","diffusion","gradients","bvals",":","(","N",",",")","ndarray","The","b-value",",","or","magnitude",",","of","each","gradient","direction",".","qvals",":","(","N",",",")","ndarray","The","q-value","for","each","gradient","direction",".","Needs","big","and","small","delta",".","bvecs",":","(","N,3",")","ndarray","The","direction",",","represented","as","a","unit","vector",",","of","each","gradient",".","b0s_mask",":","(","N",",",")","ndarray","Boolean","array","indicating","which","gradients","have","no","diffusion","weighting",",","ie","b-value","is","close","to","0.","b0_threshold",":","float","Gradients","with","b-value","less","than","or","equal","to","`","b0_threshold","`","are","considered","to","not","have","diffusion","weighting",".","btens",":","(","N,3,3",")","ndarray","The","b-tensor","of","each","gradient","direction",".","See","Also","--","--","--","--","gradient_table","Notes","--","--","-","The","GradientTable","object","is","immutable",".","Do","NOT","assign","attributes",".","If","you","have","your","gradient","table","in","a","bval","&","bvec","format",",","we","recommend","using","the","factory","function","gradient_table"]
docstr: Constructor for GradientTable class
code: def __init__(
      self,
      gradients,
      big_delta=None,
      small_delta=None,
      b0_threshold=50,
      btens=None,
  ):
      """Constructor for GradientTable class"""
      gradients = np.asarray(gradients)
      if gradients.ndim != 2 or gradients.shape[1] != 3:
          raise ValueError("gradients should be an (N, 3) array")
      self.gradients = gradients
      # Avoid nan gradients. Set these to 0 instead:
      self.gradients = np.where(np.isnan(gradients), 0.0, gradients)
      self.big_delta = big_delta
      self.small_delta = small_delta
      self.b0_threshold = b0_threshold
      if btens is not None:
          linear_tensor = np.array([[1, 0, 0], [0, 0, 0], [0, 0, 0]])
          planar_tensor = (
              np.array([[0, 0, 0], [0, 1, 0], [0, 0, 1]]) / 2
          )
          spherical_tensor = (
              np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]]) / 3
          )
          cigar_tensor = (
              np.array([[2, 0, 0], [0, 0.5, 0], [0, 0, 0.5]]) / 3
          )
          if isinstance(btens, str):
              b_tensors = np.zeros((len(self.bvals), 3, 3))
              if btens == "LTE":
                  b_tensor = linear_tensor
              elif btens == "PTE":
                  b_tensor = planar_tensor
              elif btens == "STE":
                  b_tensor = spherical_tensor
              elif btens == "CTE":
                  b_tensor = cigar_tensor
              else:
                  raise ValueError(
                      "%s is an invalid value for btens. " % btens
                      + "Please provide one of the following: "
                      + "'LTE', 'PTE', 'STE', 'CTE'."
                  )
              for i, (bvec, bval) in enumerate(
                  zip(self.bvecs, self.bvals)
              ):
                  if btens == "STE":
                      b_tensors[i] = b_tensor * bval
                  else:
                      R = vec2vec_rotmat(np.array([1, 0, 0]), bvec)
                      b_tensors[i] = (
                          np.matmul(np.matmul(R, b_tensor), R.T) * bval
                      )
              self.btens = b_tensors
          elif isinstance(btens, np.ndarray) and btens.shape in (
              (gradients.shape[0],),
              (gradients.shape[0], 1),
              (1, gradients.shape[0]),
          ):
              b_tensors = np.zeros((len(self.bvals), 3, 3))
              if btens.shape == (1, gradients.shape[0]):
                  btens = btens.reshape((gradients.shape[0], 1))
              for i, (bvec, bval) in enumerate(
                  zip(self.bvecs, self.bvals)
              ):
                  R = vec2vec_rotmat(np.array([1, 0, 0]), bvec)
                  if btens[i] == "LTE":
                      b_tensors[i] = (
                          np.matmul(np.matmul(R, linear_tensor), R.T)
                          * bval
                      )
                  elif btens[i] == "PTE":
                      b_tensors[i] = (
                          np.matmul(np.matmul(R, planar_tensor), R.T)
                          * bval
                      )
                  elif btens[i] == "STE":
                      b_tensors[i] = spherical_tensor * bval
                  elif btens[i] == "CTE":
                      b_tensors[i] = (
                          np.matmul(np.matmul(R, cigar_tensor), R.T)
                          * bval
                      )
                  else:
                      raise ValueError(
                          "%s is an invalid value in btens. " % btens[i]
                          + "Array element options: 'LTE', 'PTE', 'STE', "
                          + "'CTE'."
                      )
              self.btens = b_tensors
          elif isinstance(btens, np.ndarray) and btens.shape == (
              gradients.shape[0],
              3,
              3,
          ):
              self.btens = btens
          else:
              raise ValueError(
                  "%s is an invalid value for btens. " % btens
                  + "Please provide a string, an array of "
                  + "strings, or an array of exact b-tensors. "
                  + "String options: 'LTE', 'PTE', 'STE', 'CTE'"
              )
      else:
          self.btens = None
  
imports: import logging
  from warnings import warn
  import numpy
  from scipy.linalg import inv, polar
  from dipy.io import gradients
  from dipy.core.onetime import auto_attr
  from dipy.core.geometry import vector_norm, vec2vec_rotmat
  from dipy.core.sphere import disperse_charges, HemiSphere
  from dipy.utils.deprecator import deprecate_with_version
label_desc: Use image node_id 1 to create a new GradientTable object  with example: obj = GradientTable(gradients, big_delta, small_delta, b0_threshold, btens)