1# Copyright (c) 2012-2015 The GPy authors (see AUTHORS.txt) 2# Licensed under the BSD 3-clause license (see LICENSE.txt) 3 4import numpy as np 5import scipy 6from ..util.univariate_Gaussian import std_norm_cdf, std_norm_pdf 7import scipy as sp 8from ..util.misc import safe_exp, safe_square, safe_cube, safe_quad, safe_three_times 9 10class GPTransformation(object): 11 """ 12 Link function class for doing non-Gaussian likelihoods approximation 13 14 :param Y: observed output (Nx1 numpy.darray) 15 16 .. note:: Y values allowed depend on the likelihood_function used 17 18 """ 19 def __init__(self): 20 pass 21 22 def transf(self,f): 23 """ 24 Gaussian process tranformation function, latent space -> output space 25 """ 26 raise NotImplementedError 27 28 def dtransf_df(self,f): 29 """ 30 derivative of transf(f) w.r.t. f 31 """ 32 raise NotImplementedError 33 34 def d2transf_df2(self,f): 35 """ 36 second derivative of transf(f) w.r.t. f 37 """ 38 raise NotImplementedError 39 40 def d3transf_df3(self,f): 41 """ 42 third derivative of transf(f) w.r.t. f 43 """ 44 raise NotImplementedError 45 46 def to_dict(self): 47 raise NotImplementedError 48 49 def _save_to_input_dict(self): 50 return {} 51 52 @staticmethod 53 def from_dict(input_dict): 54 """ 55 Instantiate an object of a derived class using the information 56 in input_dict (built by the to_dict method of the derived class). 57 More specifically, after reading the derived class from input_dict, 58 it calls the method _build_from_input_dict of the derived class. 59 Note: This method should not be overrided in the derived class. In case 60 it is needed, please override _build_from_input_dict instate. 61 62 :param dict input_dict: Dictionary with all the information needed to 63 instantiate the object. 64 """ 65 66 import copy 67 input_dict = copy.deepcopy(input_dict) 68 link_class = input_dict.pop('class') 69 import GPy 70 link_class = eval(link_class) 71 return link_class._build_from_input_dict(link_class, input_dict) 72 73 @staticmethod 74 def _build_from_input_dict(link_class, input_dict): 75 return link_class(**input_dict) 76 77class Identity(GPTransformation): 78 """ 79 .. math:: 80 81 g(f) = f 82 83 """ 84 def transf(self,f): 85 return f 86 87 def dtransf_df(self,f): 88 return np.ones_like(f) 89 90 def d2transf_df2(self,f): 91 return np.zeros_like(f) 92 93 def d3transf_df3(self,f): 94 return np.zeros_like(f) 95 96 def to_dict(self): 97 """ 98 Convert the object into a json serializable dictionary. 99 100 Note: It uses the private method _save_to_input_dict of the parent. 101 102 :return dict: json serializable dictionary containing the needed information to instantiate the object 103 """ 104 105 input_dict = super(Identity, self)._save_to_input_dict() 106 input_dict["class"] = "GPy.likelihoods.link_functions.Identity" 107 return input_dict 108 109class Probit(GPTransformation): 110 """ 111 .. math:: 112 113 g(f) = \\Phi^{-1} (mu) 114 115 """ 116 def transf(self,f): 117 return std_norm_cdf(f) 118 119 def dtransf_df(self,f): 120 return std_norm_pdf(f) 121 122 def d2transf_df2(self,f): 123 return -f * std_norm_pdf(f) 124 125 def d3transf_df3(self,f): 126 return (safe_square(f)-1.)*std_norm_pdf(f) 127 128 def to_dict(self): 129 """ 130 Convert the object into a json serializable dictionary. 131 132 Note: It uses the private method _save_to_input_dict of the parent. 133 134 :return dict: json serializable dictionary containing the needed information to instantiate the object 135 """ 136 137 input_dict = super(Probit, self)._save_to_input_dict() 138 input_dict["class"] = "GPy.likelihoods.link_functions.Probit" 139 return input_dict 140 141class ScaledProbit(Probit): 142 """ 143 .. math:: 144 g(f) = \\Phi^{-1} (nu*mu) 145 """ 146 def __init__(self, nu=1.): 147 self.nu = float(nu) 148 149 def transf(self,f): 150 return std_norm_cdf(f*self.nu) 151 152 def dtransf_df(self,f): 153 return std_norm_pdf(f*self.nu)*self.nu 154 155 def d2transf_df2(self,f): 156 return -(f*self.nu) * std_norm_pdf(f*self.nu)*(self.nu**2) 157 158 def d3transf_df3(self,f): 159 return (safe_square(f*self.nu)-1.)*std_norm_pdf(f*self.nu)*(self.nu**3) 160 161 def to_dict(self): 162 """ 163 Convert the object into a json serializable dictionary. 164 165 Note: It uses the private method _save_to_input_dict of the parent. 166 167 :return dict: json serializable dictionary containing the needed information to instantiate the object 168 """ 169 170 input_dict = super(ScaledProbit, self)._save_to_input_dict() 171 input_dict["class"] = "GPy.likelihoods.link_functions.ScaledProbit" 172 return input_dict 173 174class Cloglog(GPTransformation): 175 """ 176 Complementary log-log link 177 .. math:: 178 179 p(f) = 1 - e^{-e^f} 180 181 or 182 183 f = \log (-\log(1-p)) 184 185 """ 186 def transf(self,f): 187 ef = safe_exp(f) 188 return 1-np.exp(-ef) 189 190 def dtransf_df(self,f): 191 ef = safe_exp(f) 192 return np.exp(f-ef) 193 194 def d2transf_df2(self,f): 195 ef = safe_exp(f) 196 return -np.exp(f-ef)*(ef-1.) 197 198 def d3transf_df3(self,f): 199 ef = safe_exp(f) 200 ef2 = safe_square(ef) 201 three_times_ef = safe_three_times(ef) 202 r_val = np.exp(f-ef)*(1.-three_times_ef + ef2) 203 return r_val 204 205class Log(GPTransformation): 206 """ 207 .. math:: 208 209 g(f) = \\log(\\mu) 210 211 """ 212 def transf(self,f): 213 return safe_exp(f) 214 215 def dtransf_df(self,f): 216 return safe_exp(f) 217 218 def d2transf_df2(self,f): 219 return safe_exp(f) 220 221 def d3transf_df3(self,f): 222 return safe_exp(f) 223 224class Log_ex_1(GPTransformation): 225 """ 226 .. math:: 227 228 g(f) = \\log(\\exp(\\mu) - 1) 229 230 """ 231 def transf(self,f): 232 return scipy.special.log1p(safe_exp(f)) 233 234 def dtransf_df(self,f): 235 ef = safe_exp(f) 236 return ef/(1.+ef) 237 238 def d2transf_df2(self,f): 239 ef = safe_exp(f) 240 aux = ef/(1.+ef) 241 return aux*(1.-aux) 242 243 def d3transf_df3(self,f): 244 ef = safe_exp(f) 245 aux = ef/(1.+ef) 246 daux_df = aux*(1.-aux) 247 return daux_df - (2.*aux*daux_df) 248 249class Reciprocal(GPTransformation): 250 def transf(self,f): 251 return 1./f 252 253 def dtransf_df(self, f): 254 f2 = safe_square(f) 255 return -1./f2 256 257 def d2transf_df2(self, f): 258 f3 = safe_cube(f) 259 return 2./f3 260 261 def d3transf_df3(self,f): 262 f4 = safe_quad(f) 263 return -6./f4 264 265class Heaviside(GPTransformation): 266 """ 267 268 .. math:: 269 270 g(f) = I_{x \\geq 0} 271 272 """ 273 def transf(self,f): 274 #transformation goes here 275 return np.where(f>0, 1, 0) 276 277 def dtransf_df(self,f): 278 raise NotImplementedError("This function is not differentiable!") 279 280 def d2transf_df2(self,f): 281 raise NotImplementedError("This function is not differentiable!") 282