PyDLLib 1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. DLL/Data/Metrics.py +626 -0
  2. DLL/Data/Preprocessing.py +384 -0
  3. DLL/Data/_DataReader.py +55 -0
  4. DLL/Data/__init__.py +1 -0
  5. DLL/DeepLearning/Callbacks/_BackUp.py +71 -0
  6. DLL/DeepLearning/Callbacks/_BaseCallback.py +43 -0
  7. DLL/DeepLearning/Callbacks/_EarlyStopping.py +102 -0
  8. DLL/DeepLearning/Callbacks/_ReduceLROnPlateau.py +90 -0
  9. DLL/DeepLearning/Callbacks/__init__.py +4 -0
  10. DLL/DeepLearning/Initialisers/_Initialiser.py +24 -0
  11. DLL/DeepLearning/Initialisers/_Kaiming_He.py +55 -0
  12. DLL/DeepLearning/Initialisers/_Xavier_Glorot.py +39 -0
  13. DLL/DeepLearning/Initialisers/__init__.py +2 -0
  14. DLL/DeepLearning/Layers/Activations/_Activation.py +23 -0
  15. DLL/DeepLearning/Layers/Activations/_ReLU.py +51 -0
  16. DLL/DeepLearning/Layers/Activations/_Sigmoid.py +51 -0
  17. DLL/DeepLearning/Layers/Activations/_SoftMax.py +83 -0
  18. DLL/DeepLearning/Layers/Activations/_Tanh.py +47 -0
  19. DLL/DeepLearning/Layers/Activations/__init__.py +4 -0
  20. DLL/DeepLearning/Layers/Regularisation/_BaseRegularisation.py +12 -0
  21. DLL/DeepLearning/Layers/Regularisation/_BatchNormalisation.py +106 -0
  22. DLL/DeepLearning/Layers/Regularisation/_Dropout.py +79 -0
  23. DLL/DeepLearning/Layers/Regularisation/_GroupNormalisation.py +112 -0
  24. DLL/DeepLearning/Layers/Regularisation/_InstanceNormalisation.py +50 -0
  25. DLL/DeepLearning/Layers/Regularisation/_LayerNormalisation.py +37 -0
  26. DLL/DeepLearning/Layers/Regularisation/__init__.py +5 -0
  27. DLL/DeepLearning/Layers/_Add.py +109 -0
  28. DLL/DeepLearning/Layers/_Attention.py +161 -0
  29. DLL/DeepLearning/Layers/_BaseLayer.py +87 -0
  30. DLL/DeepLearning/Layers/_Bidirectional.py +113 -0
  31. DLL/DeepLearning/Layers/_Conv2D.py +133 -0
  32. DLL/DeepLearning/Layers/_Dense.py +109 -0
  33. DLL/DeepLearning/Layers/_DenseKAN.py +184 -0
  34. DLL/DeepLearning/Layers/_Flatten.py +55 -0
  35. DLL/DeepLearning/Layers/_Identity.py +50 -0
  36. DLL/DeepLearning/Layers/_Input.py +11 -0
  37. DLL/DeepLearning/Layers/_LSTM.py +261 -0
  38. DLL/DeepLearning/Layers/_LayerList.py +98 -0
  39. DLL/DeepLearning/Layers/_MaxPooling2D.py +101 -0
  40. DLL/DeepLearning/Layers/_RNN.py +185 -0
  41. DLL/DeepLearning/Layers/_Reshape.py +52 -0
  42. DLL/DeepLearning/Layers/__init__.py +14 -0
  43. DLL/DeepLearning/Losses/_BCE.py +96 -0
  44. DLL/DeepLearning/Losses/_BaseLoss.py +18 -0
  45. DLL/DeepLearning/Losses/_CCE.py +87 -0
  46. DLL/DeepLearning/Losses/_Exponential.py +101 -0
  47. DLL/DeepLearning/Losses/_Huber.py +104 -0
  48. DLL/DeepLearning/Losses/_MAE.py +85 -0
  49. DLL/DeepLearning/Losses/_MSE.py +86 -0
  50. DLL/DeepLearning/Losses/__init__.py +6 -0
  51. DLL/DeepLearning/Model.py +265 -0
  52. DLL/DeepLearning/Optimisers/_ADADELTA.py +53 -0
  53. DLL/DeepLearning/Optimisers/_ADAGRAD.py +51 -0
  54. DLL/DeepLearning/Optimisers/_ADAM.py +65 -0
  55. DLL/DeepLearning/Optimisers/_BaseOptimiser.py +20 -0
  56. DLL/DeepLearning/Optimisers/_LBFGS.py +120 -0
  57. DLL/DeepLearning/Optimisers/_RMSPROP.py +62 -0
  58. DLL/DeepLearning/Optimisers/_SGD.py +44 -0
  59. DLL/DeepLearning/Optimisers/__init__.py +6 -0
  60. DLL/DeepLearning/__init__.py +0 -0
  61. DLL/Exceptions/_Exceptions.py +19 -0
  62. DLL/Exceptions/__init__.py +2 -0
  63. DLL/MachineLearning/SupervisedLearning/Calibration/_CalibratedClassifier.py +88 -0
  64. DLL/MachineLearning/SupervisedLearning/Calibration/_IsotonicRegression.py +114 -0
  65. DLL/MachineLearning/SupervisedLearning/Calibration/__init__.py +2 -0
  66. DLL/MachineLearning/SupervisedLearning/GaussianProcesses/_GaussianProcessRegressor.py +190 -0
  67. DLL/MachineLearning/SupervisedLearning/GaussianProcesses/__init__.py +1 -0
  68. DLL/MachineLearning/SupervisedLearning/Kernels.py +851 -0
  69. DLL/MachineLearning/SupervisedLearning/LinearModels/TimeSeries.py +147 -0
  70. DLL/MachineLearning/SupervisedLearning/LinearModels/_Elasticnet.py +161 -0
  71. DLL/MachineLearning/SupervisedLearning/LinearModels/_LassoRegression.py +157 -0
  72. DLL/MachineLearning/SupervisedLearning/LinearModels/_LinearRegression.py +81 -0
  73. DLL/MachineLearning/SupervisedLearning/LinearModels/_LogisticRegression.py +182 -0
  74. DLL/MachineLearning/SupervisedLearning/LinearModels/_RANSAC.py +97 -0
  75. DLL/MachineLearning/SupervisedLearning/LinearModels/_RidgeRegression.py +78 -0
  76. DLL/MachineLearning/SupervisedLearning/LinearModels/__init__.py +6 -0
  77. DLL/MachineLearning/SupervisedLearning/NaiveBayes/_BernoulliNaiveBayes.py +115 -0
  78. DLL/MachineLearning/SupervisedLearning/NaiveBayes/_GaussianNaiveBayes.py +111 -0
  79. DLL/MachineLearning/SupervisedLearning/NaiveBayes/_MultinomialNaiveBayes.py +115 -0
  80. DLL/MachineLearning/SupervisedLearning/NaiveBayes/__init__.py +3 -0
  81. DLL/MachineLearning/SupervisedLearning/Neighbors/_KNearestNeighborsClassifier.py +115 -0
  82. DLL/MachineLearning/SupervisedLearning/Neighbors/_KNearestNeighborsRegressor.py +90 -0
  83. DLL/MachineLearning/SupervisedLearning/Neighbors/__init__.py +2 -0
  84. DLL/MachineLearning/SupervisedLearning/SupportVectorMachines/_SVC.py +293 -0
  85. DLL/MachineLearning/SupervisedLearning/SupportVectorMachines/_SVR.py +100 -0
  86. DLL/MachineLearning/SupervisedLearning/SupportVectorMachines/__init__.py +2 -0
  87. DLL/MachineLearning/SupervisedLearning/Trees/_AdaBoostClassifier.py +179 -0
  88. DLL/MachineLearning/SupervisedLearning/Trees/_AdaBoostRegressor.py +141 -0
  89. DLL/MachineLearning/SupervisedLearning/Trees/_DecisionTree.py +219 -0
  90. DLL/MachineLearning/SupervisedLearning/Trees/_GradientBoostingClassifier.py +207 -0
  91. DLL/MachineLearning/SupervisedLearning/Trees/_GradientBoostingRegressor.py +122 -0
  92. DLL/MachineLearning/SupervisedLearning/Trees/_LGBMClassifier.py +288 -0
  93. DLL/MachineLearning/SupervisedLearning/Trees/_LGBMRegressor.py +168 -0
  94. DLL/MachineLearning/SupervisedLearning/Trees/_LGBMTree.py +199 -0
  95. DLL/MachineLearning/SupervisedLearning/Trees/_RandomForest.py +96 -0
  96. DLL/MachineLearning/SupervisedLearning/Trees/_RegressionForest.py +75 -0
  97. DLL/MachineLearning/SupervisedLearning/Trees/_RegressionTree.py +148 -0
  98. DLL/MachineLearning/SupervisedLearning/Trees/_XGBoostClassifier.py +239 -0
  99. DLL/MachineLearning/SupervisedLearning/Trees/_XGBoostRegressor.py +131 -0
  100. DLL/MachineLearning/SupervisedLearning/Trees/_XGBoostTree.py +105 -0
  101. DLL/MachineLearning/SupervisedLearning/Trees/__init__.py +12 -0
  102. DLL/MachineLearning/SupervisedLearning/__init__.py +0 -0
  103. DLL/MachineLearning/UnsupervisedLearning/Clustering/_DBScan.py +87 -0
  104. DLL/MachineLearning/UnsupervisedLearning/Clustering/_GaussianMixtureModels.py +121 -0
  105. DLL/MachineLearning/UnsupervisedLearning/Clustering/_KMeans.py +120 -0
  106. DLL/MachineLearning/UnsupervisedLearning/Clustering/_SpectralClustering.py +105 -0
  107. DLL/MachineLearning/UnsupervisedLearning/Clustering/__init__.py +4 -0
  108. DLL/MachineLearning/UnsupervisedLearning/DimensionalityReduction/_LDA.py +118 -0
  109. DLL/MachineLearning/UnsupervisedLearning/DimensionalityReduction/_PCA.py +101 -0
  110. DLL/MachineLearning/UnsupervisedLearning/DimensionalityReduction/_QDA.py +69 -0
  111. DLL/MachineLearning/UnsupervisedLearning/DimensionalityReduction/_RobustPCA.py +143 -0
  112. DLL/MachineLearning/UnsupervisedLearning/DimensionalityReduction/_TSNE.py +151 -0
  113. DLL/MachineLearning/UnsupervisedLearning/DimensionalityReduction/_UMAP.py +176 -0
  114. DLL/MachineLearning/UnsupervisedLearning/DimensionalityReduction/__init__.py +6 -0
  115. DLL/MachineLearning/UnsupervisedLearning/OutlierDetection/_IsolationForest.py +175 -0
  116. DLL/MachineLearning/UnsupervisedLearning/OutlierDetection/__init__.py +1 -0
  117. DLL/MachineLearning/UnsupervisedLearning/__init__.py +0 -0
  118. DLL/MachineLearning/__init__.py +0 -0
  119. DLL/__init__.py +0 -0
  120. pydllib-1.0.dist-info/METADATA +104 -0
  121. pydllib-1.0.dist-info/RECORD +124 -0
  122. pydllib-1.0.dist-info/WHEEL +5 -0
  123. pydllib-1.0.dist-info/licenses/LICENSE +21 -0
  124. pydllib-1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,851 @@
