GPy/GPy/core/parameterization/priors.py

269 lines
8.6 KiB
Python
Raw Normal View History

# Copyright (c) 2012, GPy authors (see AUTHORS.txt).
# Licensed under the BSD 3-clause license (see LICENSE.txt)
2012-11-29 16:25:27 +00:00
import numpy as np
import pylab as pb
from scipy.special import gammaln, digamma
from ...util.linalg import pdinv
from domains import _REAL, _POSITIVE
2013-06-04 17:03:29 +01:00
import warnings
import weakref
2012-11-29 16:25:27 +00:00
class Prior:
2013-06-04 17:03:29 +01:00
domain = None
2013-06-04 17:03:29 +01:00
def pdf(self, x):
2013-01-27 18:12:29 +00:00
return np.exp(self.lnpdf(x))
2013-01-27 18:23:19 +00:00
2013-01-27 18:12:29 +00:00
def plot(self):
rvs = self.rvs(1000)
2013-06-04 17:03:29 +01:00
pb.hist(rvs, 100, normed=True)
xmin, xmax = pb.xlim()
xx = np.linspace(xmin, xmax, 1000)
pb.plot(xx, self.pdf(xx), 'r', linewidth=2)
2012-11-29 16:25:27 +00:00
2013-01-27 18:23:19 +00:00
class Gaussian(Prior):
2013-01-27 18:12:29 +00:00
"""
2013-01-27 18:23:19 +00:00
Implementation of the univariate Gaussian probability function, coupled with random variables.
:param mu: mean
:param sigma: standard deviation
.. Note:: Bishop 2006 notation is used throughout the code
"""
domain = _REAL
_instances = []
def __new__(cls, mu, sigma): # Singleton:
if cls._instances:
cls._instances[:] = [instance for instance in cls._instances if instance()]
for instance in cls._instances:
if instance().mu == mu and instance().sigma == sigma:
return instance()
o = super(Prior, cls).__new__(cls, mu, sigma)
cls._instances.append(weakref.ref(o))
return cls._instances[-1]()
2013-06-04 17:03:29 +01:00
def __init__(self, mu, sigma):
2013-01-27 18:12:29 +00:00
self.mu = float(mu)
self.sigma = float(sigma)
self.sigma2 = np.square(self.sigma)
2013-06-04 17:03:29 +01:00
self.constant = -0.5 * np.log(2 * np.pi * self.sigma2)
2013-01-27 18:23:19 +00:00
2013-01-27 18:12:29 +00:00
def __str__(self):
2013-06-04 17:03:29 +01:00
return "N(" + str(np.round(self.mu)) + ', ' + str(np.round(self.sigma2)) + ')'
2013-01-27 18:23:19 +00:00
2013-06-04 17:03:29 +01:00
def lnpdf(self, x):
return self.constant - 0.5 * np.square(x - self.mu) / self.sigma2
2013-01-27 18:23:19 +00:00
2013-06-04 17:03:29 +01:00
def lnpdf_grad(self, x):
return -(x - self.mu) / self.sigma2
2013-01-27 18:23:19 +00:00
2013-06-04 17:03:29 +01:00
def rvs(self, n):
return np.random.randn(n) * self.sigma + self.mu
2012-11-29 16:25:27 +00:00
2013-01-27 18:23:19 +00:00
class LogGaussian(Prior):
2013-01-27 18:12:29 +00:00
"""
2013-01-27 18:23:19 +00:00
Implementation of the univariate *log*-Gaussian probability function, coupled with random variables.
:param mu: mean
:param sigma: standard deviation
.. Note:: Bishop 2006 notation is used throughout the code
2013-01-27 18:12:29 +00:00
"""
domain = _POSITIVE
_instances = []
def __new__(cls, mu, sigma): # Singleton:
if cls._instances:
cls._instances[:] = [instance for instance in cls._instances if instance()]
for instance in cls._instances:
if instance().mu == mu and instance().sigma == sigma:
return instance()
o = super(Prior, cls).__new__(cls, mu, sigma)
cls._instances.append(weakref.ref(o))
return cls._instances[-1]()
2013-06-04 17:03:29 +01:00
def __init__(self, mu, sigma):
2013-01-27 18:12:29 +00:00
self.mu = float(mu)
self.sigma = float(sigma)
self.sigma2 = np.square(self.sigma)
2013-06-04 17:03:29 +01:00
self.constant = -0.5 * np.log(2 * np.pi * self.sigma2)
2013-01-27 18:23:19 +00:00
2013-01-27 18:12:29 +00:00
def __str__(self):
2013-06-04 17:03:29 +01:00
return "lnN(" + str(np.round(self.mu)) + ', ' + str(np.round(self.sigma2)) + ')'
2013-01-27 18:23:19 +00:00
2013-06-04 17:03:29 +01:00
def lnpdf(self, x):
return self.constant - 0.5 * np.square(np.log(x) - self.mu) / self.sigma2 - np.log(x)
2013-01-27 18:23:19 +00:00
2013-06-04 17:03:29 +01:00
def lnpdf_grad(self, x):
return -((np.log(x) - self.mu) / self.sigma2 + 1.) / x
2013-01-27 18:23:19 +00:00
2013-06-04 17:03:29 +01:00
def rvs(self, n):
return np.exp(np.random.randn(n) * self.sigma + self.mu)
2012-11-29 16:25:27 +00:00
2013-01-27 18:23:19 +00:00
class MultivariateGaussian:
2013-01-27 18:12:29 +00:00
"""
2013-01-27 18:23:19 +00:00
Implementation of the multivariate Gaussian probability function, coupled with random variables.
:param mu: mean (N-dimensional array)
:param var: covariance matrix (NxN)
.. Note:: Bishop 2006 notation is used throughout the code
"""
domain = _REAL
_instances = []
def __new__(cls, mu, var): # Singleton:
if cls._instances:
cls._instances[:] = [instance for instance in cls._instances if instance()]
for instance in cls._instances:
if np.all(instance().mu == mu) and np.all(instance().var == var):
return instance()
o = super(Prior, cls).__new__(cls, mu, var)
cls._instances.append(weakref.ref(o))
return cls._instances[-1]()
2013-06-04 17:03:29 +01:00
def __init__(self, mu, var):
2013-01-27 18:12:29 +00:00
self.mu = np.array(mu).flatten()
self.var = np.array(var)
2013-06-04 17:03:29 +01:00
assert len(self.var.shape) == 2
assert self.var.shape[0] == self.var.shape[1]
assert self.var.shape[0] == self.mu.size
self.input_dim = self.mu.size
2013-01-27 18:12:29 +00:00
self.inv, self.hld = pdinv(self.var)
self.constant = -0.5 * self.input_dim * np.log(2 * np.pi) - self.hld
2012-11-29 16:25:27 +00:00
2013-01-27 18:12:29 +00:00
def summary(self):
2013-01-27 18:23:19 +00:00
raise NotImplementedError
2013-06-04 17:03:29 +01:00
def pdf(self, x):
2013-01-27 18:12:29 +00:00
return np.exp(self.lnpdf(x))
2013-01-27 18:23:19 +00:00
2013-06-04 17:03:29 +01:00
def lnpdf(self, x):
d = x - self.mu
return self.constant - 0.5 * np.sum(d * np.dot(d, self.inv), 1)
2013-01-27 18:23:19 +00:00
2013-06-04 17:03:29 +01:00
def lnpdf_grad(self, x):
d = x - self.mu
return -np.dot(self.inv, d)
2013-01-27 18:23:19 +00:00
2013-06-04 17:03:29 +01:00
def rvs(self, n):
return np.random.multivariate_normal(self.mu, self.var, n)
2013-01-27 18:23:19 +00:00
2013-01-27 18:12:29 +00:00
def plot(self):
if self.input_dim == 2:
2013-01-27 18:12:29 +00:00
rvs = self.rvs(200)
2013-06-04 17:03:29 +01:00
pb.plot(rvs[:, 0], rvs[:, 1], 'kx', mew=1.5)
xmin, xmax = pb.xlim()
ymin, ymax = pb.ylim()
2013-01-27 18:12:29 +00:00
xx, yy = np.mgrid[xmin:xmax:100j, ymin:ymax:100j]
2013-06-04 17:03:29 +01:00
xflat = np.vstack((xx.flatten(), yy.flatten())).T
zz = self.pdf(xflat).reshape(100, 100)
pb.contour(xx, yy, zz, linewidths=2)
2012-11-29 16:25:27 +00:00
2013-06-04 17:03:29 +01:00
def gamma_from_EV(E, V):
warnings.warn("use Gamma.from_EV to create Gamma Prior", FutureWarning)
return Gamma.from_EV(E, V)
2012-11-29 16:25:27 +00:00
class Gamma(Prior):
2013-01-27 18:12:29 +00:00
"""
2013-01-27 18:23:19 +00:00
Implementation of the Gamma probability function, coupled with random variables.
:param a: shape parameter
:param b: rate parameter (warning: it's the *inverse* of the scale)
.. Note:: Bishop 2006 notation is used throughout the code
2013-01-27 18:12:29 +00:00
"""
domain = _POSITIVE
_instances = []
def __new__(cls, a, b): # Singleton:
if cls._instances:
cls._instances[:] = [instance for instance in cls._instances if instance()]
for instance in cls._instances:
if instance().a == a and instance().b == b:
return instance()
o = super(Prior, cls).__new__(cls, a, b)
cls._instances.append(weakref.ref(o))
return cls._instances[-1]()
2013-06-04 17:03:29 +01:00
def __init__(self, a, b):
2013-01-27 18:12:29 +00:00
self.a = float(a)
self.b = float(b)
2013-06-04 17:03:29 +01:00
self.constant = -gammaln(self.a) + a * np.log(b)
2013-01-27 18:23:19 +00:00
2013-01-27 18:12:29 +00:00
def __str__(self):
2013-06-04 17:03:29 +01:00
return "Ga(" + str(np.round(self.a)) + ', ' + str(np.round(self.b)) + ')'
2013-01-27 18:23:19 +00:00
2013-01-27 18:12:29 +00:00
def summary(self):
2013-06-04 17:03:29 +01:00
ret = {"E[x]": self.a / self.b, \
"E[ln x]": digamma(self.a) - np.log(self.b), \
"var[x]": self.a / self.b / self.b, \
"Entropy": gammaln(self.a) - (self.a - 1.) * digamma(self.a) - np.log(self.b) + self.a}
if self.a > 1:
ret['Mode'] = (self.a - 1.) / self.b
2013-01-27 18:12:29 +00:00
else:
ret['mode'] = np.nan
return ret
2013-01-27 18:23:19 +00:00
2013-06-04 17:03:29 +01:00
def lnpdf(self, x):
return self.constant + (self.a - 1) * np.log(x) - self.b * x
2013-01-27 18:23:19 +00:00
2013-06-04 17:03:29 +01:00
def lnpdf_grad(self, x):
return (self.a - 1.) / x - self.b
2013-01-27 18:23:19 +00:00
2013-06-04 17:03:29 +01:00
def rvs(self, n):
return np.random.gamma(scale=1. / self.b, shape=self.a, size=n)
@staticmethod
def from_EV(E, V):
"""
Creates an instance of a Gamma Prior by specifying the Expected value(s)
and Variance(s) of the distribution.
:param E: expected value
:param V: variance
"""
a = np.square(E) / V
b = E / V
return Gamma(a, b)
class inverse_gamma(Prior):
2013-05-20 11:03:35 +01:00
"""
Implementation of the inverse-Gamma probability function, coupled with random variables.
:param a: shape parameter
:param b: rate parameter (warning: it's the *inverse* of the scale)
.. Note:: Bishop 2006 notation is used throughout the code
"""
domain = _POSITIVE
def __new__(cls, a, b): # Singleton:
if cls._instances:
cls._instances[:] = [instance for instance in cls._instances if instance()]
for instance in cls._instances:
if instance().a == a and instance().b == b:
return instance()
o = super(Prior, cls).__new__(cls, a, b)
cls._instances.append(weakref.ref(o))
return cls._instances[-1]()
2013-06-04 17:03:29 +01:00
def __init__(self, a, b):
2013-05-20 11:03:35 +01:00
self.a = float(a)
self.b = float(b)
2013-06-04 17:03:29 +01:00
self.constant = -gammaln(self.a) + a * np.log(b)
2013-05-20 11:03:35 +01:00
def __str__(self):
2013-06-04 17:03:29 +01:00
return "iGa(" + str(np.round(self.a)) + ', ' + str(np.round(self.b)) + ')'
2013-05-20 11:03:35 +01:00
2013-06-04 17:03:29 +01:00
def lnpdf(self, x):
return self.constant - (self.a + 1) * np.log(x) - self.b / x
2013-05-20 11:03:35 +01:00
2013-06-04 17:03:29 +01:00
def lnpdf_grad(self, x):
return -(self.a + 1.) / x + self.b / x ** 2
2013-05-20 11:03:35 +01:00
2013-06-04 17:03:29 +01:00
def rvs(self, n):
return 1. / np.random.gamma(scale=1. / self.b, shape=self.a, size=n)