0

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

wernergh9
  • 21
  • 4
  • Show us your `def __mul__` code, please. https://docs.python.org/3/library/operator.html#operator.mul First statement should probably be a debug print that logs the input args. https://stackoverflow.com/help/minimal-reproducible-example Maybe what you're looking for is an object that combines x and y, so all needed elements are conveniently available to the operator? – J_H Nov 18 '22 at 01:44

0 Answers0