class_docstr: Tucker tensor regression
  
      Learns a low rank Tucker weight for the regression
  
  Parameters
  ----------
  weight_ranks : int list
      dimension of each mode of the core Tucker weight
  tol : float
      convergence value
  reg_W : int, optional, default is 1
      regularisation on the weights
  n_iter_max : int, optional, default is 100
      maximum number of iteration
  random_state : None, int or RandomState, optional, default is None
  verbose : int, default is 1
      level of verbosity
docstr: Fits the model to the data (X, y)
  
  Parameters
  ----------
  X : ndarray of shape (n_samples, N1, ..., NS)
      tensor data
  y : array of shape (n_samples)
      labels associated with each sample
  
  Returns
  -------
  self
code: def fit(self, X, y):
      """Fits the model to the data (X, y)
  
      Parameters
      ----------
      X : ndarray of shape (n_samples, N1, ..., NS)
          tensor data
      y : array of shape (n_samples)
          labels associated with each sample
  
      Returns
      -------
      self
      """
      rng = T.check_random_state(self.random_state)
  
      # Initialise randomly the weights
      G = T.tensor(rng.randn(*self.weight_ranks), **T.context(X))
      W = []
      for i in range(
          1, T.ndim(X)
      ):  # First dimension of X = number of samples
          W.append(
              T.tensor(
                  rng.randn(X.shape[i], G.shape[i - 1]), **T.context(X)
              )
          )
  
      # Norm of the weight tensor at each iteration
      norm_W = []
  
      for iteration in range(self.n_iter_max):
          # Optimise modes of W
          for i in range(len(W)):
              phi = partial_tensor_to_vec(
                  T.dot(
                      partial_unfold(X, i),
                      T.dot(
                          kronecker(W, skip_matrix=i),
                          T.transpose(unfold(G, i)),
                      ),
                  )
              )
              # Regress phi on y: we could call a package here, e.g. scikit-learn
              inv_term = T.dot(
                  T.transpose(phi), phi
              ) + self.reg_W * T.tensor(
                  np.eye(phi.shape[1]), **T.context(X)
              )
              W_i = vec_to_tensor(
                  T.solve(inv_term, T.dot(T.transpose(phi), y)),
                  (X.shape[i + 1], G.shape[i]),
              )
              W[i] = W_i
  
          phi = T.dot(partial_tensor_to_vec(X), kronecker(W))
          G = vec_to_tensor(
              T.solve(
                  T.dot(T.transpose(phi), phi)
                  + self.reg_W
                  * T.tensor(np.eye(phi.shape[1]), **T.context(X)),
                  T.dot(T.transpose(phi), y),
              ),
              G.shape,
          )
  
          weight_tensor_ = tucker_to_tensor((G, W))
          norm_W.append(T.norm(weight_tensor_, 2))
  
          # Convergence check
          if iteration > 1:
              weight_evolution = (
                  abs(norm_W[-1] - norm_W[-2]) / norm_W[-1]
              )
  
              if weight_evolution <= self.tol:
                  if self.verbose:
                      print(f"\nConverged in {iteration} iterations")
                  break
  
      self.weight_tensor_ = weight_tensor_
      self.tucker_weight_ = (G, W)
      self.vec_W_ = tucker_to_vec((G, W))
      self.n_iterations_ = iteration + 1
      self.norm_W_ = norm_W
  
      return self
  
file_path: tensorly/tensorly/regression/tucker_regression.py
imports: import numpy
  from ..base import unfold, vec_to_tensor
  from ..base import partial_tensor_to_vec, partial_unfold
  from ..tenalg import kronecker
  from ..tucker_tensor import tucker_to_tensor, tucker_to_vec
  from ..None import backend
label_desc: Use image node_id 4 for calling the TuckerRegressor obj's underlying member method code with example usage: obj.fit(X, y) and returns: self