/////////////////////////////////////////////////////////////////////////// // // Copyright (c) 2007-2011, Industrial Light & Magic, a division of Lucas // Digital Ltd. LLC // // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following disclaimer // in the documentation and/or other materials provided with the // distribution. // * Neither the name of Industrial Light & Magic nor the names of // its contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // /////////////////////////////////////////////////////////////////////////// #ifndef _PyImathAutovectorize_h_ #define _PyImathAutovectorize_h_ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace PyImath { struct op_with_precomputation {}; namespace detail { using boost::is_base_of; using boost::is_same; using boost::is_const; using boost::remove_const; using boost::remove_reference; using boost::function_traits; using boost::mpl::at; using boost::mpl::at_c; using boost::mpl::push_front; using boost::mpl::vector; using boost::mpl::push_back; using boost::mpl::transform; using boost::mpl::fold; using boost::mpl::_; using boost::mpl::_1; using boost::mpl::_2; using boost::mpl::long_; using boost::mpl::false_; using boost::mpl::true_; using boost::mpl::not_; using boost::mpl::or_; using boost::mpl::and_; using boost::mpl::size; using boost::mpl::remove_if; using boost::mpl::if_; using boost::mpl::for_each; template struct name_of_type; template <> struct name_of_type { static const char *apply() { return "int"; } }; template <> struct name_of_type { static const char *apply() { return "float"; } }; template <> struct name_of_type { static const char *apply() { return "double"; } }; template struct name_of_type > { static const char *apply() { return PyImath::FixedArray::name(); } }; struct null_precomputation { static void precompute(size_t len) { return; } }; template struct op_precompute { static void apply(size_t len) { if_, T, null_precomputation>::type::precompute(len); } }; template struct possible_vectorizations { typedef typename fold< typename possible_vectorizations::type, vector<>, push_back >,push_back<_2,true_> > >::type type; }; template <> struct possible_vectorizations<0> { typedef vector > type; }; template struct disallow_vectorization { template struct apply { // Valid = !Vectorize || Vectorizable typedef typename transform >::type DontVectorize; typedef typename transform >::type Valid; typedef typename not_ > >::type type; }; }; template struct allowable_vectorizations { typedef typename possible_vectorizations::value>::type possible; typedef typename remove_if >::type type; }; template bool any_masked(const T &value) { return false; }; template bool any_masked(const PyImath::FixedArray &value) { return value.isMaskedReference(); }; template bool any_masked(const T1 &a, const T2 &b) { return any_masked(a) || any_masked(b); } template bool any_masked(const T1 &a, const T2 &b, const T3 &c) { return any_masked(a,b) || any_masked(c); } template bool any_masked(const T1 &a, const T2 &b, const T3 &c, const T4 &d) { return any_masked(a,b) || any_masked(c,d); } template struct access_value { static inline T & apply(T &arg, size_t i) { return arg; } }; template struct access_value { static inline T & apply(T &arg, size_t i) { return arg; } }; template struct access_value &> { static inline T & apply(PyImath::FixedArray &arg, size_t i) { return arg[i]; } }; template struct access_value &> { static inline const T & apply(const PyImath::FixedArray &arg, size_t i) { return arg[i]; } }; template struct direct_access_value { static inline T & apply(T &arg, size_t i) { return arg; } }; template struct direct_access_value { static inline T & apply(T &arg, size_t i) { return arg; } }; template struct direct_access_value &> { static inline T & apply(PyImath::FixedArray &arg, size_t i) { return arg.direct_index(i); } }; template struct direct_access_value &> { static inline const T & apply(const PyImath::FixedArray &arg, size_t i) { return arg.direct_index(i); } }; //----------------------------------------------------------------------------------------- // // measure_argument returns a pair indicating the integral length of the argument // (scalar arguments have implicit length 1), and a bool indicating whether the argument // is a vectorized argument. // template struct measure_argument { static inline std::pair apply(T arg) { return std::make_pair(1,false); } }; template struct measure_argument > { static inline std::pair apply(const PyImath::FixedArray &arg) { return std::make_pair(arg.len(),true); } }; // // match_lengths returns the compatible length given two argument lengths // static inline std::pair match_lengths(const std::pair &len1, const std::pair &len2) { // scalar arguemnts are always compatible with other arguments if (len1.second == false) return len2; if (len2.second == false) return len1; // now both arguments are vectorized, check for dimension match if (len1.first != len2.first) throw IEX_NAMESPACE::ArgExc("Array dimensions passed into function do not match"); return len1; } // // measure_arguments finds the length that a return value from a given // set of arguments should have, throwing an exception if the lengths // are incompatible. If all arguments are scalar, length 1 is returned. // template size_t measure_arguments(const arg1_type &arg1) { std::pair len = measure_argument::apply(arg1); return len.first; } template size_t measure_arguments(const arg1_type &arg1, const arg2_type &arg2) { std::pair len = measure_argument::apply(arg1); len = match_lengths(len,measure_argument::apply(arg2)); return len.first; } template size_t measure_arguments(const arg1_type &arg1, const arg2_type &arg2, const arg3_type &arg3) { std::pair len = measure_argument::apply(arg1); len = match_lengths(len,measure_argument::apply(arg2)); len = match_lengths(len,measure_argument::apply(arg3)); return len.first; } //----------------------------------------------------------------------------------------- template struct create_uninitalized_return_value { static T apply(size_t length) { return T(); } }; template struct create_uninitalized_return_value > { static PyImath::FixedArray apply(size_t length) { return PyImath::FixedArray(Py_ssize_t(length),PyImath::UNINITIALIZED); } }; template struct vectorized_result_type { typedef typename if_,T>::type type; }; template struct vectorized_argument_type { typedef typename remove_const::type>::type base_type; typedef typename if_ &,T>::type type; }; template struct VectorizedOperation1 : public Task { result_type &retval; arg1_type arg1; VectorizedOperation1(result_type &r, arg1_type a1) : retval(r), arg1(a1) {} void execute(size_t start, size_t end) { if (any_masked(retval,arg1)) { for (size_t i=start; i::apply(retval,i) = Op::apply(access_value::apply(arg1,i)); } } else { for (size_t i=start; i::apply(retval,i) = Op::apply(direct_access_value::apply(arg1,i)); } } } }; template struct VectorizedOperation2 : public Task { result_type &retval; arg1_type arg1; arg2_type arg2; VectorizedOperation2(result_type &r, arg1_type a1, arg2_type a2) : retval(r), arg1(a1), arg2(a2) {} void execute(size_t start, size_t end) { if (any_masked(retval,arg1,arg2)) { for (size_t i=start; i::apply(retval,i) = Op::apply(access_value::apply(arg1,i), access_value::apply(arg2,i)); } } else { for (size_t i=start; i::apply(retval,i) = Op::apply(direct_access_value::apply(arg1,i), direct_access_value::apply(arg2,i)); } } } }; template struct VectorizedOperation3 : public Task { result_type &retval; arg1_type arg1; arg2_type arg2; arg3_type arg3; VectorizedOperation3(result_type &r, arg1_type a1, arg2_type a2, arg3_type a3) : retval(r), arg1(a1), arg2(a2), arg3(a3) {} void execute(size_t start, size_t end) { if (any_masked(retval,arg1,arg2,arg3)) { for (size_t i=start; i::apply(retval,i) = Op::apply(access_value::apply(arg1,i), access_value::apply(arg2,i), access_value::apply(arg3,i)); } } else { for (size_t i=start; i::apply(retval,i) = Op::apply(direct_access_value::apply(arg1,i), direct_access_value::apply(arg2,i), direct_access_value::apply(arg3,i)); } } } }; template struct VectorizedFunction1 { BOOST_STATIC_ASSERT((size::value == function_traits::arity)); typedef function_traits traits; typedef typename fold >::type any_vectorized; typedef typename vectorized_result_type::type result_type; typedef typename vectorized_argument_type >::type>::type arg1_type; static result_type apply(arg1_type arg1) { PY_IMATH_LEAVE_PYTHON; size_t len = measure_arguments(arg1); op_precompute::apply(len); result_type retval = create_uninitalized_return_value::apply(len); VectorizedOperation1 vop(retval,arg1); dispatchTask(vop,len); mathexcon.handleOutstandingExceptions(); return retval; } static std::string format_arguments(const boost::python::detail::keywords<1> &args) { // TODO: add types here return std::string("(")+args.elements[0].name+") - "; } }; template struct VectorizedFunction2 { BOOST_STATIC_ASSERT((size::value == function_traits::arity)); typedef function_traits traits; typedef typename fold >::type any_vectorized; typedef typename vectorized_result_type::type result_type; typedef typename vectorized_argument_type >::type>::type arg1_type; typedef typename vectorized_argument_type >::type>::type arg2_type; static result_type apply(arg1_type arg1, arg2_type arg2) { PY_IMATH_LEAVE_PYTHON; size_t len = measure_arguments(arg1,arg2); op_precompute::apply(len); result_type retval = create_uninitalized_return_value::apply(len); VectorizedOperation2 vop(retval,arg1,arg2); dispatchTask(vop,len); mathexcon.handleOutstandingExceptions(); return retval; } static std::string format_arguments(const boost::python::detail::keywords<2> &args) { // TODO: add types here return std::string("(")+args.elements[0].name+","+args.elements[1].name+") - "; } }; template struct VectorizedFunction3 { BOOST_STATIC_ASSERT((size::value == function_traits::arity)); typedef function_traits traits; typedef typename fold >::type any_vectorized; typedef typename vectorized_result_type::type result_type; typedef typename vectorized_argument_type >::type>::type arg1_type; typedef typename vectorized_argument_type >::type>::type arg2_type; typedef typename vectorized_argument_type >::type>::type arg3_type; static result_type apply(arg1_type arg1, arg2_type arg2, arg3_type arg3) { PY_IMATH_LEAVE_PYTHON; size_t len = measure_arguments(arg1,arg2,arg3); op_precompute::apply(len); result_type retval = create_uninitalized_return_value::apply(len); VectorizedOperation3 vop(retval,arg1,arg2,arg3); dispatchTask(vop,len); mathexcon.handleOutstandingExceptions(); return retval; } static std::string format_arguments(const boost::python::detail::keywords<3> &args) { // TODO: add types here return std::string("(")+args.elements[0].name+","+args.elements[1].name+","+args.elements[2].name+") - "; } }; template struct function_binding { std::string _name, _doc; const Keywords &_args; function_binding(const std::string &name, const std::string &doc,const Keywords &args) : _name(name), _doc(doc), _args(args) {} template void operator()(Vectorize) const { typedef typename at, VectorizedFunction2, VectorizedFunction3 >, long_::arity> >::type vectorized_function_type; std::string doc = _name + vectorized_function_type::format_arguments(_args) + _doc; boost::python::def(_name.c_str(),&vectorized_function_type::apply,doc.c_str(),_args); } }; template function_binding build_function_binding(Func *func,const std::string &name,const std::string &doc,const Keywords &args) { return function_binding(name,doc,args); } template struct generate_bindings_struct { //BOOST_STATIC_ASSERT(size::value == function_traits::arity); static void apply(const std::string &name,const std::string &doc,const Keywords &args) { for_each::type>(build_function_binding(Op::apply,name,doc,args)); } }; template struct vectorized_class_reference_type { typedef typename remove_const::type>::type base_type; typedef typename if_,const PyImath::FixedArray &,PyImath::FixedArray &>::type type; }; template struct VectorizedVoidOperation0 : public Task { class_type cls; VectorizedVoidOperation0(class_type c) : cls(c) {} void execute(size_t start, size_t end) { if (any_masked(cls)) { for (size_t i=start; i::apply(cls,i)); } } else { for (size_t i=start; i::apply(cls,i)); } } } }; template struct VectorizedVoidOperation1 : public Task { class_type cls; arg1_type arg1; VectorizedVoidOperation1(class_type c, arg1_type a1) : cls(c), arg1(a1) {} void execute(size_t start, size_t end) { if (any_masked(cls,arg1)) { for (size_t i=start; i::apply(cls,i), access_value::apply(arg1,i)); } } else { for (size_t i=start; i::apply(cls,i), direct_access_value::apply(arg1,i)); } } } }; template struct VectorizedMaskedVoidOperation1 : public Task { class_type cls; arg1_type arg1; VectorizedMaskedVoidOperation1(class_type c, arg1_type a1) : cls(c), arg1(a1) {} void execute(size_t start, size_t end) { if (any_masked(arg1)) { for (size_t i=start; i::apply(cls,i), access_value::apply(arg1,cls.raw_ptr_index(i))); } } else { for (size_t i=start; i::apply(cls,i), direct_access_value::apply(arg1,cls.raw_ptr_index(i))); } } } }; template struct VectorizedVoidOperation2 : public Task { class_type cls; arg1_type arg1; arg2_type arg2; VectorizedVoidOperation2(class_type c, arg1_type a1, arg2_type a2) : cls(c), arg1(a1), arg2(a2) {} void execute(size_t start, size_t end) { if (any_masked(cls,arg1,arg2)) { for (size_t i=start; i::apply(cls,i), access_value::apply(arg1,i), access_value::apply(arg2,i)); } } else { for (size_t i=start; i::apply(cls,i), direct_access_value::apply(arg1,i), direct_access_value::apply(arg2,i)); } } } }; template struct VectorizedVoidMemberFunction0 { BOOST_STATIC_ASSERT((size::value+1 == function_traits::arity)); typedef function_traits traits; typedef typename vectorized_class_reference_type::type class_type; static class_type apply(class_type cls) { PY_IMATH_LEAVE_PYTHON; size_t len = measure_arguments(cls); op_precompute::apply(len); VectorizedVoidOperation0 vop(cls); dispatchTask(vop,len); mathexcon.handleOutstandingExceptions(); return cls; } }; template struct VectorizedVoidMemberFunction1 { BOOST_STATIC_ASSERT((size::value+1 == function_traits::arity)); typedef function_traits traits; typedef typename vectorized_class_reference_type::type class_type; typedef typename vectorized_argument_type >::type>::type arg1_type; static class_type apply(class_type cls, arg1_type arg1) { PY_IMATH_LEAVE_PYTHON; size_t len = measure_arguments(cls,arg1); op_precompute::apply(len); VectorizedVoidOperation1 vop(cls,arg1); dispatchTask(vop,len); mathexcon.handleOutstandingExceptions(); return cls; } static std::string format_arguments(const boost::python::detail::keywords<1> &args) { // TODO: add types here return std::string("(")+args.elements[0].name+") - "; } }; // // special class to handle single argument void memberfunctions, such as those // used for the inplace operators like +=, -=, etc. In this case we allow additional // compatibilty between a masked class and an unmasked right hand side, using the // mask to select results. // template struct VectorizedVoidMaskableMemberFunction1 { BOOST_STATIC_ASSERT((2 == function_traits::arity)); typedef function_traits traits; typedef typename vectorized_class_reference_type::type class_type; typedef typename vectorized_argument_type::type arg1_type; static class_type apply(class_type cls, arg1_type arg1) { PY_IMATH_LEAVE_PYTHON; size_t len = cls.match_dimension(arg1, false); op_precompute::apply(len); if (cls.isMaskedReference() && arg1.len() == cls.unmaskedLength()) { // class is masked, and the unmasked length matches the right hand side VectorizedMaskedVoidOperation1 vop(cls,arg1); dispatchTask(vop,len); } else { // the two arrays match length (masked or otherwise), use the standard path. VectorizedVoidOperation1 vop(cls,arg1); dispatchTask(vop,len); } mathexcon.handleOutstandingExceptions(); return cls; } static std::string format_arguments(const boost::python::detail::keywords<1> &args) { // TODO: add types here return std::string("(")+args.elements[0].name+") - "; } }; template struct VectorizedVoidMemberFunction2 { BOOST_STATIC_ASSERT((size::value+1 == function_traits::arity)); typedef function_traits traits; typedef typename vectorized_class_reference_type::type class_type; typedef typename vectorized_argument_type >::type>::type arg1_type; typedef typename vectorized_argument_type >::type>::type arg2_type; static class_type apply(class_type cls, arg1_type arg1, arg2_type arg2) { PY_IMATH_LEAVE_PYTHON; size_t len = measure_arguments(cls,arg1,arg2); op_precompute::apply(len); VectorizedVoidOperation2 vop(cls,arg1,arg2); dispatchTask(vop,len); mathexcon.handleOutstandingExceptions(); return cls; } static std::string format_arguments(const boost::python::detail::keywords<2> &args) { // TODO: add types here return std::string("(")+args.elements[0].name+","+args.elements[1].name+") - "; } }; template struct VectorizedMemberFunction0 { BOOST_STATIC_ASSERT((size::value+1 == function_traits::arity)); typedef function_traits traits; typedef typename vectorized_result_type::type result_type; typedef typename vectorized_class_reference_type::type class_type; static result_type apply(class_type cls) { PY_IMATH_LEAVE_PYTHON; size_t len = measure_arguments(cls); op_precompute::apply(len); result_type retval = create_uninitalized_return_value::apply(len); VectorizedOperation1 vop(retval,cls); dispatchTask(vop,len); mathexcon.handleOutstandingExceptions(); return retval; } }; template struct VectorizedMemberFunction1 { BOOST_STATIC_ASSERT((size::value+1 == function_traits::arity)); typedef function_traits traits; typedef typename vectorized_result_type::type result_type; typedef typename vectorized_class_reference_type::type class_type; typedef typename vectorized_argument_type >::type>::type arg1_type; static result_type apply(class_type cls, arg1_type arg1) { PY_IMATH_LEAVE_PYTHON; size_t len = measure_arguments(cls,arg1); op_precompute::apply(len); result_type retval = create_uninitalized_return_value::apply(len); VectorizedOperation2 vop(retval,cls,arg1); dispatchTask(vop,len); mathexcon.handleOutstandingExceptions(); return retval; } static std::string format_arguments(const boost::python::detail::keywords<1> &args) { // TODO: add types here return std::string("(")+args.elements[0].name+") - "; } }; template struct VectorizedMemberFunction2 { BOOST_STATIC_ASSERT((size::value+1 == function_traits::arity)); typedef function_traits traits; typedef typename vectorized_result_type::type result_type; typedef typename vectorized_class_reference_type::type class_type; typedef typename vectorized_argument_type >::type>::type arg1_type; typedef typename vectorized_argument_type >::type>::type arg2_type; static result_type apply(class_type cls, arg1_type arg1, arg2_type arg2) { PY_IMATH_LEAVE_PYTHON; size_t len = measure_arguments(cls,arg1,arg2); op_precompute::apply(len); result_type retval = create_uninitalized_return_value::apply(len); VectorizedOperation3 vop(retval,cls,arg1,arg2); dispatchTask(vop,len); mathexcon.handleOutstandingExceptions(); return retval; } static std::string format_arguments(const boost::python::detail::keywords<2> &args) { // TODO: add types here return std::string("(")+args.elements[0].name+","+args.elements[1].name+") - "; } }; template struct member_function_binding { Cls &_cls; std::string _name, _doc; const Keywords &_args; member_function_binding(Cls &cls,const std::string &name, const std::string &doc,const Keywords &args) : _cls(cls), _name(name), _doc(doc), _args(args) {} template void operator()(Vectorize) const { typedef typename if_::result_type>, typename if_ >, VectorizedVoidMaskableMemberFunction1, VectorizedVoidMemberFunction1 >::type, VectorizedMemberFunction1 >::type member_func1_type; typedef typename if_::result_type>, VectorizedVoidMemberFunction2, VectorizedMemberFunction2 >::type member_func2_type; typedef typename if_::result_type>, boost::python::return_internal_reference<>, // the void vectorizations return a reference to self boost::python::default_call_policies>::type call_policies; typedef typename at, long_::arity> >::type vectorized_function_type; std::string doc = _name + vectorized_function_type::format_arguments(_args) + _doc; _cls.def(_name.c_str(),&vectorized_function_type::apply,doc.c_str(),_args,call_policies()); } }; template member_function_binding build_member_function_binding(Cls &cls,Func *func,const std::string &name,const std::string &doc,const Keywords &args) { return member_function_binding(cls,name,doc,args); } template struct generate_member_bindings_struct { //BOOST_STATIC_ASSERT(size::value+1 == function_traits::arity); static void apply(Cls &cls,const std::string &name,const std::string &doc,const Keywords &args) { for_each::type>(build_member_function_binding(cls,Op::apply,name,doc,args)); } }; template void generate_single_member_binding(Cls &cls,Func *func,const std::string &name,const std::string &doc) { typedef typename if_::result_type>, VectorizedVoidMemberFunction0,Func>, VectorizedMemberFunction0,Func> >::type vectorized_function_type; typedef typename if_::result_type>, boost::python::return_internal_reference<>, // the void vectorizations return a reference to self boost::python::default_call_policies>::type call_policies; cls.def(name.c_str(),&vectorized_function_type::apply,doc.c_str(),call_policies()); } } // namespace detail // TODO: update for arg("name")=default_value syntax template void generate_bindings(const std::string &name,const std::string &doc,const boost::python::detail::keywords<1> &args) { using namespace detail; generate_bindings_struct,boost::python::detail::keywords<1> >::apply(name,doc,args); } template void generate_bindings(const std::string &name,const std::string &doc,const boost::python::detail::keywords<2> &args) { using namespace detail; generate_bindings_struct,boost::python::detail::keywords<2> >::apply(name,doc,args); } template void generate_bindings(const std::string &name,const std::string &doc,const boost::python::detail::keywords<3> &args) { using namespace detail; generate_bindings_struct,boost::python::detail::keywords<3> >::apply(name,doc,args); } template void generate_member_bindings(Cls &cls,const std::string &name,const std::string &doc) { using namespace detail; generate_single_member_binding(cls,&Op::apply,name,doc); } template void generate_member_bindings(Cls &cls,const std::string &name,const std::string &doc,const boost::python::detail::keywords<1> &args) { using boost::mpl::vector; detail::generate_member_bindings_struct,boost::python::detail::keywords<1> >::apply(cls,name,doc,args); } template void generate_member_bindings(Cls &cls,const std::string &name,const std::string &doc,const boost::python::detail::keywords<2> &args) { using boost::mpl::vector; detail::generate_member_bindings_struct,boost::python::detail::keywords<2> >::apply(cls,name,doc,args); } } // namespace PyImath #endif // _PyImathAutovectorize_h_