1
+ import torch
2
+ from scipy.special import gamma, kv
3
+ from math import sqrt
4
+ from functools import partial
5
+
6
+
7
+ class _Base:
8
+ def __add__(self, other):
9
+ if not isinstance(other, _Base):
10
+ raise NotImplementedError()
11
+ return _Compound(self, other, add=True)
12
+
13
+ def __mul__(self, other):
14
+ if not isinstance(other, _Base):
15
+ raise NotImplementedError()
16
+ return _Compound(self, other, multiply=True)
17
+
18
+ def __pow__(self, power):
19
+ if not isinstance(power, int) or power < 2:
20
+ raise ValueError("The exponent must be an integer greater than 1.")
21
+ kernel = _Exponent(self, power=power)
22
+ return kernel
23
+
24
+ class _Compound(_Base):
25
+ def __init__(self, kernel_1, kernel_2, add=False, multiply=False):
26
+ self.kernel_1 = kernel_1
27
+ self.kernel_2 = kernel_2
28
+ self.add = add
29
+ self.multiply = multiply
30
+
31
+ def __call__(self, X1, X2):
32
+ if self.add:
33
+ return self.kernel_1(X1, X2) + self.kernel_2(X1, X2)
34
+ elif self.multiply:
35
+ return self.kernel_1(X1, X2) * self.kernel_2(X1, X2)
36
+
37
+ def update(self, derivative_function, X):
38
+ if self.add:
39
+ self.kernel_1.update(derivative_function, X)
40
+ self.kernel_2.update(derivative_function, X)
41
+ elif self.multiply:
42
+ kernel_1_covariance = self.kernel_1(X, X)
43
+ kernel_2_covariance = self.kernel_2(X, X)
44
+
45
+ kernel_1_derivative = partial(self.kernel_derivative, derivative_function, kernel_2_covariance)
46
+ kernel_2_derivative = partial(self.kernel_derivative, derivative_function, kernel_1_covariance)
47
+
48
+ self.kernel_1.update(kernel_1_derivative, X)
49
+ self.kernel_2.update(kernel_2_derivative, X)
50
+
51
+ def kernel_derivative(self, derivative_function, X, parameter_derivative):
52
+ return derivative_function(parameter_derivative * X)
53
+
54
+ def parameters(self):
55
+ return self.kernel_1.parameters() | self.kernel_2.parameters()
56
+
57
+ class _Exponent(_Base):
58
+ def __init__(self, kernel, power):
59
+ self.kernel = kernel
60
+ self.power = power
61
+
62
+ def __call__(self, X1, X2):
63
+ return self.kernel(X1, X2) ** self.power
64
+
65
+ def update(self, derivative_function, X):
66
+ kernel_derivative = partial(self.kernel_derivative, derivative_function, X)
67
+ self.kernel.update(kernel_derivative, X)
68
+
69
+ def kernel_derivative(self, derivative_function, X, parameter_derivative):
70
+ return derivative_function(self.power * self.kernel(X, X) ** (self.power - 1) * parameter_derivative)
71
+
72
+ def parameters(self):
73
+ return self.kernel.parameters()
74
+
75
+ class RBF(_Base):
76
+ """
77
+ The commonly used radial basis function (rbf) kernel. Yields high values for samples close to one another. The used equation is:
78
+
79
+ .. math::
80
+
81
+ k(x_i, x_j) = \\sigma^2 \\exp\\left(-\\frac{(x_i - x_j)^2}{2 l^2}\\right),
82
+
83
+ where :math:`d` is the the Euclidian metric and :math:`\\sigma` and :math:`l` are the sigma and the correlation_length parameters respectively.
84
+
85
+ Args:
86
+ sigma (float, optional): The overall scale factor of the variance. Controls the amplitude of the kernel. Must be a positive real number. Defaults to 1.
87
+ correlation_length (float | torch.Tensor, optional): The length scale of the kernel. Determines how quickly the similarity decays as points become further apart. Must be a positive real number or a torch.Tensor of shape (n_features,). Defaults to 1.
88
+ train_sigma (bool, optional): Determines, wheter or not the sigma parameter should be changed during training the kernel. Defaults to True.
89
+ train_correlation_length (bool, optional): Determines, wheter or not the correlation_length parameter should be changed during training the kernel. Defaults to True.
90
+ """
91
+
92
+ instance = 0
93
+ """
94
+ :meta private:
95
+ """
96
+
97
+ def __init__(self, sigma=1, correlation_length=1, train_sigma=True, train_correlation_length=True):
98
+ if not isinstance(sigma, int | float) or sigma <= 0:
99
+ raise ValueError("sigma must be a positive real number.")
100
+ if (not isinstance(correlation_length, int | float) or correlation_length <= 0) and (not isinstance(correlation_length, torch.Tensor) or torch.any(correlation_length < 0) or correlation_length.ndim != 1):
101
+ raise ValueError("correlation_length must be a positive real number or a torch.Tensor of shape (n_features,).")
102
+ if not isinstance(train_sigma, bool) or not isinstance(train_correlation_length, bool):
103
+ raise TypeError("train_sigma andtrain_correlation_length must be boolean values. ")
104
+
105
+ RBF.instance += 1
106
+ self.number = RBF.instance
107
+
108
+ self.sigma = torch.tensor([sigma], dtype=torch.float32)
109
+ # self.correlation_length = correlation_length if isinstance(correlation_length, torch.Tensor) else torch.tensor([correlation_length], dtype=torch.float32)
110
+ self.correlation_length = torch.tensor([correlation_length], dtype=torch.float32) if isinstance(correlation_length, int | float) else correlation_length
111
+
112
+ self.train_sigma = train_sigma
113
+ self.train_correlation_length = train_correlation_length
114
+
115
+ def __call__(self, X1, X2):
116
+ """
117
+ :meta public:
118
+
119
+ Yields the kernel matrix between two vectors.
120
+
121
+ Args:
122
+ X1 (torch.Tensor of shape (n_samples_1, n_features))
123
+ X2 (torch.Tensor of shape (n_samples_2, n_features))
124
+
125
+ Returns:
126
+ kernel_matrix (torch.Tensor of shape (n_samples_1, n_samples_2)): The pairwise kernel values between samples from X1 and X2.
127
+
128
+ Raises:
129
+ TypeError: If the input matricies are not a PyTorch tensors.
130
+ ValueError: If the input matricies are not the correct shape.
131
+ """
132
+ if not isinstance(X1, torch.Tensor) or not isinstance(X2, torch.Tensor):
133
+ raise TypeError("The input matricies must be PyTorch tensors.")
134
+ if X1.ndim != 2 or X2.ndim != 2:
135
+ raise ValueError("The input matricies must be 2 dimensional tensors.")
136
+ if X1.shape[1] != X2.shape[1]:
137
+ raise ValueError("X1 and X2 must have the same number of features.")
138
+ if len(self.correlation_length) != 1 and X1.shape[1] != len(self.correlation_length):
139
+ raise ValueError("correlation_length must be of length n_features.")
140
+
141
+ if self.sigma.device != X1.device:
142
+ self.sigma = self.sigma.to(X1.device)
143
+ self.correlation_length = self.correlation_length.to(X1.device)
144
+
145
+ dists_squared = torch.cdist(X1 / self.correlation_length, X2 / self.correlation_length, p=2) ** 2
146
+ return self.sigma ** 2 * torch.exp(-dists_squared / 2)
147
+
148
+ def derivative_sigma(self, X1, X2):
149
+ """
150
+ :meta private:
151
+ """
152
+ if not self.train_sigma: return torch.zeros((len(X1), len(X2)), dtype=X1.dtype, device=X1.device)
153
+ val = 2 * self(X1, X2) / self.sigma
154
+ # descrete = -self(X1, X2)
155
+ # eps = 1e-6
156
+ # self.sigma += eps
157
+ # descrete += self(X1, X2)
158
+ # self.sigma -= eps
159
+ # print(descrete[0] / eps, val[0])
160
+ return val
161
+
162
+ def derivative_corr_len(self, X1, X2):
163
+ """
164
+ :meta private:
165
+ """
166
+ if len(self.correlation_length) != 1:
167
+ if not self.train_correlation_length: return torch.zeros((len(self.correlation_length), len(X1), len(X2)), dtype=X1.dtype, device=X1.device)
168
+ val = self(X1, X2).unsqueeze(0) * ((X1[torch.newaxis, :, :] - X2[:, torch.newaxis, :]) ** 2 / self.correlation_length ** 3).permute((2, 0, 1))
169
+ else:
170
+ if not self.train_correlation_length: return torch.zeros((len(X1), len(X2)), dtype=X1.dtype, device=X1.device)
171
+ dists_squared = torch.cdist(X1, X2, p=2) ** 2
172
+ val = self(X1, X2) * (dists_squared / (self.correlation_length ** 3))
173
+ # descrete = -self(X1, X2)
174
+ # eps = 1e-6
175
+ # self.correlation_length += eps
176
+ # descrete += self(X1, X2)
177
+ # self.correlation_length -= eps
178
+ # print(descrete[0] / eps, val[0])
179
+ return val
180
+
181
+ def update(self, derivative_function, X):
182
+ """
183
+ :meta private:
184
+ """
185
+ derivative_covariance_sigma = self.derivative_sigma(X, X)
186
+ derivative_covariance_corr_len = self.derivative_corr_len(X, X)
187
+
188
+ self.sigma.grad = derivative_function(derivative_covariance_sigma).to(dtype=self.sigma.dtype)
189
+ self.correlation_length.grad = derivative_function(derivative_covariance_corr_len).to(dtype=self.correlation_length.dtype)
190
+
191
+ def parameters(self):
192
+ """
193
+ Yields the parameters of the kernel as a dictionary. If one uses a combination of the kernels, the parameters of each of the child kernels are returned.
194
+
195
+ Returns:
196
+ parameters (dict[str, torch.Tensor]): The parameters as a dictionary. The key of the parameter is eg. "rbf_sigma_1".
197
+ """
198
+ return {("rbf_sigma" + "_" + str(self.number)): self.sigma, ("rbf_corr_len" + "_" + str(self.number)): self.correlation_length}
199
+
200
+ class Linear(_Base):
201
+ """
202
+ The linear kernel, often used as a baseline in kernel-based learning methods, representing a linear relationship between inputs. The used equation is:
203
+
204
+ .. math::
205
+
206
+ k(x_i, x_j) = \\sigma^2 x_ix_j+\\sigma_{bias},
207
+
208
+ where :math:`\\sigma` and :math:`\\sigma_{bias}` are the sigma and the sigma_bias parameters respectively.
209
+
210
+ Args:
211
+ sigma (float, optional): The overall scale factor of the variance. Controls the amplitude of the kernel. Must be a positive real number. Defaults to 1.
212
+ sigma_bias (float, optional): The constant term of the kernel, sometimes called the bias or intercept. It allows the kernel function to handle non-zero means. Must be a real number. Defaults to 0.
213
+ train_sigma (bool, optional): Determines, wheter or not the sigma parameter should be changed during training the kernel. Defaults to True.
214
+ train_sigma_bias (bool, optional): Determines, wheter or not the sigma_bias parameter should be changed during training the kernel. Defaults to True.
215
+
216
+ Example:
217
+ The commonly used polynomial kernel can be used as follows:
218
+
219
+ .. code-block:: python
220
+
221
+ from DLL.MachineLearning.SupervisedLearning.Kernels import Linear
222
+
223
+ linear_kernel = Linear()
224
+ polynomial_kernel_degree_d = linear_kernel ** d
225
+ """
226
+
227
+ instance = 0
228
+ """
229
+ :meta private:
230
+ """
231
+ def __init__(self, sigma=1, sigma_bias=0, train_sigma=True, train_sigma_bias=True):
232
+ if not isinstance(sigma, int | float) or sigma <= 0:
233
+ raise ValueError("sigma must be a positive real number.")
234
+ if not isinstance(sigma_bias, int | float):
235
+ raise TypeError("sigma_bias must be a real number.")
236
+ if not isinstance(train_sigma, bool) or not isinstance(train_sigma_bias, bool):
237
+ raise TypeError("train_sigma and train_sigma_bias must be boolean values.")
238
+
239
+ Linear.instance += 1
240
+ self.number = Linear.instance
241
+
242
+ self.sigma = torch.tensor([sigma], dtype=torch.float32)
243
+ self.sigma_bias = torch.tensor([sigma_bias], dtype=torch.float32)
244
+
245
+ self.train_sigma = train_sigma
246
+ self.train_sigma_bias = train_sigma_bias
247
+
248
+ def __call__(self, X1, X2):
249
+ """
250
+ :meta public:
251
+
252
+ Yields the kernel matrix between two vectors.
253
+
254
+ Args:
255
+ X1 (torch.Tensor of shape (n_samples_1, n_features))
256
+ X2 (torch.Tensor of shape (n_samples_2, n_features))
257
+
258
+ Returns:
259
+ kernel_matrix (torch.Tensor of shape (n_samples_1, n_samples_2)): The pairwise kernel values between samples from X1 and X2.
260
+
261
+ Raises:
262
+ TypeError: If the input matricies are not a PyTorch tensors.
263
+ ValueError: If the input matricies are not the correct shape.
264
+ """
265
+ if not isinstance(X1, torch.Tensor) or not isinstance(X2, torch.Tensor):
266
+ raise TypeError("The input matricies must be PyTorch tensors.")
267
+ if X1.ndim != 2 or X2.ndim != 2:
268
+ raise ValueError("The input matricies must be 2 dimensional tensors.")
269
+ if X1.shape[1] != X2.shape[1]:
270
+ raise ValueError("X1 and X2 must have the same number of features.")
271
+
272
+ if self.sigma.device != X1.device:
273
+ self.sigma = self.sigma.to(X1.device)
274
+ self.sigma_bias = self.sigma_bias.to(X1.device)
275
+
276
+ return self.sigma_bias ** 2 + self.sigma ** 2 * X1 @ X2.T
277
+
278
+ def derivative_sigma(self, X1, X2):
279
+ """
280
+ :meta private:
281
+ """
282
+ if not self.train_sigma: return torch.zeros((len(X1), len(X2)), dtype=X1.dtype, device=X1.device)
283
+ val = 2 * self.sigma * X1 @ X2.T
284
+ # descrete = -self(X1, X2)
285
+ # eps = 1e-6
286
+ # self.sigma += eps
287
+ # descrete += self(X1, X2)
288
+ # self.sigma -= eps
289
+ # print(descrete[10] / eps, val[10])
290
+ return val
291
+
292
+ def derivative_sigma_bias(self, X1, X2):
293
+ """
294
+ :meta private:
295
+ """
296
+ if not self.train_sigma_bias: return torch.zeros((len(X1), len(X2)), dtype=X1.dtype, device=X1.device)
297
+ val = (2 * self.sigma_bias).to(X1.dtype)
298
+ # descrete = -self(X1, X2)
299
+ # eps = 1e-6
300
+ # self.sigma_bias += eps
301
+ # descrete += self(X1, X2)
302
+ # self.sigma_bias -= eps
303
+ # print(descrete[0] / eps, val[0])
304
+ return val
305
+
306
+ def update(self, derivative_function, X):
307
+ """
308
+ :meta private:
309
+ """
310
+ derivative_covariance_sigma = self.derivative_sigma(X, X)
311
+ derivative_covariance_sigma_bias = self.derivative_sigma_bias(X, X)
312
+
313
+ self.sigma.grad = derivative_function(derivative_covariance_sigma).to(dtype=self.sigma.dtype)
314
+ self.sigma_bias.grad = derivative_function(derivative_covariance_sigma_bias).to(dtype=self.sigma_bias.dtype)
315
+
316
+ def parameters(self):
317
+ """
318
+ Yields the parameters of the kernel as a dictionary. If one uses a combination of the kernels, the parameters of each of the child kernels are returned.
319
+
320
+ Returns:
321
+ parameters (dict[str, torch.Tensor]): The parameters as a dictionary. The key of the parameter is eg. "linear_sigma_1".
322
+ """
323
+ return {("linear_sigma" + "_" + str(self.number)): self.sigma, ("linear_sigma_bias" + "_" + str(self.number)): self.sigma_bias}
324
+
325
+ class WhiteGaussian(_Base):
326
+ """
327
+ The white Gaussian kernel, commonly used to capture Gaussian noise in data. This kernel models purely random noise without dependencies on input values. The used equation is:
328
+
329
+ .. math::
330
+
331
+ k(x_i, x_j) = \\sigma^2 \mathbb{1}\{x_i = x_j\},
332
+
333
+ where :math:`\mathbb{1}` is the indicator function and :math:`\\sigma` is the sigma parameter.
334
+
335
+ Args:
336
+ sigma (float, optional): The overall scale factor of the variance. Controls the amplitude of the kernel. Must be a positive real number. Defaults to 1.
337
+ train_sigma (bool, optional): Determines, wheter or not the sigma parameter should be changed during training the kernel. Defaults to True.
338
+ """
339
+
340
+ instance = 0
341
+ """
342
+ :meta private:
343
+ """
344
+ def __init__(self, sigma=1, train_sigma=True):
345
+ if not isinstance(sigma, int | float) or sigma <= 0:
346
+ raise ValueError("sigma must be a positive real number.")
347
+ if not isinstance(train_sigma, bool):
348
+ raise TypeError("train_sigma must be a boolean value.")
349
+
350
+ WhiteGaussian.instance += 1
351
+ self.number = WhiteGaussian.instance
352
+
353
+ self.sigma = torch.tensor([sigma], dtype=torch.float32)
354
+ self.train_sigma = train_sigma
355
+
356
+ def __call__(self, X1, X2):
357
+ """
358
+ :meta public:
359
+
360
+ Yields the kernel matrix between two vectors.
361
+
362
+ Args:
363
+ X1 (torch.Tensor of shape (n_samples_1, n_features))
364
+ X2 (torch.Tensor of shape (n_samples_2, n_features))
365
+
366
+ Returns:
367
+ kernel_matrix (torch.Tensor of shape (n_samples_1, n_samples_2)): The pairwise kernel values between samples from X1 and X2.
368
+
369
+ Raises:
370
+ TypeError: If the input matricies are not a PyTorch tensors.
371
+ ValueError: If the input matricies are not the correct shape.
372
+ """
373
+ if not isinstance(X1, torch.Tensor) or not isinstance(X2, torch.Tensor):
374
+ raise TypeError("The input matricies must be PyTorch tensors.")
375
+ if X1.ndim != 2 or X2.ndim != 2:
376
+ raise ValueError("The input matricies must be 2 dimensional tensors.")
377
+ if X1.shape[1] != X2.shape[1]:
378
+ raise ValueError("X1 and X2 must have the same number of features.")
379
+
380
+ if self.sigma.device != X1.device:
381
+ self.sigma = self.sigma.to(X1.device)
382
+
383
+ covariance_matrix = (X1[:, None] == X2).all(-1).to(dtype=X1.dtype)
384
+ return self.sigma ** 2 * covariance_matrix
385
+
386
+ def derivative_sigma(self, X1, X2):
387
+ """
388
+ :meta private:
389
+ """
390
+ if not self.train_sigma: return torch.zeros((len(X1), len(X2)), dtype=X1.dtype, device=X1.device)
391
+ covariance_matrix = (X1[:, None] == X2).all(-1).to(dtype=X1.dtype)
392
+ val = 2 * self.sigma * covariance_matrix
393
+ # descrete = -self(X1, X2)
394
+ # eps = 1e-6
395
+ # self.sigma += eps
396
+ # descrete += self(X1, X2)
397
+ # self.sigma -= eps
398
+ # print(descrete[0] / eps, val[0])
399
+ return val
400
+
401
+ def update(self, derivative_function, X):
402
+ """
403
+ :meta private:
404
+ """
405
+ derivative_covariance_sigma = self.derivative_sigma(X, X)
406
+
407
+ self.sigma.grad = derivative_function(derivative_covariance_sigma).to(dtype=self.sigma.dtype)
408
+
409
+ def parameters(self):
410
+ """
411
+ Yields the parameters of the kernel as a dictionary. If one uses a combination of the kernels, the parameters of each of the child kernels are returned.
412
+
413
+ Returns:
414
+ parameters (dict[str, torch.Tensor]): The parameters as a dictionary. The key of the parameter is eg. "white_gaussian_sigma_1".
415
+ """
416
+ return {("white_gaussian_sigma" + "_" + str(self.number)): self.sigma}
417
+
418
+ class Periodic(_Base):
419
+ """
420
+ The periodic kernel, commonly used to capture periodic relationships in data, such as seasonal patterns or repeating cycles. The used equation is:
421
+
422
+ .. math::
423
+
424
+ k(x_i, x_j) = \\sigma^2 \\exp\\left(-\\frac{2\\sin^2(\\frac{\pi d(x_i, x_j)}{p})}{l^2}\\right),
425
+
426
+ where :math:`d` is the Euclidian metric and :math:`\\sigma`, :math:`l` and :math:`p` are the sigma, the correlation_length and the period parameters respectively.
427
+
428
+ Args:
429
+ sigma (float, optional): The overall scale factor of the variance. Controls the amplitude of the kernel. Must be a positive real number. Defaults to 1.
430
+ correlation_length (float, optional): Controls how quickly the similarity decays as points move further apart in the input space. Must be a positive real number. Defaults to 1.
431
+ period (float, optional): The period of the kernel, indicating the distance over which the function repeats. Must be a positive real number. Defaults to 1.
432
+ train_sigma (bool, optional): Determines, wheter or not the sigma parameter should be changed during training the kernel. Defaults to True.
433
+ train_correlation_length (bool, optional): Determines, wheter or not the correlation_length parameter should be changed during training the kernel. Defaults to True.
434
+ train_period (bool, optional): Determines, wheter or not the period parameter should be changed during training the kernel. Defaults to True.
435
+ """
436
+
437
+ instance = 0
438
+ """
439
+ :meta private:
440
+ """
441
+
442
+ def __init__(self, sigma=1, correlation_length=1, period=1, train_sigma=True, train_correlation_length=True, train_period=True):
443
+ if not isinstance(sigma, int | float) or sigma <= 0:
444
+ raise ValueError("sigma must be a positive real number.")
445
+ if (not isinstance(correlation_length, int | float) or correlation_length <= 0):
446
+ raise ValueError("correlation_length must be a positive real number.")
447
+ if not isinstance(period, int | float) or period <= 0:
448
+ raise TypeError("period must be a real number.")
449
+ if not isinstance(train_sigma, bool) or not isinstance(train_correlation_length, bool) or not isinstance(train_period, bool):
450
+ raise TypeError("train_sigma, train_correlation_length and train_period must be boolean values.")
451
+
452
+
453
+ Periodic.instance += 1
454
+ self.number = Periodic.instance
455
+
456
+ self.sigma = torch.tensor([sigma], dtype=torch.float32)
457
+ self.correlation_length = correlation_length if isinstance(correlation_length, torch.Tensor) else torch.tensor([correlation_length], dtype=torch.float32)
458
+ self.period = torch.tensor([period], dtype=torch.float32)
459
+
460
+ self.train_sigma = train_sigma
461
+ self.train_correlation_length = train_correlation_length
462
+ self.train_period = train_period
463
+
464
+ def __call__(self, X1, X2):
465
+ """
466
+ :meta public:
467
+
468
+ Yields the kernel matrix between two vectors.
469
+
470
+ Args:
471
+ X1 (torch.Tensor of shape (n_samples_1, n_features))
472
+ X2 (torch.Tensor of shape (n_samples_2, n_features))
473
+
474
+ Returns:
475
+ kernel_matrix (torch.Tensor of shape (n_samples_1, n_samples_2)): The pairwise kernel values between samples from X1 and X2.
476
+
477
+ Raises:
478
+ TypeError: If the input matricies are not a PyTorch tensors.
479
+ ValueError: If the input matricies are not the correct shape.
480
+ """
481
+ if not isinstance(X1, torch.Tensor) or not isinstance(X2, torch.Tensor):
482
+ raise TypeError("The input matricies must be PyTorch tensors.")
483
+ if X1.ndim != 2 or X2.ndim != 2:
484
+ raise ValueError("The input matricies must be 2 dimensional tensors.")
485
+ if X1.shape[1] != X2.shape[1]:
486
+ raise ValueError("X1 and X2 must have the same number of features.")
487
+
488
+ if self.sigma.device != X1.device:
489
+ self.sigma = self.sigma.to(X1.device)
490
+ self.correlation_length = self.correlation_length.to(device=X1.device)
491
+ self.period = self.period.to(device=X1.device)
492
+
493
+ norm = torch.cdist(X1, X2)
494
+ periodic_term = torch.sin(torch.pi * norm / self.period) ** 2
495
+ covariance_matrix = self.sigma ** 2 * torch.exp(-2 * periodic_term / (self.correlation_length ** 2))
496
+ return covariance_matrix
497
+
498
+ def derivative_sigma(self, X1, X2):
499
+ """
500
+ :meta private:
501
+ """
502
+ if not self.train_sigma: return torch.zeros((len(X1), len(X2)), dtype=X1.dtype, device=X1.device)
503
+ val = 2 * self(X1, X2) / self.sigma
504
+ # descrete = -self(X1, X2)
505
+ # eps = 1e-6
506
+ # self.sigma += eps
507
+ # descrete += self(X1, X2)
508
+ # self.sigma -= eps
509
+ # print(descrete[0] / eps, val[0])
510
+ return val
511
+
512
+ def derivative_corr_len(self, X1, X2):
513
+ """
514
+ :meta private:
515
+ """
516
+ if not self.train_correlation_length: return torch.zeros((len(X1), len(X2)), dtype=X1.dtype, device=X1.device)
517
+ norm = torch.cdist(X1, X2)
518
+ periodic_term = torch.sin(torch.pi * norm / self.period) ** 2
519
+ val = 4 * self(X1, X2) * (periodic_term / (self.correlation_length ** 3))
520
+ # descrete = -self(X1, X2)
521
+ # eps = 1e-6
522
+ # self.correlation_length += eps
523
+ # descrete += self(X1, X2)
524
+ # self.correlation_length -= eps
525
+ # print(descrete[0] / eps, val[0])
526
+ return val
527
+
528
+ def derivative_period(self, X1, X2):
529
+ """
530
+ :meta private:
531
+ """
532
+ if not self.train_period: return torch.zeros((len(X1), len(X2)), dtype=X1.dtype, device=X1.device)
533
+ norm = torch.cdist(X1, X2)
534
+ periodic_term = torch.sin(torch.pi * norm / self.period)
535
+ val = 4 * self(X1, X2) * (periodic_term * torch.cos(torch.pi * norm / self.period) * (torch.pi * norm / self.period ** 2) / (self.correlation_length ** 2))
536
+ # descrete = -self(X1, X2)
537
+ # eps = 1e-6
538
+ # self.period += eps
539
+ # descrete += self(X1, X2)
540
+ # self.period -= eps
541
+ # print(descrete[0] / eps, val[0])
542
+ return val
543
+
544
+ def update(self, derivative_function, X):
545
+ """
546
+ :meta private:
547
+ """
548
+ derivative_covariance_sigma = self.derivative_sigma(X, X)
549
+ derivative_covariance_corr_len = self.derivative_corr_len(X, X)
550
+ derivative_covariance_period = self.derivative_period(X, X)
551
+
552
+ self.sigma.grad = derivative_function(derivative_covariance_sigma).to(dtype=self.sigma.dtype)
553
+ self.correlation_length.grad = derivative_function(derivative_covariance_corr_len).to(dtype=self.correlation_length.dtype)
554
+ self.period.grad = derivative_function(derivative_covariance_period).to(dtype=self.period.dtype)
555
+
556
+ def parameters(self):
557
+ """
558
+ Yields the parameters of the kernel as a dictionary. If one uses a combination of the kernels, the parameters of each of the child kernels are returned.
559
+
560
+ Returns:
561
+ parameters (dict[str, torch.Tensor]): The parameters as a dictionary. The key of the parameter is eg. "periodic_sigma_1".
562
+ """
563
+ return {("periodic_sigma" + "_" + str(self.number)): self.sigma, ("periodic_corr_len" + "_" + str(self.number)): self.correlation_length, ("periodic_period" + "_" + str(self.number)): self.period}
564
+
565
+ class RationalQuadratic(_Base):
566
+ """
567
+ The rational quadratic kernel, a versatile kernel often used in Gaussian Processes for modeling data with varying degrees of smoothness. It can be seen as a scale mixture of the squared exponential kernel, allowing flexibility between linear and non-linear relationships. The used equation is:
568
+
569
+ .. math::
570
+
571
+ k(x_i, x_j) = \\sigma^2 \\left(1 + \\frac{d(x_i, x_j)^2}{2\\alpha l^2} \\right)^{-\\alpha},
572
+
573
+ where :math:`d` is the Euclidian metric and :math:`\\sigma`, :math:`l` and :math:`\\alpha` are the sigma, correlation_length and alpha parameters respectively.
574
+
575
+ Args:
576
+ sigma (float, optional): The overall scale factor of the variance. Controls the amplitude of the kernel. Must be a positive real number. Defaults to 1.
577
+ correlation_length (float, optional): Controls how quickly the similarity decays as points move further apart in the input space. Must be a positive real number. Defaults to 1.
578
+ alpha (float, optional): Controls the relative weighting of large-scale and small-scale variations. Higher values make the kernel behave more like a squared exponential (Gaussian) kernel, while lower values allow for more flexibility. Must be a positive real number. Defaults to 1.
579
+ train_sigma (bool, optional): Determines, wheter or not the sigma parameter should be changed during training the kernel. Defaults to True.
580
+ train_correlation_length (bool, optional): Determines, wheter or not the correlation_length parameter should be changed during training the kernel. Defaults to True.
581
+ train_alpha (bool, optional): Determines, wheter or not the alpha parameter should be changed during training the kernel. Defaults to True.
582
+ """
583
+
584
+ instance = 0
585
+ """
586
+ :meta private:
587
+ """
588
+
589
+ def __init__(self, sigma=1, correlation_length=1, alpha=1, train_sigma=True, train_correlation_length=True, train_alpha=True):
590
+ if not isinstance(sigma, int | float) or sigma <= 0:
591
+ raise ValueError("sigma must be a positive real number.")
592
+ if not isinstance(correlation_length, int | float) or correlation_length <= 0:
593
+ raise TypeError("correlation_length must be a real number.")
594
+ if not isinstance(alpha, int | float) or alpha <= 0:
595
+ raise TypeError("alpha must be a real number.")
596
+ if not isinstance(train_sigma, bool) or not isinstance(train_correlation_length, bool) or not isinstance(train_alpha, bool):
597
+ raise TypeError("train_sigma, train_correlation_length and train_alpha must be boolean values.")
598
+
599
+ RationalQuadratic.instance += 1
600
+ self.number = RationalQuadratic.instance
601
+
602
+ self.sigma = torch.tensor([sigma], dtype=torch.float32)
603
+ self.correlation_length = torch.tensor([correlation_length], dtype=torch.float32)
604
+ self.alpha = torch.tensor([alpha], dtype=torch.float32)
605
+
606
+ self.train_sigma = train_sigma
607
+ self.train_correlation_length = train_correlation_length
608
+ self.train_alpha = train_alpha
609
+
610
+ def __call__(self, X1, X2):
611
+ """
612
+ :meta public:
613
+
614
+ Yields the kernel matrix between two vectors.
615
+
616
+ Args:
617
+ X1 (torch.Tensor of shape (n_samples_1, n_features))
618
+ X2 (torch.Tensor of shape (n_samples_2, n_features))
619
+
620
+ Returns:
621
+ kernel_matrix (torch.Tensor of shape (n_samples_1, n_samples_2)): The pairwise kernel values between samples from X1 and X2.
622
+
623
+ Raises:
624
+ TypeError: If the input matricies are not a PyTorch tensors.
625
+ ValueError: If the input matricies are not the correct shape.
626
+ """
627
+ if not isinstance(X1, torch.Tensor) or not isinstance(X2, torch.Tensor):
628
+ raise TypeError("The input matricies must be PyTorch tensors.")
629
+ if X1.ndim != 2 or X2.ndim != 2:
630
+ raise ValueError("The input matricies must be 2 dimensional tensors.")
631
+ if X1.shape[1] != X2.shape[1]:
632
+ raise ValueError("X1 and X2 must have the same number of features.")
633
+
634
+ if self.sigma.device != X1.device:
635
+ self.sigma = self.sigma.to(X1.device)
636
+ self.correlation_length = self.correlation_length.to(X1.device)
637
+ self.alpha = self.alpha.to(X1.device)
638
+
639
+ norm_squared = torch.cdist(X1, X2) ** 2
640
+ return self.sigma ** 2 * (1 + norm_squared / (2 * self.alpha * self.correlation_length ** 2)) ** -self.alpha
641
+
642
+ def derivative_sigma(self, X1, X2):
643
+ """
644
+ :meta private:
645
+ """
646
+ if not self.train_sigma: return torch.zeros((len(X1), len(X2)), dtype=X1.dtype, device=X1.device)
647
+ val = 2 * self(X1, X2) / self.sigma
648
+ # descrete = -self(X1, X2)
649
+ # eps = 1e-6
650
+ # self.sigma += eps
651
+ # descrete += self(X1, X2)
652
+ # self.sigma -= eps
653
+ # print(descrete[0] / eps, val[0])
654
+ return val
655
+
656
+ def derivative_corr_len(self, X1, X2):
657
+ """
658
+ :meta private:
659
+ """
660
+ if not self.train_correlation_length: return torch.zeros((len(X1), len(X2)), dtype=X1.dtype, device=X1.device)
661
+ norm_squared = torch.cdist(X1, X2) ** 2
662
+ val = (self.sigma ** 2 *
663
+ (1 + norm_squared / (2 * self.alpha * self.correlation_length ** 2)) ** (-self.alpha - 1) *
664
+ (norm_squared / (self.correlation_length ** 3)))
665
+ # descrete = -self(X1, X2)
666
+ # eps = 1e-6
667
+ # self.correlation_length += eps
668
+ # descrete += self(X1, X2)
669
+ # self.correlation_length -= eps
670
+ # print(descrete[0] / eps, val[0])
671
+ return val
672
+
673
+ def derivative_alpha(self, X1, X2):
674
+ """
675
+ :meta private:
676
+ """
677
+ if not self.train_alpha: return torch.zeros((len(X1), len(X2)), dtype=X1.dtype, device=X1.device)
678
+ norm_squared = torch.cdist(X1, X2) ** 2
679
+ term = 1 + norm_squared / (2 * self.alpha * self.correlation_length ** 2)
680
+ val = (self(X1, X2) *
681
+ (norm_squared / (2 * self.alpha * self.correlation_length ** 2 + norm_squared) -
682
+ torch.log(term)))
683
+ # descrete = -self(X1, X2)
684
+ # eps = 1e-6
685
+ # self.alpha += eps
686
+ # descrete += self(X1, X2)
687
+ # self.alpha -= eps
688
+ # print(descrete[0] / eps, val[0])
689
+ return val
690
+
691
+ def update(self, derivative_function, X):
692
+ """
693
+ :meta private:
694
+ """
695
+ derivative_covariance_sigma = self.derivative_sigma(X, X)
696
+ derivative_covariance_corr_len = self.derivative_corr_len(X, X)
697
+ derivative_covariance_alpha = self.derivative_alpha(X, X)
698
+
699
+ self.sigma.grad = derivative_function(derivative_covariance_sigma).to(dtype=self.sigma.dtype)
700
+ self.correlation_length.grad = derivative_function(derivative_covariance_corr_len).to(dtype=self.correlation_length.dtype)
701
+ self.alpha.grad = derivative_function(derivative_covariance_alpha).to(dtype=self.alpha.dtype)
702
+
703
+ def parameters(self):
704
+ """
705
+ Yields the parameters of the kernel as a dictionary. If one uses a combination of the kernels, the parameters of each of the child kernels are returned.
706
+
707
+ Returns:
708
+ parameters (dict[str, torch.Tensor]): The parameters as a dictionary. The key of the parameter is eg. "rational_quadratic_sigma_1".
709
+ """
710
+ return {("rational_quadratic_sigma" + "_" + str(self.number)): self.sigma, ("rational_quadratic_corr_len" + "_" + str(self.number)): self.correlation_length, ("rational_quadratic_alpha" + "_" + str(self.number)): self.alpha}
711
+
712
+ class Matern(_Base):
713
+ """
714
+ The Matern kernel, a versatile kernel often used in Gaussian Processes for modeling data with varying degrees of smoothness. Is a generalization of the RBF kernel with varying levels of smoothness controlled by nu. The used equation is:
715
+
716
+ .. math::
717
+
718
+ k(x_i, x_j) = \\sigma^2 \\frac{2^{1-\\nu}}{\Gamma(\\nu)} \\left(\\sqrt{2\\nu}\\frac{d(x_i, x_j)}{l}\\right)^\\nu K_\\nu\\left(\\sqrt{2\\nu}\\frac{d(x_i, x_j)}{l}\\right),
719
+
720
+ where :math:`d` is the Euclidian metric, :math:`\Gamma` is the `gamma function <https://en.wikipedia.org/wiki/Gamma_function>`_, :math:`K_\\nu` is the `modified Bessel function of the second kind <https://docs.scipy.org/doc/scipy/reference/generated/scipy.special.kv.html>`_ and :math:`\\sigma`, :math:`l` and :math:`\\nu` are the sigma, correlation_length and nu parameters respectively.
721
+
722
+ Args:
723
+ sigma (float, optional): The overall scale factor of the variance. Controls the amplitude of the kernel. Must be a positive real number. Defaults to 1.
724
+ correlation_length (float, optional): Controls how quickly the similarity decays as points move further apart in the input space. Must be a positive real number. Defaults to 1.
725
+ nu (float, optional): Controls the smoothness of the kernel. Important values of nu include 0.5 for the rbf kernel with the l1 norm modelling non differentiable functions, 1.5 for once differentiable functions and 2.5 for twice differentiable functions. If is set to float("inf"), the kernel is equivalent to the RBF kernel. It is not possible to train nu. Must be a positive real number. Defaults to 1.5.
726
+ train_sigma (bool, optional): Determines, wheter or not the sigma parameter should be changed during training the kernel. Defaults to True.
727
+ train_correlation_length (bool, optional): Determines, wheter or not the correlation_length parameter should be changed during training the kernel. Defaults to True.
728
+ """
729
+
730
+ instance = 0
731
+ """
732
+ :meta private:
733
+ """
734
+
735
+ def __new__(cls, sigma=1, correlation_length=1, nu=1.5, train_sigma=True, train_correlation_length=True):
736
+ # if nu is infinite, the kernel is equivalent to the RBF kernel
737
+ if nu == float("inf"):
738
+ return RBF(sigma, correlation_length, train_sigma, train_correlation_length)
739
+ return super().__new__(cls)
740
+
741
+ def __init__(self, sigma=1, correlation_length=1, nu=1, zero_d_eps=1e-5, train_sigma=True, train_correlation_length=True):
742
+ if not isinstance(sigma, int | float) or sigma <= 0:
743
+ raise ValueError("sigma must be a positive real number.")
744
+ if not isinstance(correlation_length, int | float) or correlation_length <= 0:
745
+ raise TypeError("correlation_length must be a real number.")
746
+ if not isinstance(nu, int | float) or nu <= 0:
747
+ raise TypeError("nu must be a real number.")
748
+ if not isinstance(train_sigma, bool) or not isinstance(train_correlation_length, bool):
749
+ raise TypeError("train_sigma and train_correlation_length must be boolean values.")
750
+
751
+ RationalQuadratic.instance += 1
752
+ self.number = RationalQuadratic.instance
753
+
754
+ self.sigma = torch.tensor([sigma], dtype=torch.float32)
755
+ self.correlation_length = torch.tensor([correlation_length], dtype=torch.float32)
756
+ self.nu = torch.tensor([nu], dtype=torch.float32)
757
+
758
+ self.zero_d_eps = zero_d_eps
759
+ self._gamma_val = gamma(nu)
760
+
761
+ self.train_sigma = train_sigma
762
+ self.train_correlation_length = train_correlation_length
763
+
764
+ def __call__(self, X1, X2):
765
+ """
766
+ :meta public:
767
+
768
+ Yields the kernel matrix between two vectors.
769
+
770
+ Args:
771
+ X1 (torch.Tensor of shape (n_samples_1, n_features))
772
+ X2 (torch.Tensor of shape (n_samples_2, n_features))
773
+
774
+ Returns:
775
+ kernel_matrix (torch.Tensor of shape (n_samples_1, n_samples_2)): The pairwise kernel values between samples from X1 and X2.
776
+
777
+ Raises:
778
+ TypeError: If the input matricies are not a PyTorch tensors.
779
+ ValueError: If the input matricies are not the correct shape.
780
+ """
781
+ if not isinstance(X1, torch.Tensor) or not isinstance(X2, torch.Tensor):
782
+ raise TypeError("The input matricies must be PyTorch tensors.")
783
+ if X1.ndim != 2 or X2.ndim != 2:
784
+ raise ValueError("The input matricies must be 2 dimensional tensors.")
785
+ if X1.shape[1] != X2.shape[1]:
786
+ raise ValueError("X1 and X2 must have the same number of features.")
787
+
788
+ if self.sigma.device != X1.device:
789
+ self.sigma = self.sigma.to(X1.device)
790
+ self.correlation_length = self.correlation_length.to(X1.device)
791
+ self.nu = self.nu.to(X1.device)
792
+
793
+ norm = torch.cdist(X1, X2)
794
+ y = torch.sqrt(2 * self.nu) * norm / self.correlation_length + self.zero_d_eps
795
+ bessel_factor = kv(self.nu, y)
796
+ constant_factor = 2 ** (1 - self.nu) / self._gamma_val
797
+ other_factor = y ** self.nu
798
+ return self.sigma ** 2 * constant_factor * other_factor * bessel_factor
799
+
800
+ def derivative_sigma(self, X1, X2):
801
+ """
802
+ :meta private:
803
+ """
804
+ if not self.train_sigma: return torch.zeros((len(X1), len(X2)), dtype=X1.dtype, device=X1.device)
805
+ val = 2 * self(X1, X2) / self.sigma
806
+ # descrete = -self(X1, X2)
807
+ # eps = 1e-6
808
+ # self.sigma += eps
809
+ # descrete += self(X1, X2)
810
+ # self.sigma -= eps
811
+ # print(descrete[0] / eps, val[0])
812
+ return val
813
+
814
+ def derivative_corr_len(self, X1, X2):
815
+ """
816
+ :meta private:
817
+ """
818
+ if not self.train_correlation_length: return torch.zeros((len(X1), len(X2)), dtype=X1.dtype, device=X1.device)
819
+ # derivatives from wolfram mathematica
820
+ norm = torch.cdist(X1, X2)
821
+ y = torch.sqrt(2 * self.nu) * norm / self.correlation_length + self.zero_d_eps
822
+ val = -((2 ** (-self.nu / 2) * (norm * torch.sqrt(self.nu) / self.correlation_length) ** self.nu * torch.sqrt(self.nu) * self.sigma ** 2 *
823
+ (-sqrt(2) * norm * kv(self.nu - 1, y) + 2 * self.correlation_length * kv(self.nu, y) - sqrt(2) * norm * kv(self.nu + 1, y))
824
+ )) / (self.correlation_length ** 2 * self._gamma_val)
825
+ # descrete = -self(X1, X2)
826
+ # eps = 1e-6
827
+ # self.correlation_length += eps
828
+ # descrete += self(X1, X2)
829
+ # self.correlation_length -= eps
830
+ # print(descrete[0] / eps, val[0])
831
+ return val
832
+
833
+ def update(self, derivative_function, X):
834
+ """
835
+ :meta private:
836
+ """
837
+ derivative_covariance_sigma = self.derivative_sigma(X, X)
838
+ derivative_covariance_corr_len = self.derivative_corr_len(X, X)
839
+
840
+ self.sigma.grad = derivative_function(derivative_covariance_sigma).to(dtype=self.sigma.dtype)
841
+ self.correlation_length.grad = derivative_function(derivative_covariance_corr_len).to(dtype=self.correlation_length.dtype)
842
+
843
+ def parameters(self):
844
+ """
845
+ Yields the parameters of the kernel as a dictionary. If one uses a combination of the kernels, the parameters of each of the child kernels are returned.
846
+
847
+ Returns:
848
+ parameters (dict[str, torch.Tensor]): The parameters as a dictionary. The key of the parameter is eg. "matern_sigma_1".
849
+ """
850
+ return {("matern_sigma" + "_" + str(self.number)): self.sigma, ("matern_corr_len" + "_" + str(self.number)): self.correlation_length}
851
+