15
15
16
16
class GP (Continuous ):
17
17
"""Gausian process
18
-
18
+
19
19
Parameters
20
20
----------
21
21
mean_func : Mean
22
22
Mean function of Gaussian process
23
23
cov_func : Covariance
24
24
Covariance function of Gaussian process
25
25
X : array
26
- Grid of points to evaluate Gaussian process over. Only required if the
26
+ Grid of points to evaluate Gaussian process over. Only required if the
27
27
GP is not an observed variable.
28
28
sigma : scalar or array
29
29
Observation standard deviation (defaults to zero)
30
30
"""
31
31
def __init__ (self , mean_func = None , cov_func = None , X = None , sigma = 0 , * args , ** kwargs ):
32
-
32
+
33
33
if mean_func is None :
34
34
self .M = Zero ()
35
35
else :
36
36
if not isinstance (mean_func , Mean ):
37
37
raise ValueError ('mean_func must be a subclass of Mean' )
38
38
self .M = mean_func
39
-
39
+
40
40
if cov_func is None :
41
41
raise ValueError ('A covariance function must be specified for GPP' )
42
42
if not isinstance (cov_func , Covariance ):
43
43
raise ValueError ('cov_func must be a subclass of Covariance' )
44
44
self .K = cov_func
45
-
45
+
46
46
self .sigma = sigma
47
-
47
+
48
48
if X is not None :
49
49
self .X = X
50
50
self .mean = self .mode = self .M (X )
51
51
kwargs .setdefault ("shape" , X .squeeze ().shape )
52
-
52
+
53
53
super (GP , self ).__init__ (* args , ** kwargs )
54
-
54
+
55
55
def random (self , point = None , size = None , ** kwargs ):
56
56
X = self .X
57
57
mu , cov = draw_values ([self .M (X ).squeeze (), self .K (X ) + np .eye (X .shape [0 ])* self .sigma ** 2 ], point = point )
58
-
58
+
59
59
def _random (mean , cov , size = None ):
60
60
return stats .multivariate_normal .rvs (
61
61
mean , cov , None if size == mean .shape else size )
@@ -74,9 +74,9 @@ def logp(self, Y, X=None):
74
74
Sigma = self .K (X ) + tt .eye (X .shape [0 ])* self .sigma ** 2
75
75
76
76
return MvNormal .dist (mu , Sigma ).logp (Y )
77
-
78
77
79
- def sample_gp (trace , gp , X_values , samples = None , obs_noise = True , model = None , random_seed = None , progressbar = True ):
78
+
79
+ def sample_gp (trace , gp , X_values , samples = None , obs_noise = True , model = None , random_seed = None , progressbar = True , chol_const = True ):
80
80
"""Generate samples from a posterior Gaussian process.
81
81
82
82
Parameters
@@ -92,38 +92,41 @@ def sample_gp(trace, gp, X_values, samples=None, obs_noise=True, model=None, ran
92
92
length of `trace`
93
93
obs_noise : bool
94
94
Flag for including observation noise in sample. Defaults to True.
95
- model : Model
95
+ model : Model
96
96
Model used to generate `trace`. Optional if in `with` context manager.
97
97
random_seed : integer > 0
98
98
Random number seed for sampling.
99
99
progressbar : bool
100
100
Flag for showing progress bar.
101
-
101
+ chol_const : bool
102
+ Flag to a small diagonal to the posterior covariance
103
+ for numerical stability
104
+
102
105
Returns
103
106
-------
104
107
Array of samples from posterior GP evaluated at Z.
105
108
"""
106
109
model = modelcontext (model )
107
-
110
+
108
111
if samples is None :
109
112
samples = len (trace )
110
-
113
+
111
114
if random_seed :
112
115
np .random .seed (random_seed )
113
-
116
+
114
117
if progressbar :
115
118
indices = tqdm (np .random .randint (0 , len (trace ), samples ), total = samples )
116
119
else :
117
120
indices = np .random .randint (0 , len (trace ), samples )
118
121
119
- K = gp .distribution .K
120
-
122
+ K = gp .distribution .K
123
+
121
124
data = [v for v in model .observed_RVs if v .name == gp .name ][0 ].data
122
125
123
126
X = data ['X' ]
124
127
Y = data ['Y' ]
125
128
Z = X_values
126
-
129
+
127
130
S_xz = K (X , Z )
128
131
S_zz = K (Z )
129
132
if obs_noise :
@@ -136,8 +139,10 @@ def sample_gp(trace, gp, X_values, samples=None, obs_noise=True, model=None, ran
136
139
# Posterior covariance
137
140
S_post = S_zz - tt .dot (tt .dot (S_xz .T , S_inv ), S_xz )
138
141
139
- gp_post = MvNormal .dist (m_post , S_post , shape = Z .shape [0 ])
140
-
142
+ if chol_const :
143
+ n = S_post .shape [0 ]
144
+ correction = 1e-6 * tt .nlinalg .trace (S_post ) * tt .eye (n )
145
+
146
+ gp_post = MvNormal .dist (m_post , S_post + correction , shape = Z .shape [0 ])
141
147
samples = [gp_post .random (point = trace [idx ]) for idx in indices ]
142
-
143
148
return np .array (samples )
0 commit comments