summaryrefslogtreecommitdiffstats
path: root/src/boost/libs/python/example/numpy
diff options
context:
space:
mode:
Diffstat (limited to 'src/boost/libs/python/example/numpy')
-rw-r--r--src/boost/libs/python/example/numpy/Jamfile29
-rw-r--r--src/boost/libs/python/example/numpy/demo_gaussian.py37
-rw-r--r--src/boost/libs/python/example/numpy/dtype.cpp49
-rw-r--r--src/boost/libs/python/example/numpy/fromdata.cpp48
-rw-r--r--src/boost/libs/python/example/numpy/gaussian.cpp315
-rw-r--r--src/boost/libs/python/example/numpy/ndarray.cpp71
-rw-r--r--src/boost/libs/python/example/numpy/simple.cpp32
-rw-r--r--src/boost/libs/python/example/numpy/ufunc.cpp86
-rw-r--r--src/boost/libs/python/example/numpy/wrap.cpp135
9 files changed, 802 insertions, 0 deletions
diff --git a/src/boost/libs/python/example/numpy/Jamfile b/src/boost/libs/python/example/numpy/Jamfile
new file mode 100644
index 000000000..ac70a6ff0
--- /dev/null
+++ b/src/boost/libs/python/example/numpy/Jamfile
@@ -0,0 +1,29 @@
+# Copyright Stefan Seefeld 2016.
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE_1_0.txt or copy at
+# http://www.boost.org/LICENSE_1_0.txt)
+
+import python ;
+
+# Adjust the following if Boost.Python isn't installed in a default location
+lib boost_numpy
+ :
+ : <search>/usr/local/Boost/lib
+ <include>/usr/local/Boost/include
+ ;
+
+project numpy
+ : requirements
+ <include>/usr/local/Boost/include
+ <library>boost_numpy
+ <location>.
+ ;
+
+exe simple : simple.cpp boost_numpy /python//python ;
+exe dtype : dtype.cpp boost_numpy /python//python ;
+exe ndarray : ndarray.cpp /python//python ;
+exe fromdata : fromdata.cpp /python//python ;
+exe ufunc : ufunc.cpp /python//python ;
+exe wrap : wrap.cpp /python//python ;
+
+python-extension gaussian : gaussian.cpp ;
diff --git a/src/boost/libs/python/example/numpy/demo_gaussian.py b/src/boost/libs/python/example/numpy/demo_gaussian.py
new file mode 100644
index 000000000..0b1c78995
--- /dev/null
+++ b/src/boost/libs/python/example/numpy/demo_gaussian.py
@@ -0,0 +1,37 @@
+# Copyright Jim Bosch 2010-2012.
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE_1_0.txt or copy at
+# http://www.boost.org/LICENSE_1_0.txt)
+
+import numpy
+import gaussian
+
+mu = numpy.zeros(2, dtype=float)
+sigma = numpy.identity(2, dtype=float)
+sigma[0, 1] = 0.15
+sigma[1, 0] = 0.15
+
+g = gaussian.bivariate_gaussian(mu, sigma)
+
+r = numpy.linspace(-40, 40, 1001)
+x, y = numpy.meshgrid(r, r)
+
+z = g(x, y)
+
+s = z.sum() * (r[1] - r[0])**2
+print "sum (should be ~ 1):", s
+
+xc = (z * x).sum() / z.sum()
+print "x centroid (should be ~ %f): %f" % (mu[0], xc)
+
+yc = (z * y).sum() / z.sum()
+print "y centroid (should be ~ %f): %f" % (mu[1], yc)
+
+xx = (z * (x - xc)**2).sum() / z.sum()
+print "xx moment (should be ~ %f): %f" % (sigma[0,0], xx)
+
+yy = (z * (y - yc)**2).sum() / z.sum()
+print "yy moment (should be ~ %f): %f" % (sigma[1,1], yy)
+
+xy = 0.5 * (z * (x - xc) * (y - yc)).sum() / z.sum()
+print "xy moment (should be ~ %f): %f" % (sigma[0,1], xy)
diff --git a/src/boost/libs/python/example/numpy/dtype.cpp b/src/boost/libs/python/example/numpy/dtype.cpp
new file mode 100644
index 000000000..749a36b5e
--- /dev/null
+++ b/src/boost/libs/python/example/numpy/dtype.cpp
@@ -0,0 +1,49 @@
+// Copyright Ankit Daftery 2011-2012.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+/**
+ * @brief An example to show how to create ndarrays with built-in python data types, and extract
+ * the types and values of member variables
+ *
+ * @todo Add an example to show type conversion.
+ * Add an example to show use of user-defined types
+ *
+ */
+
+#include <boost/python/numpy.hpp>
+#include <iostream>
+
+namespace p = boost::python;
+namespace np = boost::python::numpy;
+
+int main(int argc, char **argv)
+{
+ // Initialize the Python runtime.
+ Py_Initialize();
+ // Initialize NumPy
+ np::initialize();
+ // Create a 3x3 shape...
+ p::tuple shape = p::make_tuple(3, 3);
+ // ...as well as a type for C++ double
+ np::dtype dtype = np::dtype::get_builtin<double>();
+ // Construct an array with the above shape and type
+ np::ndarray a = np::zeros(shape, dtype);
+ // Print the array
+ std::cout << "Original array:\n" << p::extract<char const *>(p::str(a)) << std::endl;
+ // Print the datatype of the elements
+ std::cout << "Datatype is:\n" << p::extract<char const *>(p::str(a.get_dtype())) << std::endl ;
+ // Using user defined dtypes to create dtype and an array of the custom dtype
+ // First create a tuple with a variable name and its dtype, double, to create a custom dtype
+ p::tuple for_custom_dtype = p::make_tuple("ha",dtype) ;
+ // The list needs to be created, because the constructor to create the custom dtype
+ // takes a list of (variable,variable_type) as an argument
+ p::list list_for_dtype ;
+ list_for_dtype.append(for_custom_dtype) ;
+ // Create the custom dtype
+ np::dtype custom_dtype = np::dtype(list_for_dtype) ;
+ // Create an ndarray with the custom dtype
+ np::ndarray new_array = np::zeros(shape,custom_dtype);
+
+}
diff --git a/src/boost/libs/python/example/numpy/fromdata.cpp b/src/boost/libs/python/example/numpy/fromdata.cpp
new file mode 100644
index 000000000..bd073cc69
--- /dev/null
+++ b/src/boost/libs/python/example/numpy/fromdata.cpp
@@ -0,0 +1,48 @@
+// Copyright Ankit Daftery 2011-2012.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+/**
+ * @brief An example to show how to access data using raw pointers. This shows that you can use and
+ * manipulate data in either Python or C++ and have the changes reflected in both.
+ */
+
+#include <boost/python/numpy.hpp>
+#include <iostream>
+
+namespace p = boost::python;
+namespace np = boost::python::numpy;
+
+
+int main(int argc, char **argv)
+{
+ // Initialize the Python runtime.
+ Py_Initialize();
+ // Initialize NumPy
+ np::initialize();
+ // Create an array in C++
+ int arr[] = {1,2,3,4} ;
+ // Create the ndarray in Python
+ np::ndarray py_array = np::from_data(arr, np::dtype::get_builtin<int>() , p::make_tuple(4), p::make_tuple(4), p::object());
+ // Print the ndarray that we just created, and the source C++ array
+ std::cout << "C++ array :" << std::endl ;
+ for (int j=0;j<4;j++)
+ {
+ std::cout << arr[j] << ' ' ;
+ }
+ std::cout << std::endl << "Python ndarray :" << p::extract<char const *>(p::str(py_array)) << std::endl;
+ // Change an element in the python ndarray
+ py_array[1] = 5 ;
+ // And see if the C++ container is changed or not
+ std::cout << "Is the change reflected in the C++ array used to create the ndarray ? " << std::endl ;
+ for (int j = 0;j<4 ; j++)
+ {
+ std::cout << arr[j] << ' ' ;
+ }
+ // Conversely, change it in C++
+ arr[2] = 8 ;
+ // And see if the changes are reflected in the Python ndarray
+ std::cout << std::endl << "Is the change reflected in the Python ndarray ?" << std::endl << p::extract<char const *>(p::str(py_array)) << std::endl;
+
+}
diff --git a/src/boost/libs/python/example/numpy/gaussian.cpp b/src/boost/libs/python/example/numpy/gaussian.cpp
new file mode 100644
index 000000000..5f138b397
--- /dev/null
+++ b/src/boost/libs/python/example/numpy/gaussian.cpp
@@ -0,0 +1,315 @@
+// Copyright Jim Bosch 2010-2012.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#include <boost/python/numpy.hpp>
+
+#include <cmath>
+#include <memory>
+
+#ifndef M_PI
+#include <boost/math/constants/constants.hpp>
+const double M_PI = boost::math::constants::pi<double>();
+#endif
+
+namespace bp = boost::python;
+namespace bn = boost::python::numpy;
+
+/**
+ * A 2x2 matrix class, purely for demonstration purposes.
+ *
+ * Instead of wrapping this class with Boost.Python, we'll convert it to/from numpy.ndarray.
+ */
+class matrix2 {
+public:
+
+ double & operator()(int i, int j) {
+ return _data[i*2 + j];
+ }
+
+ double const & operator()(int i, int j) const {
+ return _data[i*2 + j];
+ }
+
+ double const * data() const { return _data; }
+
+private:
+ double _data[4];
+};
+
+/**
+ * A 2-element vector class, purely for demonstration purposes.
+ *
+ * Instead of wrapping this class with Boost.Python, we'll convert it to/from numpy.ndarray.
+ */
+class vector2 {
+public:
+
+ double & operator[](int i) {
+ return _data[i];
+ }
+
+ double const & operator[](int i) const {
+ return _data[i];
+ }
+
+ double const * data() const { return _data; }
+
+ vector2 operator+(vector2 const & other) const {
+ vector2 r;
+ r[0] = _data[0] + other[0];
+ r[1] = _data[1] + other[1];
+ return r;
+ }
+
+ vector2 operator-(vector2 const & other) const {
+ vector2 r;
+ r[0] = _data[0] - other[0];
+ r[1] = _data[1] - other[1];
+ return r;
+ }
+
+private:
+ double _data[2];
+};
+
+/**
+ * Matrix-vector multiplication.
+ */
+vector2 operator*(matrix2 const & m, vector2 const & v) {
+ vector2 r;
+ r[0] = m(0, 0) * v[0] + m(0, 1) * v[1];
+ r[1] = m(1, 0) * v[0] + m(1, 1) * v[1];
+ return r;
+}
+
+/**
+ * Vector inner product.
+ */
+double dot(vector2 const & v1, vector2 const & v2) {
+ return v1[0] * v2[0] + v1[1] * v2[1];
+}
+
+/**
+ * This class represents a simple 2-d Gaussian (Normal) distribution, defined by a
+ * mean vector 'mu' and a covariance matrix 'sigma'.
+ */
+class bivariate_gaussian {
+public:
+
+ vector2 const & get_mu() const { return _mu; }
+
+ matrix2 const & get_sigma() const { return _sigma; }
+
+ /**
+ * Evaluate the density of the distribution at a point defined by a two-element vector.
+ */
+ double operator()(vector2 const & p) const {
+ vector2 u = _cholesky * (p - _mu);
+ return 0.5 * _cholesky(0, 0) * _cholesky(1, 1) * std::exp(-0.5 * dot(u, u)) / M_PI;
+ }
+
+ /**
+ * Evaluate the density of the distribution at an (x, y) point.
+ */
+ double operator()(double x, double y) const {
+ vector2 p;
+ p[0] = x;
+ p[1] = y;
+ return operator()(p);
+ }
+
+ /**
+ * Construct from a mean vector and covariance matrix.
+ */
+ bivariate_gaussian(vector2 const & mu, matrix2 const & sigma)
+ : _mu(mu), _sigma(sigma), _cholesky(compute_inverse_cholesky(sigma))
+ {}
+
+private:
+
+ /**
+ * This evaluates the inverse of the Cholesky factorization of a 2x2 matrix;
+ * it's just a shortcut in evaluating the density.
+ */
+ static matrix2 compute_inverse_cholesky(matrix2 const & m) {
+ matrix2 l;
+ // First do cholesky factorization: l l^t = m
+ l(0, 0) = std::sqrt(m(0, 0));
+ l(0, 1) = m(0, 1) / l(0, 0);
+ l(1, 1) = std::sqrt(m(1, 1) - l(0,1) * l(0,1));
+ // Now do forward-substitution (in-place) to invert:
+ l(0, 0) = 1.0 / l(0, 0);
+ l(1, 0) = l(0, 1) = -l(0, 1) / l(1, 1);
+ l(1, 1) = 1.0 / l(1, 1);
+ return l;
+ }
+
+ vector2 _mu;
+ matrix2 _sigma;
+ matrix2 _cholesky;
+
+};
+
+/*
+ * We have a two options for wrapping get_mu and get_sigma into NumPy-returning Python methods:
+ * - we could deep-copy the data, making totally new NumPy arrays;
+ * - we could make NumPy arrays that point into the existing memory.
+ * The latter is often preferable, especially if the arrays are large, but it's dangerous unless
+ * the reference counting is correct: the returned NumPy array needs to hold a reference that
+ * keeps the memory it points to from being deallocated as long as it is alive. This is what the
+ * "owner" argument to from_data does - the NumPy array holds a reference to the owner, keeping it
+ * from being destroyed.
+ *
+ * Note that this mechanism isn't completely safe for data members that can have their internal
+ * storage reallocated. A std::vector, for instance, can be invalidated when it is resized,
+ * so holding a Python reference to a C++ class that holds a std::vector may not be a guarantee
+ * that the memory in the std::vector will remain valid.
+ */
+
+/**
+ * These two functions are custom wrappers for get_mu and get_sigma, providing the shallow-copy
+ * conversion with reference counting described above.
+ *
+ * It's also worth noting that these return NumPy arrays that cannot be modified in Python;
+ * the const overloads of vector::data() and matrix::data() return const references,
+ * and passing a const pointer to from_data causes NumPy's 'writeable' flag to be set to false.
+ */
+static bn::ndarray py_get_mu(bp::object const & self) {
+ vector2 const & mu = bp::extract<bivariate_gaussian const &>(self)().get_mu();
+ return bn::from_data(
+ mu.data(),
+ bn::dtype::get_builtin<double>(),
+ bp::make_tuple(2),
+ bp::make_tuple(sizeof(double)),
+ self
+ );
+}
+static bn::ndarray py_get_sigma(bp::object const & self) {
+ matrix2 const & sigma = bp::extract<bivariate_gaussian const &>(self)().get_sigma();
+ return bn::from_data(
+ sigma.data(),
+ bn::dtype::get_builtin<double>(),
+ bp::make_tuple(2, 2),
+ bp::make_tuple(2 * sizeof(double), sizeof(double)),
+ self
+ );
+}
+
+/**
+ * To allow the constructor to work, we need to define some from-Python converters from NumPy arrays
+ * to the matrix/vector types. The rvalue-from-python functionality is not well-documented in Boost.Python
+ * itself; you can learn more from boost/python/converter/rvalue_from_python_data.hpp.
+ */
+
+/**
+ * We start with two functions that just copy a NumPy array into matrix/vector objects. These will be used
+ * in the templated converted below. The first just uses the operator[] overloads provided by
+ * bp::object.
+ */
+static void copy_ndarray_to_mv2(bn::ndarray const & array, vector2 & vec) {
+ vec[0] = bp::extract<double>(array[0]);
+ vec[1] = bp::extract<double>(array[1]);
+}
+
+/**
+ * Here, we'll take the alternate approach of using the strides to access the array's memory directly.
+ * This can be much faster for large arrays.
+ */
+static void copy_ndarray_to_mv2(bn::ndarray const & array, matrix2 & mat) {
+ // Unfortunately, get_strides() can't be inlined, so it's best to call it once up-front.
+ Py_intptr_t const * strides = array.get_strides();
+ for (int i = 0; i < 2; ++i) {
+ for (int j = 0; j < 2; ++j) {
+ mat(i, j) = *reinterpret_cast<double const *>(array.get_data() + i * strides[0] + j * strides[1]);
+ }
+ }
+}
+
+/**
+ * Here's the actual converter. Because we've separated the differences into the above functions,
+ * we can write a single template class that works for both matrix2 and vector2.
+ */
+template <typename T, int N>
+struct mv2_from_python {
+
+ /**
+ * Register the converter.
+ */
+ mv2_from_python() {
+ bp::converter::registry::push_back(
+ &convertible,
+ &construct,
+ bp::type_id< T >()
+ );
+ }
+
+ /**
+ * Test to see if we can convert this to the desired type; if not return zero.
+ * If we can convert, returned pointer can be used by construct().
+ */
+ static void * convertible(PyObject * p) {
+ try {
+ bp::object obj(bp::handle<>(bp::borrowed(p)));
+ std::auto_ptr<bn::ndarray> array(
+ new bn::ndarray(
+ bn::from_object(obj, bn::dtype::get_builtin<double>(), N, N, bn::ndarray::V_CONTIGUOUS)
+ )
+ );
+ if (array->shape(0) != 2) return 0;
+ if (N == 2 && array->shape(1) != 2) return 0;
+ return array.release();
+ } catch (bp::error_already_set & err) {
+ bp::handle_exception();
+ return 0;
+ }
+ }
+
+ /**
+ * Finish the conversion by initializing the C++ object into memory prepared by Boost.Python.
+ */
+ static void construct(PyObject * obj, bp::converter::rvalue_from_python_stage1_data * data) {
+ // Extract the array we passed out of the convertible() member function.
+ std::auto_ptr<bn::ndarray> array(reinterpret_cast<bn::ndarray*>(data->convertible));
+ // Find the memory block Boost.Python has prepared for the result.
+ typedef bp::converter::rvalue_from_python_storage<T> storage_t;
+ storage_t * storage = reinterpret_cast<storage_t*>(data);
+ // Use placement new to initialize the result.
+ T * m_or_v = new (storage->storage.bytes) T();
+ // Fill the result with the values from the NumPy array.
+ copy_ndarray_to_mv2(*array, *m_or_v);
+ // Finish up.
+ data->convertible = storage->storage.bytes;
+ }
+
+};
+
+
+BOOST_PYTHON_MODULE(gaussian) {
+ bn::initialize();
+
+ // Register the from-python converters
+ mv2_from_python< vector2, 1 >();
+ mv2_from_python< matrix2, 2 >();
+
+ typedef double (bivariate_gaussian::*call_vector)(vector2 const &) const;
+
+ bp::class_<bivariate_gaussian>("bivariate_gaussian", bp::init<bivariate_gaussian const &>())
+
+ // Declare the constructor (wouldn't work without the from-python converters).
+ .def(bp::init< vector2 const &, matrix2 const & >())
+
+ // Use our custom reference-counting getters
+ .add_property("mu", &py_get_mu)
+ .add_property("sigma", &py_get_sigma)
+
+ // First overload accepts a two-element array argument
+ .def("__call__", (call_vector)&bivariate_gaussian::operator())
+
+ // This overload works like a binary NumPy universal function: you can pass
+ // in scalars or arrays, and the C++ function will automatically be called
+ // on each element of an array argument.
+ .def("__call__", bn::binary_ufunc<bivariate_gaussian,double,double,double>::make())
+ ;
+}
diff --git a/src/boost/libs/python/example/numpy/ndarray.cpp b/src/boost/libs/python/example/numpy/ndarray.cpp
new file mode 100644
index 000000000..d7b57aa73
--- /dev/null
+++ b/src/boost/libs/python/example/numpy/ndarray.cpp
@@ -0,0 +1,71 @@
+// Copyright Ankit Daftery 2011-2012.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+/**
+ * @brief An example to show how to create ndarrays using arbitrary Python sequences.
+ *
+ * The Python sequence could be any object whose __array__ method returns an array, or any
+ * (nested) sequence. This example also shows how to create arrays using both unit and
+ * non-unit strides.
+ */
+
+#include <boost/python/numpy.hpp>
+#include <iostream>
+
+namespace p = boost::python;
+namespace np = boost::python::numpy;
+
+#if _MSC_VER
+using boost::uint8_t;
+#endif
+
+int main(int argc, char **argv)
+{
+ // Initialize the Python runtime.
+ Py_Initialize();
+ // Initialize NumPy
+ np::initialize();
+ // Create an ndarray from a simple tuple
+ p::object tu = p::make_tuple('a','b','c') ;
+ np::ndarray example_tuple = np::array (tu) ;
+ // and from a list
+ p::list l ;
+ np::ndarray example_list = np::array (l) ;
+ // Optionally, you can also specify a dtype
+ np::dtype dt = np::dtype::get_builtin<int>();
+ np::ndarray example_list1 = np::array (l,dt);
+ // You can also create an array by supplying data.First,create an integer array
+ int data[] = {1,2,3,4} ;
+ // Create a shape, and strides, needed by the function
+ p::tuple shape = p::make_tuple(4) ;
+ p::tuple stride = p::make_tuple(4) ;
+ // The function also needs an owner, to keep track of the data array passed. Passing none is dangerous
+ p::object own ;
+ // The from_data function takes the data array, datatype,shape,stride and owner as arguments
+ // and returns an ndarray
+ np::ndarray data_ex = np::from_data(data,dt,shape,stride,own);
+ // Print the ndarray we created
+ std::cout << "Single dimensional array ::" << std::endl << p::extract < char const * > (p::str(data_ex)) << std::endl ;
+ // Now lets make an 3x2 ndarray from a multi-dimensional array using non-unit strides
+ // First lets create a 3x4 array of 8-bit integers
+ uint8_t mul_data[][4] = {{1,2,3,4},{5,6,7,8},{1,3,5,7}};
+ // Now let's create an array of 3x2 elements, picking the first and third elements from each row
+ // For that, the shape will be 3x2
+ shape = p::make_tuple(3,2) ;
+ // The strides will be 4x2 i.e. 4 bytes to go to the next desired row, and 2 bytes to go to the next desired column
+ stride = p::make_tuple(4,2) ;
+ // Get the numpy dtype for the built-in 8-bit integer data type
+ np::dtype dt1 = np::dtype::get_builtin<uint8_t>();
+ // First lets create and print out the ndarray as is
+ np::ndarray mul_data_ex = np::from_data(mul_data,dt1, p::make_tuple(3,4),p::make_tuple(4,1),p::object());
+ std::cout << "Original multi dimensional array :: " << std::endl << p::extract < char const * > (p::str(mul_data_ex)) << std::endl ;
+ // Now create the new ndarray using the shape and strides
+ mul_data_ex = np::from_data(mul_data,dt1, shape,stride,p::object());
+ // Print out the array we created using non-unit strides
+ std::cout << "Selective multidimensional array :: "<<std::endl << p::extract < char const * > (p::str(mul_data_ex)) << std::endl ;
+
+}
+
+
diff --git a/src/boost/libs/python/example/numpy/simple.cpp b/src/boost/libs/python/example/numpy/simple.cpp
new file mode 100644
index 000000000..ad598bde4
--- /dev/null
+++ b/src/boost/libs/python/example/numpy/simple.cpp
@@ -0,0 +1,32 @@
+// Copyright 2011 Stefan Seefeld.
+// Distributed under the Boost Software License, Version 1.0. (See
+// accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#include <boost/python/numpy.hpp>
+#include <iostream>
+
+namespace p = boost::python;
+namespace np = boost::python::numpy;
+
+int main(int argc, char **argv)
+{
+ // Initialize the Python runtime.
+ Py_Initialize();
+ // Initialize NumPy
+ np::initialize();
+ // Create a 3x3 shape...
+ p::tuple shape = p::make_tuple(3, 3);
+ // ...as well as a type for C++ float
+ np::dtype dtype = np::dtype::get_builtin<float>();
+ // Construct an array with the above shape and type
+ np::ndarray a = np::zeros(shape, dtype);
+ // Construct an empty array with the above shape and dtype as well
+ np::ndarray b = np::empty(shape,dtype);
+ // Print the array
+ std::cout << "Original array:\n" << p::extract<char const *>(p::str(a)) << std::endl;
+ // Reshape the array into a 1D array
+ a = a.reshape(p::make_tuple(9));
+ // Print it again.
+ std::cout << "Reshaped array:\n" << p::extract<char const *>(p::str(a)) << std::endl;
+}
diff --git a/src/boost/libs/python/example/numpy/ufunc.cpp b/src/boost/libs/python/example/numpy/ufunc.cpp
new file mode 100644
index 000000000..5fb692045
--- /dev/null
+++ b/src/boost/libs/python/example/numpy/ufunc.cpp
@@ -0,0 +1,86 @@
+// Copyright Ankit Daftery 2011-2012.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+/**
+ * @brief An example to demonstrate use of universal functions or ufuncs
+ *
+ *
+ * @todo Calling the overloaded () operator is in a roundabout manner, find a simpler way
+ * None of the methods like np::add, np::multiply etc are supported as yet
+ */
+
+#include <boost/python/numpy.hpp>
+#include <iostream>
+
+namespace p = boost::python;
+namespace np = boost::python::numpy;
+
+
+// Create the structs necessary to implement the ufuncs
+// The typedefs *must* be made
+
+struct UnarySquare
+{
+ typedef double argument_type;
+ typedef double result_type;
+
+ double operator()(double r) const { return r * r;}
+};
+
+struct BinarySquare
+{
+ typedef double first_argument_type;
+ typedef double second_argument_type;
+ typedef double result_type;
+
+ double operator()(double a,double b) const { return (a*a + b*b) ; }
+};
+
+int main(int argc, char **argv)
+{
+ // Initialize the Python runtime.
+ Py_Initialize();
+ // Initialize NumPy
+ np::initialize();
+ // Expose the struct UnarySquare to Python as a class, and let ud be the class object
+ p::object ud = p::class_<UnarySquare, boost::shared_ptr<UnarySquare> >("UnarySquare")
+ .def("__call__", np::unary_ufunc<UnarySquare>::make());
+ // Let inst be an instance of the class ud
+ p::object inst = ud();
+ // Use the "__call__" method to call the overloaded () operator and print the value
+ std::cout << "Square of unary scalar 1.0 is " << p::extract <char const * > (p::str(inst.attr("__call__")(1.0))) << std::endl ;
+ // Create an array in C++
+ int arr[] = {1,2,3,4} ;
+ // ..and use it to create the ndarray in Python
+ np::ndarray demo_array = np::from_data(arr, np::dtype::get_builtin<int>() , p::make_tuple(4), p::make_tuple(4), p::object());
+ // Print out the demo array
+ std::cout << "Demo array is " << p::extract <char const * > (p::str(demo_array)) << std::endl ;
+ // Call the "__call__" method to perform the operation and assign the value to result_array
+ p::object result_array = inst.attr("__call__")(demo_array) ;
+ // Print the resultant array
+ std::cout << "Square of demo array is " << p::extract <char const * > (p::str(result_array)) << std::endl ;
+ // Lets try the same with a list
+ p::list li ;
+ li.append(3);
+ li.append(7);
+ // Print out the demo list
+ std::cout << "Demo list is " << p::extract <char const * > (p::str(li)) << std::endl ;
+ // Call the ufunc for the list
+ result_array = inst.attr("__call__")(li) ;
+ // And print the list out
+ std::cout << "Square of demo list is " << p::extract <char const * > (p::str(result_array)) << std::endl ;
+ // Now lets try Binary ufuncs
+ // Expose the struct BinarySquare to Python as a class, and let ud be the class object
+ ud = p::class_<BinarySquare, boost::shared_ptr<BinarySquare> >("BinarySquare")
+ .def("__call__", np::binary_ufunc<BinarySquare>::make());
+ // Again initialise inst as an instance of the class ud
+ inst = ud();
+ // Print the two input listsPrint the two input lists
+ std::cout << "The two input list for binary ufunc are " << std::endl << p::extract <char const * > (p::str(demo_array)) << std::endl << p::extract <char const * > (p::str(demo_array)) << std::endl ;
+ // Call the binary ufunc taking demo_array as both inputs
+ result_array = inst.attr("__call__")(demo_array,demo_array) ;
+ std::cout << "Square of list with binary ufunc is " << p::extract <char const * > (p::str(result_array)) << std::endl ;
+}
+
diff --git a/src/boost/libs/python/example/numpy/wrap.cpp b/src/boost/libs/python/example/numpy/wrap.cpp
new file mode 100644
index 000000000..33f71c85a
--- /dev/null
+++ b/src/boost/libs/python/example/numpy/wrap.cpp
@@ -0,0 +1,135 @@
+// Copyright Jim Bosch 2011-2012.
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+/**
+ * A simple example showing how to wrap a couple of C++ functions that
+ * operate on 2-d arrays into Python functions that take NumPy arrays
+ * as arguments.
+ *
+ * If you find have a lot of such functions to wrap, you may want to
+ * create a C++ array type (or use one of the many existing C++ array
+ * libraries) that maps well to NumPy arrays and create Boost.Python
+ * converters. There's more work up front than the approach here,
+ * but much less boilerplate per function. See the "Gaussian" example
+ * included with Boost.NumPy for an example of custom converters, or
+ * take a look at the "ndarray" project on GitHub for a more complete,
+ * high-level solution.
+ *
+ * Note that we're using embedded Python here only to make a convenient
+ * self-contained example; you could just as easily put the wrappers
+ * in a regular C++-compiled module and imported them in regular
+ * Python. Again, see the Gaussian demo for an example.
+ */
+
+#include <boost/python/numpy.hpp>
+#include <boost/scoped_array.hpp>
+#include <iostream>
+
+namespace p = boost::python;
+namespace np = boost::python::numpy;
+
+// This is roughly the most efficient way to write a C/C++ function that operates
+// on a 2-d NumPy array - operate directly on the array by incrementing a pointer
+// with the strides.
+void fill1(double * array, int rows, int cols, int row_stride, int col_stride) {
+ double * row_iter = array;
+ double n = 0.0; // just a counter we'll fill the array with.
+ for (int i = 0; i < rows; ++i, row_iter += row_stride) {
+ double * col_iter = row_iter;
+ for (int j = 0; j < cols; ++j, col_iter += col_stride) {
+ *col_iter = ++n;
+ }
+ }
+}
+
+// Here's a simple wrapper function for fill1. It requires that the passed
+// NumPy array be exactly what we're looking for - no conversion from nested
+// sequences or arrays with other data types, because we want to modify it
+// in-place.
+void wrap_fill1(np::ndarray const & array) {
+ if (array.get_dtype() != np::dtype::get_builtin<double>()) {
+ PyErr_SetString(PyExc_TypeError, "Incorrect array data type");
+ p::throw_error_already_set();
+ }
+ if (array.get_nd() != 2) {
+ PyErr_SetString(PyExc_TypeError, "Incorrect number of dimensions");
+ p::throw_error_already_set();
+ }
+ fill1(reinterpret_cast<double*>(array.get_data()),
+ array.shape(0), array.shape(1),
+ array.strides(0) / sizeof(double), array.strides(1) / sizeof(double));
+}
+
+// Another fill function that takes a double**. This is less efficient, because
+// it's not the native NumPy data layout, but it's common enough in C/C++ that
+// it's worth its own example. This time we don't pass the strides, and instead
+// in wrap_fill2 we'll require the C_CONTIGUOUS bitflag, which guarantees that
+// the column stride is 1 and the row stride is the number of columns. That
+// restricts the arrays that can be passed to fill2 (it won't work on most
+// subarray views or transposes, for instance).
+void fill2(double ** array, int rows, int cols) {
+ double n = 0.0; // just a counter we'll fill the array with.
+ for (int i = 0; i < rows; ++i) {
+ for (int j = 0; j < cols; ++j) {
+ array[i][j] = ++n;
+ }
+ }
+}
+// Here's the wrapper for fill2; it's a little more complicated because we need
+// to check the flags and create the array of pointers.
+void wrap_fill2(np::ndarray const & array) {
+ if (array.get_dtype() != np::dtype::get_builtin<double>()) {
+ PyErr_SetString(PyExc_TypeError, "Incorrect array data type");
+ p::throw_error_already_set();
+ }
+ if (array.get_nd() != 2) {
+ PyErr_SetString(PyExc_TypeError, "Incorrect number of dimensions");
+ p::throw_error_already_set();
+ }
+ if (!(array.get_flags() & np::ndarray::C_CONTIGUOUS)) {
+ PyErr_SetString(PyExc_TypeError, "Array must be row-major contiguous");
+ p::throw_error_already_set();
+ }
+ double * iter = reinterpret_cast<double*>(array.get_data());
+ int rows = array.shape(0);
+ int cols = array.shape(1);
+ boost::scoped_array<double*> ptrs(new double*[rows]);
+ for (int i = 0; i < rows; ++i, iter += cols) {
+ ptrs[i] = iter;
+ }
+ fill2(ptrs.get(), array.shape(0), array.shape(1));
+}
+
+BOOST_PYTHON_MODULE(example) {
+ np::initialize(); // have to put this in any module that uses Boost.NumPy
+ p::def("fill1", wrap_fill1);
+ p::def("fill2", wrap_fill2);
+}
+
+int main(int argc, char **argv)
+{
+ // This line makes our module available to the embedded Python intepreter.
+# if PY_VERSION_HEX >= 0x03000000
+ PyImport_AppendInittab("example", &PyInit_example);
+# else
+ PyImport_AppendInittab("example", &initexample);
+# endif
+ // Initialize the Python runtime.
+ Py_Initialize();
+
+ PyRun_SimpleString(
+ "import example\n"
+ "import numpy\n"
+ "z1 = numpy.zeros((5,6), dtype=float)\n"
+ "z2 = numpy.zeros((4,3), dtype=float)\n"
+ "example.fill1(z1)\n"
+ "example.fill2(z2)\n"
+ "print z1\n"
+ "print z2\n"
+ );
+ Py_Finalize();
+}
+
+