Source code for VolterraBasis.basis._multidim_basis

"""
This the main estimator module
"""
import numpy as np
import scipy.stats
from sklearn.base import TransformerMixin
from ._data_describe import DescribeResult


[docs]class TensorialBasis2D(TransformerMixin): """ Combine two 1D basis to get a 2D basis """
[docs] def __init__(self, b1, b2=None): """Take two of basis""" self.b1 = b1 if b2 is None: self.b2 = b1 else: self.b2 = b2 # Force rank projection self.b1.const_removed = False self.b2.const_removed = False self.const_removed = False
def fit(self, describe_result): if isinstance(describe_result, np.ndarray): describe_result = scipy.stats.describe(describe_result) dim = describe_result.mean.shape[0] if dim != 2: raise ValueError("This basis does not support dimension other than 2.") self.b1.fit(DescribeResult(describe_result.nobs, (describe_result.minmax[0][0:1], describe_result.minmax[1][0:1]), describe_result.mean[0:1], describe_result.variance[0:1], describe_result.skewness[0:1], describe_result.kurtosis[0:1])) self.b2.fit(DescribeResult(describe_result.nobs, (describe_result.minmax[0][1:2], describe_result.minmax[1][1:2]), describe_result.mean[1:2], describe_result.variance[1:2], describe_result.skewness[1:2], describe_result.kurtosis[1:2])) self.n_output_features_ = self.b1.n_output_features_ * self.b2.n_output_features_ self.dim_out_basis = 1 # This is dimension of the output return self def basis(self, X): nsamples, nfeatures = X.shape features = np.einsum("nk,nl->nkl", self.b1.basis(X[:, slice(0, 1)]), self.b2.basis(X[:, slice(1, 2)])).reshape(nsamples, -1) return features def deriv(self, X, deriv_order=1): if deriv_order == 2: return self.hessian(X) elif deriv_order > 2: raise NotImplementedError("Implement it yourself") nsamples, nfeatures = X.shape temp_arr_1 = np.einsum("nk,nl...->nkl...", self.b1.basis(X[:, slice(0, 1)]), self.b2.deriv(X[:, slice(1, 2)], deriv_order=deriv_order)) grad_1 = temp_arr_1.reshape(nsamples, -1, *temp_arr_1.shape[3:]) temp_arr_2 = np.einsum("nk...,nl->nkl...", self.b1.deriv(X[:, slice(0, 1)], deriv_order=deriv_order), self.b2.basis(X[:, slice(1, 2)])) grad_2 = temp_arr_2.reshape(nsamples, -1, *temp_arr_2.shape[3:]) return np.concatenate((grad_2, grad_1), axis=-1) def hessian(self, X): nsamples, nfeatures = X.shape temp_arr_1 = np.einsum("nk,nl...->nkl...", self.b1.basis(X[:, slice(0, 1)]), self.b2.hessian(X[:, slice(1, 2)])) hess_1 = temp_arr_1.reshape(nsamples, -1, *temp_arr_1.shape[3:]) temp_arr_cross = np.einsum("nkd,nlf->nkldf", self.b1.deriv(X[:, slice(0, 1)]), self.b2.deriv(X[:, slice(1, 2)])) hess_cross = temp_arr_cross.reshape(nsamples, -1, *temp_arr_cross.shape[3:]) temp_arr_2 = np.einsum("nk...,nl->nkl...", self.b1.hessian(X[:, slice(0, 1)]), self.b2.basis(X[:, slice(1, 2)])) hess_2 = temp_arr_2.reshape(nsamples, -1, *temp_arr_2.shape[3:]) # print(hess_1.shape, hess_cross.shape, hess_2.shape) return np.concatenate((np.concatenate((hess_2, hess_cross), axis=-2), np.concatenate((hess_cross, hess_1), axis=-2)), axis=-1) def antiderivative(self, X, order=1): raise NotImplementedError("Don't try this")
[docs] def comb_indices(self, i, j): """ Get index k of the (i,j) element of the basis """ return np.ravel_multi_index((i, j), (self.b1.n_output_features_, self.b2.n_output_features_))
[docs] def split_index(self, k): """ Get (i,j) decomposition of the keme element of the basis """ return np.unravel_index(k, (self.b1.n_output_features_, self.b2.n_output_features_))
# # class TensorialBasis(TransformerMixin): # """ # Combine several 1D basis to get a multidimensionnal basis # """ # # def __init__(self, *basis: TransformerMixin): # """Take set of basis""" # if len(basis) <= 1: # raise ValueError("Not enough basis in Tensorial Basis") # self.basis_set = basis # self.dim = len(basis) # # def fit(self, X, y=None): # for i, b in enumerate(self.basis_set): # b.fit(X[:, slice(i, i + 1)]) # return self # # def basis(self, X): # nsamples, nfeatures = X.shape # features = self.basis_set[0].basis(X[:, slice(0, 1)]) # for i, b in enumerate(self.basis_set[1:]): # features = np.einsum("nk,nl->nkl", features, b.basis(X[:, slice(i, i + 1)])).reshape(nsamples, -1) # return features # # def deriv(self, X, deriv_order=1): # nsamples, nfeatures = X.shape # features = self.basis_set[0].basis(X[:, slice(0, 1)]) # (ntimes x nb features) # grad = self.basis_set[0].deriv(X[:, slice(0, 1)]) # (ntimes x nb features x 1) -> (ntimes x nb features x dim_x) # # grad = np.concatenate([np.kron(f1.grad, f2.value[np.newaxis, :, :]), np.kron(f1.value[np.newaxis, :, :], f2.grad)], axis=0) # for i, b in enumerate(self.basis_set[1:]): # # On doit multiplier grad Ă  gauche par ba_f et ajouter une line qui est features (avant kronecker product x grad) # ba_f = b.basis(X[:, slice(i, i + 1)]) # grad_f = self.basis_set[0].deriv(X[:, slice(i, i + 1)]) # # grad = np.einsum("nkl,nj-> nkjl", grad, ba_f) # # On ajoute ensuite # to_concatenate = np.einsum("ni,njk-> nijk", features, grad_f).reshape(nsamples, -1, i + 1) # # Puis on concatenate grad et to_concatenate # grapd = np.concatenate(grad, to_concatenate) # features = np.einsum("nk,nl->nkl", features, ba_f).reshape(nsamples, -1) # # return grad # # def hessian(self, X): # return self.deriv(X, deriv_order=2) if __name__ == "__main__": # pragma: no cover from _local_features import BSplineFeatures # Plot basis x_range = np.linspace(-10, 10, 30).reshape(-1, 2) n_elems = 7 ten_basis = TensorialBasis2D(BSplineFeatures(n_elems)).fit(x_range) print(x_range.shape) print("Basis") print(ten_basis.basis(x_range).shape) print("Deriv") print(ten_basis.deriv(x_range).shape) print("Hessian") print(ten_basis.hessian(x_range).shape)