code: def fit(self, X, y=None):
          
          N = X.shape[0]
  
          if y is None:
              y = np.zeros(N)
  
          self.classes = list(set(y))
          self.classes.sort()
          self.n_classes = len(self.classes)
  
          # If no kernel parameters specified, try to choose some defaults
          if not self.sigma:
              self.sigma = median_kneighbour_distance(X)
              self.gamma = self.sigma**-2
  
          if not self.gamma:
              self.gamma = self.sigma**-2
  
          if not self.rho:
              self.rho = 0.1
  
          # choose kernel basis centres
          if self.kernel_pos is None:
              B = min(self.n_kernels_max, N)
              kernel_idx = np.random.permutation(N)
              self.kernel_pos = X[kernel_idx[:B]]
          else:
              B = self.kernel_pos.shape[0]
  
          # fit coefficients
          Phi = metrics.pairwise.rbf_kernel(X, self.kernel_pos, self.gamma)
          theta = {}
          Phi_PhiT = np.dot(Phi.T, Phi)
          inverse_term = np.linalg.inv(Phi_PhiT + self.rho*np.eye(B))
          for c in self.classes:
              m = (y == c).astype(int)
              theta[c] = np.dot(inverse_term, np.dot(Phi.T, m))
  
          self.theta = theta
docs: Fit the inlier model given training data.
  
          This function attempts to choose reasonable defaults for parameters
          sigma and rho if none are specified, which could then be adjusted
          to improve performance.
  
          Parameters
          ----------
          X : array
              Examples of inlier data, of dimension N times d (rows are
              examples, columns are data dimensions)
          y : array, optional
              If the inliers have multiple classes, then y contains the class
              assignments as a vector of length N. If this is specified then
              the model will attempt to assign test data to one of the inlier
              classes or to the outlier class.