class_docstr: CP tensor regression
  
  Learns a low rank CP tensor weight, This performs a partial least square regression to a tensor X (>= 2 modes)
  against a matrix Y. The first modes of X and Y will be considered coupled, and the decomposition will maximize
  the covariance between them.
  
  Parameters
  ----------
  n_components : int
      rank of the CP decomposition of the regression weights
  tol : float
      convergence value
  n_iter_max : int, optional, default is 100
      maximum number of iteration
  random_state : None, int or RandomState, optional, default is None
  verbose : bool, default is False
      whether to be verbose during fitting
  
  References
  ----------
  .. [1] Rasmus Bro, "Multiway calibration. Multilinear PLS", Chemometrics, 1996
class_docstr_tok: ["CP","tensor","regression","Learns","a","low","rank","CP","tensor","weight",",","This","performs","a","partial","least","square","regression","to","a","tensor","X","(",">","=","2","modes",")","against","a","matrix","Y",".","The","first","modes","of","X","and","Y","will","be","considered","coupled",",","and","the","decomposition","will","maximize","the","covariance","between","them",".","Parameters","--","--","--","--","--","n_components",":","int","rank","of","the","CP","decomposition","of","the","regression","weights","tol",":","float","convergence","value","n_iter_max",":","int",",","optional",",","default","is","100","maximum","number","of","iteration","random_state",":","None",",","int","or","RandomState",",","optional",",","default","is","None","verbose",":","bool",",","default","is","False","whether","to","be","verbose","during","fitting","References","--","--","--","--","--","..","[","1","]","Rasmus","Bro",",","``","Multiway","calibration",".","Multilinear","PLS","''",",","Chemometrics",",","1996"]
docstr: Fits the model to the data (X, Y)
  
  Parameters
  ----------
  X : ndarray
      tensor data of shape (n_samples, N1, ..., NS)
  Y : 2D-array of shape (n_samples, n_predictions)
      labels associated with each sample
  
  Attributes
  ----------
  X_factors : list of ndarray of shape (X.shape[i], n_components)
      The factors of X tensor to approximate X. The first component, X_factors[0],
      directs to the maximal covariance with Y_factors[0]
  Y_factors : list of ndarray of shape (Y.shape[i], n_components)
      The factors of Y matrix to approximate Y. The first component, Y_factors[0],
      directs to the maximal covariance with X_factors[0]
  coef_ : ndarray of shape (n_component, n_component)
      The coefficients of the linear model such that `Y_factors[0]` is approximated as
      `Y_factors[0] = X_factors[0] @ coef_`.
  
  Returns
  -------
  self
