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