I´m creating Kernel classes for Gaussian Processes. Firstly, I created a class "Kernel" that specifies some basic things that every Kernel object must have (still needs improvement).
class Kernel:
def __init__(self, hypers, sigma_n=None):
self.hypers = hypers
self.sigma_n = sigma_n
def show_hypers(self):
for key in self.hypers.keys():
print(f"{key}: {self.hypers[key]}")
if self.sigma_n:
print(f"sigma_n: {self.sigma_n}")
def outersum(self, x,y):
return np.outer(x,np.ones_like(y))+np.outer(np.ones_like(x),y)
def gram(self,x,y,with_noise=True):
pass
So, in particular I'm creating a SpecMix class that is a specific type of kernel which inherits from Kernel. In it, I define how gram is computed (returns a matrix):
class SpecMix(Kernel):
def __init__(self, hypers, sigma_n=None):
super().__init__(hypers, sigma_n=sigma_n)
possible_hypers = ['gamma', 'mu', 'sigma']
for key in hypers.keys():
if key in possible_hypers:
continue
else:
raise Exception(f"'{key}' is not a possible hyperparameter")
def gram(self, x, y, with_noise=True):
Gram = self.hypers['sigma']**2 * np.exp(-self.hypers['gamma']*self.outersum(x,-y)**2)*\
np.cos(2*np.pi*self.hypers['mu']*self.outersum(x,-y))
if with_noise:
if self.sigma_n is not None:
Gram = Gram+self.sigma_n**2*np.eye(len(x))
return Gram
Here is in example of how to instantiate and use an object of type SpecMix:
hypers0 = {'gamma':0.5, 'mu':0.1, 'sigma':0.3}
sigma_n = 0.001
x = np.array([1,2,3])
y = np.array([4,3,0])
spec_mix = SpecMix(hypers0, sigma_n=sigma_n)
spec_mix.gram(x,y)
Now, a usual practice for kernels is to mix different kernel, for example by multiplying them. So, let's say I define another Kernel called "Periodic" with it's unique gram method, I would like something using magic methods that makes the multiplication of two kernels a new kernel object with its own gram method:
spec_mix = SpecMix(hypers0, sigma_n=sigma_n1)
periodic = Periodic(hypers1, sigma_n=sigma_n2)
mult_kernels = spec_mix * periodic
I've tried doing this using __mul__
, but I don´t know how to make the new object mult_kernels have a gram method that is the multiplication of the grams from spec_mix and periodic. In other words, I would like that:
mult_kernels.gram(x,y)
and
spec_mix.gram(x,y) * periodic.gram(x,y)
be the same matrix. (Note that it's point-wise matrix multiplication). I've seen setattr
to redefine a method from an object, and thought maybe I could use it but got stuck in the implementation. Actually I made some code to try it out:
spec_mix = SpecMix(hypers0, sigma_n=sigma_n)
kernel = Kernel(hypers0, sigma_n=sigma_n)
setattr(kernel, 'gram', spec_mix.gram)
And this made the kernel's gram method equal to spec_mix's gram method, but I need the gram method to be the multiplication of two other gram methods and doing this and inside a magic method is where I'm having problem. Maybe it's not the correct approach, but it's what I've tried so far.
Thanks in advance. I'm open to any help :D