3
# Copyrighted David Cournapeau
4
# Last Change: Fri Nov 10 10:00 AM 2006 J
7
import numpy.linalg as lin
8
from numpy.random import randn
9
from scipy.stats import chi2
12
class DenError(Exception):
13
"""Base class for exceptions in this module.
16
expression -- input expression in which the error occurred
17
message -- explanation of the error"""
18
def __init__(self, message):
19
self.message = message
24
# The following function do all the fancy stuff to check that parameters
25
# are Ok, and call the right implementation if args are OK.
26
def gauss_den(x, mu, va, log = False):
27
""" Compute multivariate Gaussian density at points x for
28
mean mu and variance va.
30
Vector are row vectors, except va which can be a matrix
31
(row vector variance for diagonal variance)
33
If log is True, than the log density is returned
34
(useful for underflow ?)"""
39
#=======================#
40
# Checking parameters #
41
#=======================#
42
if len(N.shape(mu)) != 2:
43
raise DenError("mu is not rank 2")
45
if len(N.shape(va)) != 2:
46
raise DenError("va is not rank 2")
48
if len(N.shape(x)) != 2:
49
raise DenError("x is not rank 2")
55
# Check x and mu same dimension
57
msg = "mean must be a row vector!"
60
msg = "x and mu not same dim"
62
# Check va and mu same size
64
msg = "mu and va not same dim"
66
if dv0 != 1 and dv0 != d:
75
return _scalar_gauss_den(x[:, 0], mu[0, 0], va[0, 0], log)
77
# Diagonal matrix case
78
return _diag_gauss_den(x, mu, va, log)
81
return _full_gauss_den(x, mu, va, log)
83
raise DenError("variance mode not recognized, this is a bug")
85
# Those 3 functions do almost all the actual computation
86
def _scalar_gauss_den(x, mu, va, log):
87
""" This function is the actual implementation
88
of gaussian pdf in scalar case. It assumes all args
89
are conformant, so it should not be used directly
91
Call gauss_den instead"""
94
fac = (2*N.pi) ** (-d/2.0) * N.sqrt(inva)
95
y = ((x-mu) ** 2) * -0.5 * inva
103
#from ctypes import cdll, c_uint, c_int, c_double, POINTER
104
#_gden = cdll.LoadLibrary('src/libgden.so')
105
#_gden.gden_diag.restype = c_int
106
#_gden.gden_diag.argtypes = [POINTER(c_double), c_uint, c_uint,
107
# POINTER(c_double), POINTER(c_double), POINTER(c_double)]
109
def _diag_gauss_den(x, mu, va, log):
110
""" This function is the actual implementation
111
of gaussian pdf in scalar case. It assumes all args
112
are conformant, so it should not be used directly
114
Call gauss_den instead"""
115
# Diagonal matrix case
120
fac = (2*N.pi) ** (-d/2.0) * N.sqrt(inva)
121
y = (x[:,0] - mu[0,0]) ** 2 * inva * -0.5
122
for i in range(1, d):
125
y += (x[:,i] - mu[0,i]) ** 2 * inva * -0.5
128
y = _scalar_gauss_den(x[:,0], mu[0,0], va[0,0], log)
129
for i in range(1, d):
130
y += _scalar_gauss_den(x[:,i], mu[0,i], va[0,i], log)
133
def _full_gauss_den(x, mu, va, log):
134
""" This function is the actual implementation
135
of gaussian pdf in full matrix case.
137
It assumes all args are conformant, so it should
138
not be used directly Call gauss_den instead
140
Does not check if va is definite positive (on inversible
141
for that matter), so the inverse computation and/or determinant
142
would throw an exception."""
145
fac = 1 / N.sqrt( (2*N.pi) ** d * N.fabs(lin.det(va)))
151
# y[i] = N.dot(x[i,:],
152
# N.dot(inva, N.transpose(x[i,:])))
155
# we are using a trick with sum to "emulate"
156
# the matrix multiplication inva * x without any explicit loop
157
y = N.dot((x-mu), inva)
158
y = -0.5 * N.sum(y * (x-mu), 1)
167
# To plot a confidence ellipse from multi-variate gaussian pdf
168
def gauss_ell(mu, va, dim = [0, 1], npoints = 100, level = 0.39):
169
""" Given a mean and covariance for multi-variate
170
gaussian, returns npoints points for the ellipse
171
of confidence given by level (all points will be inside
172
the ellipsoides with a probability equal to level)
174
Returns the coordinate x and y of the ellipse"""
176
mu = N.atleast_1d(mu)
177
va = N.atleast_1d(va)
180
if mu.size == va.size:
184
if va.shape[0] == va.shape[1]:
187
raise DenError("variance not square")
189
raise DenError("mean and variance are not dim conformant")
192
mahal = N.sqrt(chi22d.ppf(level))
194
# Generates a circle of npoints
195
theta = N.linspace(0, 2 * N.pi, npoints)
196
circle = mahal * N.array([N.cos(theta), N.sin(theta)])
198
# Get the dimension which we are interested in:
202
elps = N.outer(mu, N.ones(npoints))
203
elps += N.dot(N.diag(N.sqrt(va)), circle)
206
# Method: compute the cholesky decomp of each cov matrix, that is
207
# compute cova such as va = cova * cova'
208
# WARN: scipy is different than matlab here, as scipy computes a lower
209
# triangular cholesky decomp:
210
# - va = cova * cova' (scipy)
211
# - va = cova' * cova (matlab)
212
# So take care when comparing results with matlab !
213
cova = lin.cholesky(va)
214
elps = N.outer(mu, N.ones(npoints))
215
elps += N.dot(cova, circle)
217
raise DenParam("var mode not recognized")
219
return elps[0, :], elps[1, :]
221
if __name__ == "__main__":
224
#=========================================
225
# Test plotting a simple diag 2d variance:
226
#=========================================
230
# Generate a multivariate gaussian of mean mu and covariance va
232
Yc = N.dot(N.diag(N.sqrt(va)), X.transpose())
233
Yc = Yc.transpose() + mu
236
Xe, Ye = gauss_ell(mu, va, npoints = 100)
238
pylab.plot(Yc[:, 0], Yc[:, 1], '.')
239
pylab.plot(Xe, Ye, 'r')
241
#=========================================
242
# Test plotting a simple full 2d variance:
243
#=========================================
244
va = N.array([[0.2, 0.1],[0.1, 0.5]])
247
# Generate a multivariate gaussian of mean mu and covariance va
249
Yc = N.dot(lin.cholesky(va), X.transpose())
250
Yc = Yc.transpose() + mu
253
Xe, Ye = gauss_ell(mu, va, npoints = 100, level=0.95)
255
pylab.plot(Yc[:, 0], Yc[:, 1], '.')
256
pylab.plot(Xe, Ye, 'r')