121 lines
3.5 KiB
ReStructuredText
121 lines
3.5 KiB
ReStructuredText
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;
|
|
}
|
|
|