docstr_tok: ["Fits","the","model","to","the","data","(","X",",","Y",")","Parameters","--","--","--","--","--","X",":","ndarray","tensor","data","of","shape","(","n_samples",",","N1",",","...",",","NS",")","Y",":","2D-array","of","shape","(","n_samples",",","n_predictions",")","labels","associated","with","each","sample","Attributes","--","--","--","--","--","X_factors",":","list","of","ndarray","of","shape","(","X.shape","[","i","]",",","n_components",")","The","factors","of","X","tensor","to","approximate","X",".","The","first","component",",","X_factors","[","0","]",",","directs","to","the","maximal","covariance","with","Y_factors","[","0","]","Y_factors",":","list","of","ndarray","of","shape","(","Y.shape","[","i","]",",","n_components",")","The","factors","of","Y","matrix","to","approximate","Y",".","The","first","component",",","Y_factors","[","0","]",",","directs","to","the","maximal","covariance","with","X_factors","[","0","]","coef_",":","ndarray","of","shape","(","n_component",",","n_component",")","The","coefficients","of","the","linear","model","such","that","`","Y_factors","[","0","]","`","is","approximated","as","`","Y_factors","[","0","]","=","X_factors","[","0","]","@","coef_","`",".","Returns","--","--","--","-","self"]
code: def fit(self, X, Y):
      """Fits the model to the data (X, Y)
  
      Parameters
      ----------
      X : ndarray
          tensor data of shape (n_samples, N1, ..., NS)
      Y : 2D-array of shape (n_samples, n_predictions)
          labels associated with each sample
  
      Attributes
      ----------
      X_factors : list of ndarray of shape (X.shape[i], n_components)
          The factors of X tensor to approximate X. The first component, X_factors[0],
          directs to the maximal covariance with Y_factors[0]
      Y_factors : list of ndarray of shape (Y.shape[i], n_components)
          The factors of Y matrix to approximate Y. The first component, Y_factors[0],
          directs to the maximal covariance with X_factors[0]
      coef_ : ndarray of shape (n_component, n_component)
          The coefficients of the linear model such that `Y_factors[0]` is approximated as
          `Y_factors[0] = X_factors[0] @ coef_`.
  
      Returns
      -------
      self
      """
      ## PREPROCESSING
      # Check that both tensors are coupled along the first mode
      if T.shape(X)[0] != T.shape(Y)[0]:
          raise ValueError(
              "The first modes of X and Y must be coupled and have the same length."
          )
      X, Y = T.copy(X), T.copy(Y)
  
      # Check the shape of X and Y; convert vector Y to a matrix
      if T.ndim(X) < 2:
          raise ValueError("X must be at least a 2-mode tensor.")
      if (T.ndim(Y) != 1) and (T.ndim(Y) != 2):
          raise ValueError(
              "Only a matrix (2-mode tensor) Y is allowed."
          )
      if T.ndim(Y) == 1:
          Y = T.reshape(Y, (-1, 1))
  
      # Mean center the data, record info the object
      self.X_shape_ = T.shape(X)
      self.Y_shape_ = T.shape(Y)
  
      self.X_mean_ = T.mean(X, axis=0)
      self.Y_mean_ = T.mean(Y, axis=0)
      X -= self.X_mean_
      Y -= self.Y_mean_
  
      self.X_factors = [
          T.zeros((l, self.n_components), **T.context(X))
          for l in T.shape(X)
      ]
      self.Y_factors = [
          T.zeros((l, self.n_components), **T.context(X))
          for l in T.shape(Y)
      ]
      self.X_r2 = T.zeros((self.n_components,), **T.context(X))
      self.Y_r2 = T.zeros((self.n_components,), **T.context(Y))
  
      # Coefficients of the linear model
      self.coef_ = T.zeros(
          (self.n_components, self.n_components), **T.context(X)
      )
  
      ## FITTING EACH COMPONENT
      for component in range(self.n_components):
          comp_X_factors = [ff[:, component] for ff in self.X_factors]
          comp_Y_factors_0 = Y[:, 0]
          old_comp_Y_factors_0 = (
              T.ones(T.shape(comp_Y_factors_0)) * T.inf
          )
  
          for iter in range(self.n_iter_max):
              Z = T.tensordot(X, comp_Y_factors_0, axes=((0,), (0,)))
  
              if iter == 0:
                  Z_comp = initialize_cp(
                      Z, 1, normalize_factors=True
                  ).factors
                  Z_comp = [T.reshape(zz, (-1,)) for zz in Z_comp]
  
              if T.ndim(Z) >= 2:
                  for mode in range(len(Z_comp)):
                      factor = multi_mode_dot(Z, Z_comp, skip=mode)
                      factor = factor / T.norm(factor, 2)
                      Z_comp[mode] = factor
              else:
                  Z_comp = [Z / T.norm(Z)]
  
              for mode in range(
                  1, X.ndim
              ):  # Mode 0 of Z collapsed by above tensordot
                  comp_X_factors[mode] = tensor_to_vec(Z_comp[mode - 1])
  
              comp_X_factors[0] = multi_mode_dot(
                  X, comp_X_factors[1:], range(1, T.ndim(X))
              )
              comp_Y_factors_1 = T.dot(
                  T.transpose(Y), comp_X_factors[0]
              )
              comp_Y_factors_1 /= T.norm(comp_Y_factors_1)
              comp_Y_factors_0 = T.dot(Y, comp_Y_factors_1)
  
              if (
                  T.norm(old_comp_Y_factors_0 - comp_Y_factors_0)
                  < self.tol
              ):
                  if self.verbose:
                      print(
                          f"Component {component}: converged after {iter} iterations"
                      )
                  break
              old_comp_Y_factors_0 = T.copy(comp_Y_factors_0)
  
          # Put iteration results back to the parameter variables
          for ii in range(len(comp_X_factors)):
              self.X_factors[ii] = T.index_update(
                  self.X_factors[ii],
                  T.index[:, component],
                  comp_X_factors[ii],
              )
          self.Y_factors[0] = T.index_update(
              self.Y_factors[0], T.index[:, component], comp_Y_factors_0
          )
          self.Y_factors[1] = T.index_update(
              self.Y_factors[1], T.index[:, component], comp_Y_factors_1
          )
  
          B = T.lstsq(
              self.X_factors[0], T.reshape(comp_Y_factors_0, (-1, 1))
          )[0]
          self.coef_ = T.index_update(
              self.coef_,
              T.index[:, component],
              T.reshape(B, (-1,)),
          )
  
          # Deflation
          X -= outer(comp_X_factors)
          Y -= T.dot(
              T.dot(
                  self.X_factors[0],
                  T.reshape(B, (-1, 1)),
              ),
              T.reshape(comp_Y_factors_1, (1, -1)),
          )  # Y -= T b q' = T pinv(T) u q'
  
      return self
  
file_path: tensorly/tensorly/regression/cp_plsr.py
imports: from ..tenalg import multi_mode_dot, outer
  from ..None import backend
  from ..None import tensor_to_vec
  from ..decomposition._cp import initialize_cp
label_desc: Use image node_id 4 for calling the CP_PLSR obj's underlying member method code with example usage: obj.fit(X, Y) and returns: self