3
# Copyrighted David Cournapeau
4
# Last Change: Thu Nov 09 05:00 PM 2006 J
6
# This module uses a C implementation through ctypes, for diagonal cases
8
# - portable way to find/open the shared library
12
import numpy.linalg as lin
13
from numpy.random import randn
14
from scipy.stats import chi2
18
from ctypes import cdll, c_uint, c_int, c_double, POINTER
19
from numpy.ctypeslib import ndpointer, load_library
21
ctypes_major = int(ctypes.__version__.split('.')[0])
23
msg = "version of ctypes is %s, expected at least %s" \
24
% (ctypes.__version__, '1.0.0')
25
raise ImportError(msg)
27
# Requirements for diag gden
28
_gden = load_library('c_gden.so', __file__)
29
arg1 = ndpointer(dtype=N.float64)
32
arg4 = ndpointer(dtype=N.float64)
33
arg5 = ndpointer(dtype=N.float64)
34
arg6 = ndpointer(dtype=N.float64)
35
_gden.gden_diag.argtypes = [arg1, arg2, arg3, arg4, arg5, arg6]
36
_gden.gden_diag.restype = c_int
39
class DenError(Exception):
40
"""Base class for exceptions in this module.
43
expression -- input expression in which the error occurred
44
message -- explanation of the error"""
45
def __init__(self, message):
46
self.message = message
51
# The following function do all the fancy stuff to check that parameters
52
# are Ok, and call the right implementation if args are OK.
53
def gauss_den(x, mu, va, log = False):
54
""" Compute multivariate Gaussian density at points x for
55
mean mu and variance va.
57
Vector are row vectors, except va which can be a matrix
58
(row vector variance for diagonal variance)
60
If log is True, than the log density is returned
61
(useful for underflow ?)"""
66
#=======================#
67
# Checking parameters #
68
#=======================#
69
if len(N.shape(mu)) != 2:
70
raise DenError("mu is not rank 2")
72
if len(N.shape(va)) != 2:
73
raise DenError("va is not rank 2")
75
if len(N.shape(x)) != 2:
76
raise DenError("x is not rank 2")
82
# Check x and mu same dimension
84
msg = "mean must be a row vector!"
87
msg = "x and mu not same dim"
89
# Check va and mu same size
91
msg = "mu and va not same dim"
93
if dv0 != 1 and dv0 != d:
102
return _scalar_gauss_den(x[:, 0], mu[0, 0], va[0, 0], log)
104
# Diagonal matrix case
105
return _diag_gauss_den(x, mu, va, log)
108
return _full_gauss_den(x, mu, va, log)
110
raise DenError("variance mode not recognized, this is a bug")
112
# Those 3 functions do almost all the actual computation
113
def _scalar_gauss_den(x, mu, va, log):
114
""" This function is the actual implementation
115
of gaussian pdf in scalar case. It assumes all args
116
are conformant, so it should not be used directly
118
** Expect centered data (ie with mean removed) **
120
Call gauss_den instead"""
123
fac = (2*N.pi) ** (-d/2.0) * N.sqrt(inva)
124
y = ((x-mu) ** 2) * -0.5 * inva
132
def _diag_gauss_den(x, mu, va, log):
133
""" This function is the actual implementation
134
of gaussian pdf in scalar case. It assumes all args
135
are conformant, so it should not be used directly
137
** Expect centered data (ie with mean removed) **
139
Call gauss_den instead"""
140
# Diagonal matrix case
146
# _gden.gden_diag(N.require(x, requirements = 'C'), n, d,
147
# N.require(mu, requirements = 'C'),
148
# N.require(inva, requirements = 'C'),
149
# N.require(y, requirements = 'C'))
150
x = N.require(x, requirements = 'C')
151
mu = N.require(mu, requirements = 'C')
152
vat = N.require(vat, requirements = 'C')
153
y = N.require(y, requirements = 'C')
154
_gden.gden_diag(x, n, d, mu, vat, y)
156
# _gden.gden_diag.restype = c_int
157
# _gden.gden_diag.argtypes = [POINTER(c_double), c_uint, c_uint,
158
# POINTER(c_double), POINTER(c_double), POINTER(c_double)]
162
# _gden.gden_diag(x.ctypes.data_as(POINTER(c_double)),
164
# mu.ctypes.data_as(POINTER(c_double)),
165
# inva.ctypes.data_as(POINTER(c_double)),
166
# y.ctypes.data_as(POINTER(c_double)))
168
y = _scalar_gauss_den(x[:,0], mu[0,0], va[0,0], log)
169
for i in range(1, d):
170
y += _scalar_gauss_den(x[:,i], mu[0,i], va[0,i], log)
173
def _full_gauss_den(x, mu, va, log):
174
""" This function is the actual implementation
175
of gaussian pdf in full matrix case.
177
It assumes all args are conformant, so it should
178
not be used directly Call gauss_den instead
180
** Expect centered data (ie with mean removed) **
182
Does not check if va is definite positive (on inversible
183
for that matter), so the inverse computation and/or determinant
184
would throw an exception."""
187
fac = 1 / N.sqrt( (2*N.pi) ** d * N.fabs(lin.det(va)))
189
# we are using a trick with sum to "emulate"
190
# the matrix multiplication inva * x without any explicit loop
191
y = N.dot((x-mu), inva)
192
y = -0.5 * N.sum(y * (x-mu), 1)
201
if __name__ == "__main__":
202
#=========================================
203
# Test accuracy between pure and C python
204
#=========================================
205
mu = N.array([2.0, 3])
206
va = N.array([5.0, 3])
208
# Generate a multivariate gaussian of mean mu and covariance va
210
X = randn(nframes, 2)
211
Yc = N.dot(N.diag(N.sqrt(va)), X.transpose())
212
Yc = Yc.transpose() + mu
214
Y = D.gauss_den(Yc, mu, va)
215
Yt = gauss_den(Yc, mu, va)
217
print "Diff is " + str(N.sqrt(N.sum((Y-Yt) ** 2))/nframes/2)