00001
00002
00003
00004
00005
00006 #include <boost/numpy.hpp>
00007
00008 #include <cmath>
00009 #include <memory>
00010
00011 #ifndef M_PI
00012 #include <boost/math/constants/constants.hpp>
00013 const double M_PI = boost::math::constants::pi<double>();
00014 #endif
00015
00016 namespace bp = boost::python;
00017 namespace bn = boost::numpy;
00018
00024 class matrix2 {
00025 public:
00026
00027 double & operator()(int i, int j) {
00028 return _data[i*2 + j];
00029 }
00030
00031 double const & operator()(int i, int j) const {
00032 return _data[i*2 + j];
00033 }
00034
00035 double const * data() const { return _data; }
00036
00037 private:
00038 double _data[4];
00039 };
00040
00046 class vector2 {
00047 public:
00048
00049 double & operator[](int i) {
00050 return _data[i];
00051 }
00052
00053 double const & operator[](int i) const {
00054 return _data[i];
00055 }
00056
00057 double const * data() const { return _data; }
00058
00059 vector2 operator+(vector2 const & other) const {
00060 vector2 r;
00061 r[0] = _data[0] + other[0];
00062 r[1] = _data[1] + other[1];
00063 return r;
00064 }
00065
00066 vector2 operator-(vector2 const & other) const {
00067 vector2 r;
00068 r[0] = _data[0] - other[0];
00069 r[1] = _data[1] - other[1];
00070 return r;
00071 }
00072
00073 private:
00074 double _data[2];
00075 };
00076
00080 vector2 operator*(matrix2 const & m, vector2 const & v) {
00081 vector2 r;
00082 r[0] = m(0, 0) * v[0] + m(0, 1) * v[1];
00083 r[1] = m(1, 0) * v[0] + m(1, 1) * v[1];
00084 return r;
00085 }
00086
00090 double dot(vector2 const & v1, vector2 const & v2) {
00091 return v1[0] * v2[0] + v1[1] * v2[1];
00092 }
00093
00098 class bivariate_gaussian {
00099 public:
00100
00101 vector2 const & get_mu() const { return _mu; }
00102
00103 matrix2 const & get_sigma() const { return _sigma; }
00104
00108 double operator()(vector2 const & p) const {
00109 vector2 u = _cholesky * (p - _mu);
00110 return 0.5 * _cholesky(0, 0) * _cholesky(1, 1) * std::exp(-0.5 * dot(u, u)) / M_PI;
00111 }
00112
00116 double operator()(double x, double y) const {
00117 vector2 p;
00118 p[0] = x;
00119 p[1] = y;
00120 return operator()(p);
00121 }
00122
00126 bivariate_gaussian(vector2 const & mu, matrix2 const & sigma)
00127 : _mu(mu), _sigma(sigma), _cholesky(compute_inverse_cholesky(sigma))
00128 {}
00129
00130 private:
00131
00136 static matrix2 compute_inverse_cholesky(matrix2 const & m) {
00137 matrix2 l;
00138
00139 l(0, 0) = std::sqrt(m(0, 0));
00140 l(0, 1) = m(0, 1) / l(0, 0);
00141 l(1, 1) = std::sqrt(m(1, 1) - l(0,1) * l(0,1));
00142
00143 l(0, 0) = 1.0 / l(0, 0);
00144 l(1, 0) = l(0, 1) = -l(0, 1) / l(1, 1);
00145 l(1, 1) = 1.0 / l(1, 1);
00146 return l;
00147 }
00148
00149 vector2 _mu;
00150 matrix2 _sigma;
00151 matrix2 _cholesky;
00152
00153 };
00154
00155
00156
00157
00158
00159
00160
00161
00162
00163
00164
00165
00166
00167
00168
00169
00170
00179 static bn::ndarray py_get_mu(bp::object const & self) {
00180 vector2 const & mu = bp::extract<bivariate_gaussian const &>(self)().get_mu();
00181 return bn::from_data(
00182 mu.data(),
00183 bn::dtype::get_builtin<double>(),
00184 bp::make_tuple(2),
00185 bp::make_tuple(sizeof(double)),
00186 self
00187 );
00188 }
00189 static bn::ndarray py_get_sigma(bp::object const & self) {
00190 matrix2 const & sigma = bp::extract<bivariate_gaussian const &>(self)().get_sigma();
00191 return bn::from_data(
00192 sigma.data(),
00193 bn::dtype::get_builtin<double>(),
00194 bp::make_tuple(2, 2),
00195 bp::make_tuple(2 * sizeof(double), sizeof(double)),
00196 self
00197 );
00198 }
00199
00211 static void copy_ndarray_to_mv2(bn::ndarray const & array, vector2 & vec) {
00212 vec[0] = bp::extract<double>(array[0]);
00213 vec[1] = bp::extract<double>(array[1]);
00214 }
00215
00220 static void copy_ndarray_to_mv2(bn::ndarray const & array, matrix2 & mat) {
00221
00222 Py_intptr_t const * strides = array.get_strides();
00223 for (int i = 0; i < 2; ++i) {
00224 for (int j = 0; j < 2; ++j) {
00225 mat(i, j) = *reinterpret_cast<double const *>(array.get_data() + i * strides[0] + j * strides[1]);
00226 }
00227 }
00228 }
00229
00234 template <typename T, int N>
00235 struct mv2_from_python {
00236
00240 mv2_from_python() {
00241 bp::converter::registry::push_back(
00242 &convertible,
00243 &construct,
00244 bp::type_id< T >()
00245 );
00246 }
00247
00252 static void * convertible(PyObject * p) {
00253 try {
00254 bp::object obj(bp::handle<>(bp::borrowed(p)));
00255 std::auto_ptr<bn::ndarray> array(
00256 new bn::ndarray(
00257 bn::from_object(obj, bn::dtype::get_builtin<double>(), N, N, bn::ndarray::V_CONTIGUOUS)
00258 )
00259 );
00260 if (array->shape(0) != 2) return 0;
00261 if (N == 2 && array->shape(1) != 2) return 0;
00262 return array.release();
00263 } catch (bp::error_already_set & err) {
00264 bp::handle_exception();
00265 return 0;
00266 }
00267 }
00268
00272 static void construct(PyObject * obj, bp::converter::rvalue_from_python_stage1_data * data) {
00273
00274 std::auto_ptr<bn::ndarray> array(reinterpret_cast<bn::ndarray*>(data->convertible));
00275
00276 typedef bp::converter::rvalue_from_python_storage<T> storage_t;
00277 storage_t * storage = reinterpret_cast<storage_t*>(data);
00278
00279 T * m_or_v = new (storage->storage.bytes) T();
00280
00281 copy_ndarray_to_mv2(*array, *m_or_v);
00282
00283 data->convertible = storage->storage.bytes;
00284 }
00285
00286 };
00287
00288
00289 BOOST_PYTHON_MODULE(gaussian) {
00290 bn::initialize();
00291
00292
00293 mv2_from_python< vector2, 1 >();
00294 mv2_from_python< matrix2, 2 >();
00295
00296 typedef double (bivariate_gaussian::*call_vector)(vector2 const &) const;
00297
00298 bp::class_<bivariate_gaussian>("bivariate_gaussian", bp::init<bivariate_gaussian const &>())
00299
00300
00301 .def(bp::init< vector2 const &, matrix2 const & >())
00302
00303
00304 .add_property("mu", &py_get_mu)
00305 .add_property("sigma", &py_get_sigma)
00306
00307
00308 .def("__call__", (call_vector)&bivariate_gaussian::operator())
00309
00310
00311
00312
00313 .def("__call__", bn::binary_ufunc<bivariate_gaussian,double,double,double>::make())
00314 ;
00315 }