def __call__(self,X,Y=None,eval_gradient=False): """ Return the kernel k(X,Y) and optionally its gradient. """ X = np.atleast_2d(X) a = _check_length_scale(X,self.a) if Y is None: dists = pdist(X/a,metric = 'sqeuclidean') K = np.exp(-0.5*dists) K = squareform(K) np.fill_diagonal(K,1) else: if eval_gradient: raise ValueError("Gradient can only be evaluated when Y is None") dists = cdist(X/a,Y/a,metric='sqeuclidean') K = np.exp(-0.5*dists) if eval_gradient: if self.hyperparameter_length_scale.fixed: return K,np.empty((X.shape[0],X.shape[0],0)) elif not self.anisotropic or a.shape[0] == 1: K_gradient = (K * squareform(dists))[:,:,np.newaxis] return K,K_gradient elif self.anisotropic: K_gradient = (X[:,np.newaxis,:] - X[np.newaxis,:,:])**2/(a**2) K-gradient *= K[...,np.newaxis] return K,K_gradient else: return K
def __call__(self, XX1, XX2=None, eval_gradient=False): """Return the kernel k(XX1, XX2) and optionally its gradient. Parameters ---------- XX1 : array, shape (n_samples_XX1, n_features) Left argument of the returned kernel k(XX1, XX2) XX2 : array, shape (n_samples_XX2, n_features), (optional, default=None) Right argument of the returned kernel k(XX1, XX2). If None, k(XX1, XX1) is evaluated instead. eval_gradient : bool (optional, default=False) Determines whether the gradient with respect to the kernel hyperparameter is determined. Only supported when XX2 is None. Returns ------- K : array, shape (n_samples_XX1, n_samples_XX2) Kernel k(XX1, XX2) K_gradient : array (opt.), shape (n_samples_XX1, n_samples_XX1, n_dims) The gradient of the kernel k(XX1, XX1) with respect to the hyperparameter of the kernel. Only returned when eval_gradient is True. """ XX1 = np.atleast_2d(XX1) length_scale = _check_length_scale(XX1, self.length_scale) if XX2 is None: K = full_kernel(XX1, length_scale, self.n_XX_func, return_code=self.return_code) else: if eval_gradient: raise ValueError( "Gradient can only be evaluated when XX2 is None.") K = full_kernel(XX1, length_scale, self.n_XX_func, XX2, self.return_code) print(K.shape, 'KK') if not eval_gradient: return K if self.hyperparameter_length_scale.fixed: # Hyperparameter l kept fixed length_scale_gradient = np.empty((K.shape[0], K.shape[1], 0)) else: # approximate gradient numerically def f(gamma): # helper function return full_kernel(XX1, gamma, self.n_XX_func, return_code=self.return_code) length_scale = np.atleast_1d(length_scale) length_scale_gradient = _approx_fprime(length_scale, f, 1e-8) return K, length_scale_gradient
def __call__(self, X, Y=None, eval_gradient=False): """Return the kernel k(X, Y) and optionally its gradient. Parameters ---------- X : array, shape (n_samples_X, n_features) Left argument of the returned kernel k(X, Y) Y : array, shape (n_samples_Y, n_features), (optional, default=None) Right argument of the returned kernel k(X, Y). If None, k(X, X) if evaluated instead. eval_gradient : bool (optional, default=False) Determines whether the gradient with respect to the kernel hyperparameter is determined. Only supported when Y is None. Returns ------- K : array, shape (n_samples_X, n_samples_Y) Kernel k(X, Y) K_gradient : array (opt.), shape (n_samples_X, n_samples_X, n_dims) The gradient of the kernel k(X, X) with respect to the hyperparameter of the kernel. Only returned when eval_gradient is True. """ X = np.atleast_2d(X) length_scale = _check_length_scale(X, self.length_scale) if Y is None: dists = pdist(X / length_scale, metric=MMetric) K = np.exp(-.5 * dists) # convert from upper-triangular matrix to square matrix K = squareform(K) np.fill_diagonal(K, 1) else: if eval_gradient: raise ValueError( "Gradient can only be evaluated when Y is None.") dists = cdist(X / length_scale, Y / length_scale, metric=MMetric) K = np.exp(-.5 * dists) if eval_gradient: if self.hyperparameter_length_scale.fixed: # Hyperparameter l kept fixed return K, np.empty((X.shape[0], X.shape[0], 0)) elif not self.anisotropic or length_scale.shape[0] == 1: K_gradient = \ (K * squareform(dists))[:, :, np.newaxis] return K, K_gradient elif self.anisotropic: # We need to recompute the pairwise dimension-wise distances K_gradient = (X[:, np.newaxis, :] - X[np.newaxis, :, :]) ** 2 \ / (length_scale ** 2) K_gradient *= K[..., np.newaxis] return K, K_gradient else: return K
def __call__(self, X, Y=None, eval_gradient=False): X = np.atleast_2d(X) qq=self.indx xbest=X[qq,] length_scale = _check_length_scale(X, self.length_scale) if Y is None: K=np.zeros((X.shape[0],X.shape[0])) for i in range(X.shape[0]): for j in range(X.shape[0]): dists1 = cdist([X[i,]]/length_scale,[xbest]/length_scale, metric='sqeuclidean')[0,0] dists2 = cdist([X[j,]]/length_scale,[xbest]/length_scale, metric='sqeuclidean')[0,0] dists3 = np.abs(dists1-dists2) K[i,j]=np.exp(-.5 * dists1*dists2)*np.exp(-.5 * dists3) else: if eval_gradient: raise ValueError( "Gradient can only be evaluated when Y is None.") K=np.zeros((X.shape[0],Y.shape[0])) for i in range(X.shape[0]): for j in range(Y.shape[0]): abs1=np.abs(np.array([X[i,]])-np.array([xbest])) abs2=np.abs(np.array([Y[j,]])-np.array([xbest])) dists = cdist(abs1/length_scale,abs2/length_scale,metric='sqeuclidean')[0,0] # dists1 = cdist([X[i,]]/length_scale,[xbest]/length_scale, metric='minkowski',p=1)[0,0] # dists2 = cdist([Y[j,]]/length_scale,[xbest]/length_scale, metric='minkowski',p=1)[0,0] # dists3 = np.abs(dists1-dists2) K[i,j]=np.exp(-.5 * dists) # dists = cdist(X / length_scale, Y / length_scale, # metric='sqeuclidean') # K = np.exp(-.5 * dists) # print(K.shape) if eval_gradient: if self.hyperparameter_length_scale.fixed: # Hyperparameter l kept fixed return K, np.empty((X.shape[0], X.shape[0], 0)) elif not self.anisotropic or length_scale.shape[0] == 1: K_gradient = \ (K * squareform(dists))[:, :, np.newaxis] return K, K_gradient elif self.anisotropic: # We need to recompute the pairwise dimension-wise distances K_gradient = (X[:, np.newaxis, :] - X[np.newaxis, :, :]) ** 2 \ / (length_scale ** 2) K_gradient *= K[..., np.newaxis] return K, K_gradient else: return K
def __call__(self, X, Y=None, eval_gradient=False): """Return the kernel k(X, Y) and optionally its gradient. Parameters ---------- X : array, shape (n_samples_X, n_features) Left argument of the returned kernel k(X, Y) Y : array, shape (n_samples_Y, n_features), (optional, default=None) Right argument of the returned kernel k(X, Y). If None, k(X, X) if evaluated instead. eval_gradient : bool (optional, default=False) Determines whether the gradient with respect to the kernel hyperparameter is determined. Only supported when Y is None. Returns ------- K : array, shape (n_samples_X, n_samples_Y) Kernel k(X, Y) K_gradient : array (opt.), shape (n_samples_X, n_samples_X, n_dims) The gradient of the kernel k(X, X) with respect to the hyperparameter of the kernel. Only returned when eval_gradient is True. """ X = np.atleast_2d(X) self.c = _check_length_scale(X, self.c) if self.c is 0: Xsub = X else: Xsub = X - self.c if Y is None: K = np.inner(Xsub, Xsub) else: if eval_gradient: raise ValueError( "Gradient can only be evaluated when Y is None.") K = np.inner(Xsub, Y - self.c) if eval_gradient: if self.hyperparameter_c.fixed: c_gradient = np.empty((X.shape[0], X.shape[0], 0)) else: if not self.non_uniform_offset: gradient_mat = np.inner(np.ones(X.shape), Xsub) c_gradient = -self.c * (gradient_mat + gradient_mat.T) c_gradient = c_gradient[:, :, np.newaxis] else: c_gradient = [] for i, c in enumerate(self.c): gradient_mat = np.vstack([Xsub[:, i]] * X.shape[0]) c_gradient.append(c * (gradient_mat + gradient_mat.T)) c_gradient = -np.array(c_gradient) c_gradient = np.rollaxis(c_gradient, 0, 3) return K, c_gradient else: return K
def __call__(self, X, Y=None, eval_gradient=False): mols = data['Smiles'].apply(Chem.MolFromSmiles) X = mols.apply(torsionFingerprint) X = np.array(list(X)) st = StandardScaler() X = st.fit_transform(X) X = np.atleast_2d(X) length_scale = _check_length_scale(X, self.length_scale) if Y is None: dists = pdist(X / length_scale, metric='jaccard') K = np.exp(-.5 * dists) # convert from upper-triangular matrix to square matrix K = squareform(K) np.fill_diagonal(K, 1) else: if eval_gradient: raise ValueError( "Gradient can only be evaluated when Y is None.") dists = cdist(X / length_scale, Y / length_scale, metric='sqeuclidean') K = np.exp(-.5 * dists) if eval_gradient: if self.hyperparameter_length_scale.fixed: # Hyperparameter l kept fixed return K, np.empty((X.shape[0], X.shape[0], 0)) elif not self.anisotropic or length_scale.shape[0] == 1: K_gradient = \ (K * squareform(dists))[:, :, np.newaxis] return K, K_gradient elif self.anisotropic: # We need to recompute the pairwise dimension-wise distances K_gradient = (X[:, np.newaxis, :] - X[np.newaxis, :, :]) ** 2 \ / (length_scale ** 2) K_gradient *= K[..., np.newaxis] return K, K_gradient else: return K
def __call__(self, X, Y=None, eval_gradient=False): if eval_gradient and Y is not None: X = np.atleast_2d(X) length_scale = _check_length_scale(X, self.length_scale) dists = cdist(X / length_scale, Y / length_scale, metric='sqeuclidean') K = np.exp(-.5 * dists) if self.hyperparameter_length_scale.fixed: return K, np.empty((X.shape[0], X.shape[0], 0)) elif not self.anisotropic or length_scale.shape[0] == 1: K_gradient = (K * dists)[:, :, np.newaxis] return K, K_gradient elif self.anisotropic: K_gradient = (X[:, np.newaxis, :] - Y[np.newaxis, :, :]) ** 2 \ / (length_scale ** 2) K_gradient *= K[..., np.newaxis] return K, K_gradient else: return super().__call__(X, Y, eval_gradient)
def _call( self, X: np.ndarray, Y: Optional[np.ndarray] = None, eval_gradient: bool = False, active: Optional[np.ndarray] = None, ) -> Union[np.ndarray, Tuple[np.ndarray, np.ndarray]]: """Return the kernel k(X, Y) and optionally its gradient. Parameters ---------- X : [array-like, shape=(n_samples_X, n_features)] Left argument of the returned kernel k(X, Y) Y : [array-like, shape=(n_samples_Y, n_features) or None(default)] Right argument of the returned kernel k(X, Y). If None, k(X, X) if evaluated instead. eval_gradient : [bool, False(default)] Determines whether the gradient with respect to the kernel hyperparameter is determined. Only supported when Y is None. active : np.ndarray (n_samples_X, n_features) (optional) Boolean array specifying which hyperparameters are active. Returns ------- K : [array-like, shape=(n_samples_X, n_samples_Y)] Kernel k(X, Y) K_gradient : [array-like, shape=(n_samples_X, n_samples_X, n_dims)] The gradient of the kernel k(X, X) with respect to the hyperparameter of the kernel. Only returned when eval_gradient is True. Note ---- Code partially copied from skopt (https://github.com/scikit-optimize). Made small changes to only compute necessary values and use scikit-learn helper functions. """ X = np.atleast_2d(X) length_scale = kernels._check_length_scale(X, self.length_scale) if Y is None: Y = X elif eval_gradient: raise ValueError("gradient can be evaluated only when Y != X") else: Y = np.atleast_2d(Y) indicator = np.expand_dims(X, axis=1) != Y K = (-1 / (2 * length_scale**2) * indicator).sum(axis=2) K = np.exp(K) if active is not None: K = K * active if eval_gradient: # dK / d theta = (dK / dl) * (dl / d theta) # theta = log(l) => dl / d (theta) = e^theta = l # dK / d theta = l * dK / dl # dK / dL computation if np.iterable(length_scale) and length_scale.shape[0] > 1: grad = np.expand_dims(K, axis=-1) * np.array(indicator, dtype=np.float32) else: grad = np.expand_dims(K * np.sum(indicator, axis=2), axis=-1) grad *= 1 / length_scale**3 return K, grad return K
def _call( self, X: np.ndarray, Y: Optional[np.ndarray] = None, eval_gradient: bool = False, active: Optional[np.ndarray] = None, ) -> Union[np.ndarray, Tuple[np.ndarray, np.ndarray]]: """Return the kernel k(X, Y) and optionally its gradient. Parameters ---------- X : array, shape (n_samples_X, n_features) Left argument of the returned kernel k(X, Y) Y : array, shape (n_samples_Y, n_features), (optional, default=None) Right argument of the returned kernel k(X, Y). If None, k(X, X) if evaluated instead. eval_gradient : bool (optional, default=False) Determines whether the gradient with respect to the kernel hyperparameter is determined. Only supported when Y is None. active : np.ndarray (n_samples_X, n_features) (optional) Boolean array specifying which hyperparameters are active. Returns ------- K : array, shape (n_samples_X, n_samples_Y) Kernel k(X, Y) K_gradient : array (opt.), shape (n_samples_X, n_samples_X, n_dims) The gradient of the kernel k(X, X) with respect to the hyperparameter of the kernel. Only returned when eval_gradient is True. """ X = np.atleast_2d(X) length_scale = kernels._check_length_scale(X, self.length_scale) if Y is None: dists = scipy.spatial.distance.pdist(X / length_scale, metric="sqeuclidean") K = np.exp(-0.5 * dists) # convert from upper-triangular matrix to square matrix K = scipy.spatial.distance.squareform(K) np.fill_diagonal(K, 1) else: if eval_gradient: raise ValueError( "Gradient can only be evaluated when Y is None.") dists = scipy.spatial.distance.cdist(X / length_scale, Y / length_scale, metric="sqeuclidean") K = np.exp(-0.5 * dists) if active is not None: K = K * active if eval_gradient: if self.hyperparameter_length_scale.fixed: # Hyperparameter l kept fixed return K, np.empty((X.shape[0], X.shape[0], 0)) elif not self.anisotropic or length_scale.shape[0] == 1: K_gradient = ( K * scipy.spatial.distance.squareform(dists))[:, :, np.newaxis] return K, K_gradient elif self.anisotropic: # We need to recompute the pairwise dimension-wise distances K_gradient = (X[:, np.newaxis, :] - X[np.newaxis, :, :])**2 / (length_scale**2) K_gradient *= K[..., np.newaxis] return K, K_gradient return K
def _call( self, X: np.ndarray, Y: Optional[np.ndarray] = None, eval_gradient: bool = False, active: Optional[np.ndarray] = None, ) -> Union[np.ndarray, Tuple[np.ndarray, np.ndarray]]: """Return the kernel k(X, Y) and optionally its gradient. Parameters ---------- X : array, shape (n_samples_X, n_features) Left argument of the returned kernel k(X, Y) Y : array, shape (n_samples_Y, n_features), (optional, default=None) Right argument of the returned kernel k(X, Y). If None, k(X, X) if evaluated instead. eval_gradient : bool (optional, default=False) Determines whether the gradient with respect to the kernel hyperparameter is determined. Only supported when Y is None. active : np.ndarray (n_samples_X, n_features) (optional) Boolean array specifying which hyperparameters are active. Returns ------- K : array, shape (n_samples_X, n_samples_Y) Kernel k(X, Y) K_gradient : array (opt.), shape (n_samples_X, n_samples_X, n_dims) The gradient of the kernel k(X, X) with respect to the hyperparameter of the kernel. Only returned when eval_gradient is True. """ X = np.atleast_2d(X) length_scale = kernels._check_length_scale(X, self.length_scale) if Y is None: dists = scipy.spatial.distance.pdist(X / length_scale, metric="euclidean") else: if eval_gradient: raise ValueError( "Gradient can only be evaluated when Y is None.") dists = scipy.spatial.distance.cdist(X / length_scale, Y / length_scale, metric="euclidean") if self.nu == 0.5: K = np.exp(-dists) elif self.nu == 1.5: K = dists * math.sqrt(3) K = (1.0 + K) * np.exp(-K) elif self.nu == 2.5: K = dists * math.sqrt(5) K = (1.0 + K + K**2 / 3.0) * np.exp(-K) else: # general case; expensive to evaluate K = dists K[K == 0.0] += np.finfo(float).eps # strict zeros result in nan tmp = math.sqrt(2 * self.nu) * K K.fill((2**(1.0 - self.nu)) / scipy.special.gamma(self.nu)) K *= tmp**self.nu K *= scipy.special.kv(self.nu, tmp) if Y is None: # convert from upper-triangular matrix to square matrix K = scipy.spatial.distance.squareform(K) np.fill_diagonal(K, 1) if active is not None: K = K * active if eval_gradient: if self.hyperparameter_length_scale.fixed: # Hyperparameter l kept fixed K_gradient = np.empty((X.shape[0], X.shape[0], 0)) return K, K_gradient # We need to recompute the pairwise dimension-wise distances if self.anisotropic: D = (X[:, np.newaxis, :] - X[np.newaxis, :, :])**2 / (length_scale**2) else: D = scipy.spatial.distance.squareform(dists**2)[:, :, np.newaxis] if self.nu == 0.5: K_gradient = (K[..., np.newaxis] * D / np.sqrt(D.sum(2))[:, :, np.newaxis]) K_gradient[~np.isfinite(K_gradient)] = 0 elif self.nu == 1.5: K_gradient = 3 * D * np.exp(-np.sqrt(3 * D.sum(-1)))[ ..., np.newaxis] elif self.nu == 2.5: tmp = np.sqrt(5 * D.sum(-1))[..., np.newaxis] K_gradient = 5.0 / 3.0 * D * (tmp + 1) * np.exp(-tmp) else: # original sklearn code would approximate gradient numerically, but this would violate our assumption # that the kernel hyperparameters are not changed within __call__ raise ValueError(self.nu) if not self.anisotropic: return K, K_gradient[:, :].sum(-1)[:, :, np.newaxis] else: return K, K_gradient else: return K
def __call__(self, X, Y=None, eval_gradient=False): """Return the kernel k(X, Y) and optionally its gradient. Parameters ---------- X : array, shape (n_samples_X, n_features) Left argument of the returned kernel k(X, Y) Y : array, shape (n_samples_Y, n_features), (optional, default=None) Right argument of the returned kernel k(X, Y). If None, k(X, X) if evaluated instead. eval_gradient : bool (optional, default=False) Determines whether the gradient with respect to the kernel hyperparameter is determined. Only supported when Y is None. Returns ------- K : array, shape (n_samples_X, n_samples_Y) Kernel k(X, Y) K_gradient : array (opt.), shape (n_samples_X, n_samples_X, n_dims) The gradient of the kernel k(X, X) with respect to the hyperparameter of the kernel. Only returned when eval_gradient is True. """ from scipy.spatial.distance import pdist, cdist, squareform from scipy import special X = np.atleast_2d(X) length_scale = _check_length_scale(X, self.length_scale) if Y is None: dists = pdist(X, metric='euclidean') Filter = (dists != 0.) K = np.zeros_like(dists) K[Filter] = ((dists[Filter]/length_scale)**(5./6.) * special.kv(5./6.,2*np.pi*dists[Filter]/length_scale)) K = squareform(K) lim0 = special.gamma(5./6.) / (2 * (np.pi**(5./6.))) np.fill_diagonal(K, lim0) K /= lim0 else: if eval_gradient: raise ValueError( "Gradient can only be evaluated when Y is None.") dists = cdist(X, Y, metric='euclidean') Filter = (dists != 0.) K = np.zeros_like(dists) K[Filter] = ((dists[Filter]/length_scale)**(5./6.) * special.kv(5./6.,2*np.pi*dists[Filter]/length_scale)) lim0 = special.gamma(5./6.) / (2 * (np.pi**(5./6.))) if np.sum(Filter) != len(K[0])*len(K[:,0]): K[~Filter] = lim0 K /= lim0 if eval_gradient: if self.hyperparameter_length_scale.fixed: # Hyperparameter l kept fixed return K, np.empty((X.shape[0], X.shape[0], 0)) elif not self.anisotropic or length_scale.shape[0] == 1: K_gradient = (K * squareform(dists))[:, :, np.newaxis] return K, K_gradient elif self.anisotropic: raise ValueError( "Gradient can only be evaluated with isotropic VonKarman kernel for the moment.") else: return K
def __call__(self, X, Y=None, eval_gradient=False): """Return the kernel k(X, Y) and optionally its gradient. Parameters ---------- X : ndarray of shape (n_samples_X, n_features) Left argument of the returned kernel k(X, Y) Y : ndarray of shape (n_samples_Y, n_features), default=None Right argument of the returned kernel k(X, Y). If None, k(X, X) if evaluated instead. eval_gradient : bool, default=False Determines whether the gradient with respect to the log of the kernel hyperparameter is computed. Only supported when Y is None. Returns ------- K : ndarray of shape (n_samples_X, n_samples_Y) Kernel k(X, Y) K_gradient : ndarray of shape (n_samples_X, n_samples_X, n_dims), \ optional The gradient of the kernel k(X, X) with respect to the log of the hyperparameter of the kernel. Only returned when `eval_gradient` is True. """ X = np.round(X) if not Y is None: Y = np.round(Y) X = np.atleast_2d(X) length_scale = _check_length_scale(X, self.length_scale) if Y is None: dists = pdist(X / length_scale, metric='euclidean') else: if eval_gradient: raise ValueError( "Gradient can only be evaluated when Y is None.") dists = cdist(X / length_scale, Y / length_scale, metric='euclidean') if self.nu == 0.5: K = np.exp(-dists) elif self.nu == 1.5: K = dists * math.sqrt(3) K = (1. + K) * np.exp(-K) elif self.nu == 2.5: K = dists * math.sqrt(5) K = (1. + K + K**2 / 3.0) * np.exp(-K) elif self.nu == np.inf: K = np.exp(-dists**2 / 2.0) else: # general case; expensive to evaluate K = dists K[K == 0.0] += np.finfo(float).eps # strict zeros result in nan tmp = (math.sqrt(2 * self.nu) * K) K.fill((2**(1. - self.nu)) / gamma(self.nu)) K *= tmp**self.nu K *= kv(self.nu, tmp) if Y is None: # convert from upper-triangular matrix to square matrix K = squareform(K) np.fill_diagonal(K, 1) if eval_gradient: if self.hyperparameter_length_scale.fixed: # Hyperparameter l kept fixed K_gradient = np.empty((X.shape[0], X.shape[0], 0)) return K, K_gradient # We need to recompute the pairwise dimension-wise distances if self.anisotropic: D = (X[:, np.newaxis, :] - X[np.newaxis, :, :])**2 \ / (length_scale ** 2) else: D = squareform(dists**2)[:, :, np.newaxis] if self.nu == 0.5: denominator = np.sqrt(D.sum(axis=2))[:, :, np.newaxis] K_gradient = K[..., np.newaxis] * \ np.divide(D, denominator, where=denominator != 0) elif self.nu == 1.5: K_gradient = \ 3 * D * np.exp(-np.sqrt(3 * D.sum(-1)))[..., np.newaxis] elif self.nu == 2.5: tmp = np.sqrt(5 * D.sum(-1))[..., np.newaxis] K_gradient = 5.0 / 3.0 * D * (tmp + 1) * np.exp(-tmp) elif self.nu == np.inf: K_gradient = D * K[..., np.newaxis] else: # approximate gradient numerically def f(theta): # helper function return self.clone_with_theta(theta)(X, Y) return K, _approx_fprime(self.theta, f, 1e-10) if not self.anisotropic: return K, K_gradient[:, :].sum(-1)[:, :, np.newaxis] else: return K, K_gradient else: return K
def __call__(self, X, Y=None, eval_gradient=False): X = np.atleast_2d(X) length_scale = _check_length_scale(X, self.length_scale) if Y is None: dists = pdist(X / length_scale, metric='euclidean') else: if eval_gradient: raise ValueError( "Gradient can only be evaluated when Y is None.") dists = cdist(X / length_scale, Y / length_scale, metric='euclidean') if self.nu == 0.5: K = np.exp(-dists) elif self.nu == 1.5: K = dists * math.sqrt(3) K = (1. + K) * np.exp(-K) elif self.nu == 2.5: K = dists * math.sqrt(5) K = (1. + K + K**2 / 3.0) * np.exp(-K) else: # general case; expensive to evaluate K = dists K[K == 0.0] += np.finfo(float).eps # strict zeros result in nan tmp = (math.sqrt(2 * self.nu) * K) K.fill((2**(1. - self.nu)) / gamma(self.nu)) K *= tmp**self.nu K *= kv(self.nu, tmp) if Y is None: # convert from upper-triangular matrix to square matrix K = squareform(K) np.fill_diagonal(K, 1) if eval_gradient: if self.hyperparameter_length_scale.fixed: # Hyperparameter l kept fixed K_gradient = np.empty((X.shape[0], X.shape[0], 0)) return K, K_gradient # We need to recompute the pairwise dimension-wise distances if self.anisotropic: D = (X[:, np.newaxis, :] - X[np.newaxis, :, :])**2 \ / (length_scale ** 2) else: D = squareform(dists**2)[:, :, np.newaxis] if self.nu == 0.5: K_gradient = safe_divide(K[..., np.newaxis] * D, np.sqrt(D.sum(2))[:, :, np.newaxis]) K_gradient[~np.isfinite(K_gradient)] = 0 elif self.nu == 1.5: K_gradient = \ 3 * D * np.exp(-np.sqrt(3 * D.sum(-1)))[..., np.newaxis] elif self.nu == 2.5: tmp = np.sqrt(5 * D.sum(-1))[..., np.newaxis] K_gradient = 5.0 / 3.0 * D * (tmp + 1) * np.exp(-tmp) else: # approximate gradient numerically def f(theta): # helper function return self.clone_with_theta(theta)(X, Y) return K, _approx_fprime(self.theta, f, 1e-10) if not self.anisotropic: return K, K_gradient[:, :].sum(-1)[:, :, np.newaxis] else: return K, K_gradient else: return K
def __call__(self, X, Y=None, dx=0, dy=0, eval_gradient=False): """Return the kernel k(X, Y) and optionally its gradient. Parameters ---------- X : array, shape (n_samples_X, n_features) Left argument of the returned kernel k(X, Y) Y : array, shape (n_samples_Y, n_features), (optional, default=None) Right argument of the returned kernel k(X, Y). If None, k(X, X) if evaluated instead. eval_gradient : bool (optional, default=False) Determines whether the gradient with respect to the kernel hyperparameter is determined. Only supported when Y is None. Returns ------- K : array, shape (n_samples_X, n_samples_Y) Kernel k(X, Y) K_gradient : array (opt.), shape (n_samples_X, n_samples_X, n_dims) The gradient of the kernel k(X, X) with respect to the hyperparameter of the kernel. Only returned when eval_gradient is True. """ # X = atoms_X.get_positions() # if atoms_Y is None: # Y = X # else: # Y = atoms_Y.get_positions() X = np.atleast_2d(X) length_scale = _check_length_scale(X, self.length_scale) dists = cdist(X / length_scale, Y / length_scale, metric='sqeuclidean') K = np.exp(-.5 * dists) if not self.anisotropic or length_scale.shape[0] == 1: if dx != 0 or dy != 0: if dx == dy: K = K * (1 - np.subtract.outer(X[:, dx - 1].T, Y[:, dx - 1])**2 / length_scale**2) / length_scale**2 # J_ii elif dx == 0: # and dy != 0: K = K * np.subtract.outer( X[:, dy - 1].T, Y[:, dy - 1]) / length_scale**2 # G elif dy == 0: # and dx != 0: K = -K * np.subtract.outer( X[:, dx - 1].T, Y[:, dx - 1]) / length_scale**2 # K_prime else: K = -K * np.subtract.outer( X[:, dx - 1].T, Y[:, dx - 1]) * np.subtract.outer( X[:, dy - 1].T, Y[:, dy - 1]) / length_scale**4 # J_ij else: if dx != 0 or dy != 0: if dx == dy: K = K * ( 1 - np.subtract.outer(X[:, dx - 1].T, Y[:, dx - 1])**2 / length_scale[dx - 1]**2) / length_scale[dx - 1]**2 elif dx == 0: K = K * np.subtract.outer( X[:, dy - 1].T, Y[:, dy - 1]) / length_scale[dy - 1]**2 elif dy == 0: K = -K * np.subtract.outer( X[:, dx - 1].T, Y[:, dx - 1]) / length_scale[dx - 1]**2 else: K = -K * np.subtract.outer( X[:, dx - 1].T, Y[:, dx - 1]) * np.subtract.outer( X[:, dy - 1].T, Y[:, dy - 1]) / (length_scale[dx - 1]**2 * length_scale[dy - 1]**2) if eval_gradient: if self.hyperparameter_length_scale.fixed: # Hyperparameter l kept fixed return K, np.empty((X.shape[0], Y.shape[0], 0)) elif not self.anisotropic or length_scale.shape[0] == 1: if dx == 0 and dy == 0: # K_gradient = (K * dists)[:, :, np.newaxis] K_gradient = (K * dists)[:, :, np.newaxis] / length_scale elif dx != 0 and dy == 0: # K_gradient = (K * (dists - 2))[:, :, np.newaxis] K_gradient = (K * (dists - 2))[:, :, np.newaxis] / length_scale elif dy != 0 and dx == 0: # K_gradient = (K * (dists - 2))[:, :, np.newaxis] K_gradient = (K * (dists - 2))[:, :, np.newaxis] / length_scale # K_gradient = ( # K * (dists/length_scale**2 - 2/length_scale**2) # )[:, :, np.newaxis] else: if dx == dy: # K_gradient = ( # K * (dists - 4) + 2 * np.exp(-0.5*dists) / # length_scale**2)[:, :, np.newaxis] K_gradient = ( K * (dists - 4) + 2 * np.exp(-0.5 * dists) / length_scale**2)[:, :, np.newaxis] / length_scale # K_gradient = ( # np.exp(-.5 * dists) / length_scale ** 2 * # (5*dists - 2 - dists **2))[:, :, np.newaxis] # K_gradient = ( # np.exp(-0.5*dists)*( # dists**2/length_scale**3 - # 1./length_scale**5 - # 4*(dists-1/length_scale**3) - # 2/length_scale**3))[:, :, np.newaxis] else: # K_gradient = (K * (dists - 4))[:, :, np.newaxis] K_gradient = (K * (dists - 4))[:, :, np.newaxis] / length_scale # K_gradient = ( # -np.exp(-.5 * dists) * dists/length_scale**2 * # (4 - dists))[:, :, np.newaxis] # K_gradient = ( # np.exp(-.5 * dists) * dists**2 / length_scale**3 # * (dists**2 - 4))[:, :, np.newaxis] return K, K_gradient elif self.anisotropic: # We need to recompute the pairwise dimension-wise distances grad = (X[:, np.newaxis, :] - Y[np.newaxis, :, :])**2 / (length_scale**2) if dx == 0 and dy == 0: K_gradient = grad * K[..., np.newaxis] elif (dx != 0 and dy == 0): K_gradient = grad * K[..., np.newaxis] K_gradient[:, :, dx - 1] = K_gradient[:, :, dx - 1] - 2 * K elif (dy != 0 and dx == 0): K_gradient = grad * K[..., np.newaxis] K_gradient[:, :, dy - 1] = K_gradient[:, :, dy - 1] - 2 * K else: if dx == dy: K_gradient = grad * K[..., np.newaxis] K_gradient[:, :, dx - 1] = ( K_gradient[:, :, dx - 1] - np.exp(-0.5 * dists) * (2 / length_scale[dx - 1]**2 - 4 * grad[:, :, dx - 1] / length_scale[dx - 1]**2)) else: K_gradient = grad * K[..., np.newaxis] K_gradient[:, :, dx - 1] = (K_gradient[:, :, dx - 1] - 2 * K) K_gradient[:, :, dy - 1] = (K_gradient[:, :, dy - 1] - 2 * K) return K, K_gradient else: return K