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.

def fit(self, X, y=None):
        """
        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.
        """
        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