C++ Boost

(NumPy)


Ufuncs

Ufuncs or universal functions operate on ndarrays element by element, and support array broadcasting, type casting, and other features.

Lets try and see how we can use the binary and unary ufunc methods

After the neccessary includes

#include <boost/python/numpy.hpp>
#include <iostream>

namespace p = boost::python;
namespace np = boost::python::numpy;

Now we create the structs necessary to implement the ufuncs. The typedefs must be made as the ufunc generators take these typedefs as inputs and return an error otherwise

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) ; }
};

Initialise the Python runtime and the numpy module

int main(int argc, char **argv)
{
  Py_Initialize();
  np::initialize();

Now 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");
ud.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. Again, 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");
ud.def("__call__", np::binary_ufunc<BinarySquare>::make());

And initialise ud

inst = ud();

Print 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);

And print the output

  std::cout << "Square of list with binary ufunc is " << p::extract<char const *>(p::str(result_array)) << std::endl;
}