diff --git a/.gitignore b/.gitignore index 64d811d4bed..ab2dd6a91bb 100644 --- a/.gitignore +++ b/.gitignore @@ -71,6 +71,7 @@ lib/tbb # local make include /make/local +/make/ucrt # python byte code *.pyc diff --git a/make/compiler_flags b/make/compiler_flags index 1552381f232..a4c6224e6da 100644 --- a/make/compiler_flags +++ b/make/compiler_flags @@ -161,6 +161,21 @@ ifeq ($(OS),Windows_NT) CXXFLAGS_OS ?= -m64 endif + make/ucrt: + pound := \# + UCRT_STRING := $(shell echo '$(pound)include ' | $(CXX) -E -dM - | findstr _UCRT) + ifneq (,$(UCRT_STRING)) + IS_UCRT ?= true + else + IS_UCRT ?= false + endif + $(shell echo "IS_UCRT ?= $(IS_UCRT)" > $(MATH)make/ucrt) + + include make/ucrt + ifeq ($(IS_UCRT),true) + CXXFLAGS_OS += -D_UCRT + endif + ifneq (gcc,$(CXX_TYPE)) LDLIBS_OS ?= -static-libgcc else diff --git a/make/libraries b/make/libraries index 441f5b8c3e0..7dd5dcbfd45 100644 --- a/make/libraries +++ b/make/libraries @@ -139,6 +139,12 @@ ifeq (Linux, $(OS)) SHELL = /usr/bin/env bash endif +ifeq (Windows_NT, $(OS)) + ifeq ($(IS_UCRT),true) + TBB_CXXFLAGS += -D_UCRT + endif +endif + # If brackets or spaces are found in MAKE on Windows # we error, as those characters cause issues when building. ifeq (Windows_NT, $(OS)) diff --git a/make/tests b/make/tests index 14a2550f08d..fcf379d6520 100644 --- a/make/tests +++ b/make/tests @@ -101,12 +101,15 @@ HEADER_TESTS := $(addsuffix -test,$(call findfiles,stan,*.hpp)) ifeq ($(OS),Windows_NT) DEV_NULL = nul + ifeq ($(IS_UCRT),true) + UCRT_NULL_FLAG = -S + endif else DEV_NULL = /dev/null endif %.hpp-test : %.hpp test/dummy.cpp - $(COMPILE.cpp) $(CXXFLAGS) -O0 -include $^ -o $(DEV_NULL) -Wunused-local-typedefs + $(COMPILE.cpp) $(CXXFLAGS) -O0 -include $^ $(UCRT_NULL_FLAG) -o $(DEV_NULL) -Wunused-local-typedefs test/dummy.cpp: @mkdir -p test diff --git a/makefile b/makefile index f34da7b164b..f14ab40df4e 100644 --- a/makefile +++ b/makefile @@ -125,6 +125,7 @@ clean-deps: @$(RM) $(call findfiles,test,*.d.*) @$(RM) $(call findfiles,lib,*.d.*) @$(RM) $(call findfiles,stan,*.dSYM) + @$(RM) $(call findfiles,make,ucrt) clean-all: clean clean-doxygen clean-deps clean-libraries diff --git a/stan/math/opencl/kernel_generator.hpp b/stan/math/opencl/kernel_generator.hpp index a3cb839d776..fd78ce3f9a8 100644 --- a/stan/math/opencl/kernel_generator.hpp +++ b/stan/math/opencl/kernel_generator.hpp @@ -108,7 +108,7 @@ #include #include #include - +#include #include #include #include diff --git a/stan/math/opencl/kernel_generator/as_operation_cl.hpp b/stan/math/opencl/kernel_generator/as_operation_cl.hpp index c6e5e819174..b87e4fb0a9d 100644 --- a/stan/math/opencl/kernel_generator/as_operation_cl.hpp +++ b/stan/math/opencl/kernel_generator/as_operation_cl.hpp @@ -2,6 +2,7 @@ #define STAN_MATH_OPENCL_KERNEL_GENERATOR_AS_OPERATION_CL_HPP #ifdef STAN_OPENCL +#include #include #include #include @@ -19,11 +20,12 @@ namespace math { /** * Converts any valid kernel generator expression into an operation. This is an * overload for operations - a no-op + * @tparam AssignOp ignored * @tparam T_operation type of the input operation * @param a an operation * @return operation */ -template >::value>> inline T_operation&& as_operation_cl(T_operation&& a) { @@ -33,11 +35,13 @@ inline T_operation&& as_operation_cl(T_operation&& a) { /** * Converts any valid kernel generator expression into an operation. This is an * overload for scalars (arithmetic types). It wraps them into \c scalar_. + * @tparam AssignOp ignored * @tparam T_scalar type of the input scalar * @param a scalar * @return \c scalar_ wrapping the input */ -template , +template , require_not_same_t* = nullptr> inline scalar_ as_operation_cl(const T_scalar a) { return scalar_(a); @@ -47,23 +51,29 @@ inline scalar_ as_operation_cl(const T_scalar a) { * Converts any valid kernel generator expression into an operation. This is an * overload for bool scalars. It wraps them into \c scalar_ as \c bool can * not be used as a type of a kernel argument. + * @tparam AssignOp ignored * @param a scalar * @return \c scalar_ wrapping the input */ -inline scalar_ as_operation_cl(const bool a) { return scalar_(a); } +template +inline scalar_ as_operation_cl(const bool a) { + return scalar_(a); +} /** * Converts any valid kernel generator expression into an operation. This is an * overload for \c matrix_cl. It wraps them into into \c load_. + * @tparam AssignOp an optional `assign_op_cl` that dictates whether the object + * is assigned using standard or compound assign. * @tparam T_matrix_cl \c matrix_cl * @param a \c matrix_cl * @return \c load_ wrapping the input */ -template , is_arena_matrix_cl>> -inline load_ as_operation_cl(T_matrix_cl&& a) { - return load_(std::forward(a)); +inline load_ as_operation_cl(T_matrix_cl&& a) { + return load_(std::forward(a)); } /** @@ -73,12 +83,16 @@ inline load_ as_operation_cl(T_matrix_cl&& a) { * as_operation_cl_t. If the return value of \c as_operation_cl() would be a * rvalue reference, the reference is removed, so that a variable of this type * actually stores the value. + * @tparam T a `matrix_cl` or `Scalar` type + * @tparam AssignOp an optional `assign_op_cl` that dictates whether the object + * is assigned using standard or compound assign. */ -template -using as_operation_cl_t = std::conditional_t< - std::is_lvalue_reference::value, - decltype(as_operation_cl(std::declval())), - std::remove_reference_t()))>>; +template +using as_operation_cl_t + = std::conditional_t::value, + decltype(as_operation_cl(std::declval())), + std::remove_reference_t(std::declval()))>>; /** @}*/ } // namespace math diff --git a/stan/math/opencl/kernel_generator/assignment_ops.hpp b/stan/math/opencl/kernel_generator/assignment_ops.hpp new file mode 100644 index 00000000000..a365492a8e1 --- /dev/null +++ b/stan/math/opencl/kernel_generator/assignment_ops.hpp @@ -0,0 +1,74 @@ +#ifndef STAN_MATH_OPENCL_KERNEL_GENERATOR_ASSIGNMENT_OPS +#define STAN_MATH_OPENCL_KERNEL_GENERATOR_ASSIGNMENT_OPS +#ifdef STAN_OPENCL +#include + +namespace stan { +namespace math { + +/** + * Ops that decide the type of assignment for LHS operations + */ +enum class assign_op_cl { + equals, + plus_equals, + minus_equals, + divide_equals, + multiply_equals +}; + +namespace internal { +/** + * @param value A static constexpr const char* member for printing assignment + * ops + */ +template +struct assignment_op_str_impl; + +template <> +struct assignment_op_str_impl { + static constexpr const char* value = " = "; +}; + +template <> +struct assignment_op_str_impl { + static constexpr const char* value = " += "; +}; + +template <> +struct assignment_op_str_impl { + static constexpr const char* value = " -= "; +}; + +template <> +struct assignment_op_str_impl { + static constexpr const char* value = " /= "; +}; + +template <> +struct assignment_op_str_impl { + static constexpr const char* value = " *= "; +}; + +template +struct assignment_op_str : assignment_op_str_impl {}; + +template +struct assignment_op_str> + : assignment_op_str_impl {}; + +} // namespace internal + +/** + * @tparam T A type that has an `assignment_op` static constexpr member type + * @return The types assignment op as a constexpr const char* + */ +template +inline constexpr const char* assignment_op() noexcept { + return internal::assignment_op_str>::value; +} + +} // namespace math +} // namespace stan +#endif +#endif diff --git a/stan/math/opencl/kernel_generator/load.hpp b/stan/math/opencl/kernel_generator/load.hpp index 319557959b6..6f48252d89e 100644 --- a/stan/math/opencl/kernel_generator/load.hpp +++ b/stan/math/opencl/kernel_generator/load.hpp @@ -4,6 +4,8 @@ #include #include +#include + #include #include #include @@ -23,17 +25,20 @@ namespace math { /** * Represents an access to a \c matrix_cl in kernel generator expressions * @tparam T \c matrix_cl + * @tparam AssignOp tells higher level operations whether the final operation + * should be an assignment or a type of compound assignment. */ -template +template class load_ - : public operation_cl_lhs, + : public operation_cl_lhs, typename std::remove_reference_t::type> { protected: T a_; public: + static constexpr assign_op_cl assignment_op = AssignOp; using Scalar = typename std::remove_reference_t::type; - using base = operation_cl, Scalar>; + using base = operation_cl, Scalar>; using base::var_name_; static_assert(disjunction, is_arena_matrix_cl>::value, "load_: argument a must be a matrix_cl!"); @@ -51,9 +56,13 @@ class load_ * Creates a deep copy of this expression. * @return copy of \c *this */ - inline load_ deep_copy() & { return load_(a_); } - inline load_ deep_copy() const& { return load_(a_); } - inline load_ deep_copy() && { return load_(std::forward(a_)); } + inline load_ deep_copy() & { return load_(a_); } + inline load_ deep_copy() const& { + return load_(a_); + } + inline load_ deep_copy() && { + return load_(std::forward(a_)); + } /** * Generates kernel code for this expression. @@ -327,6 +336,7 @@ class load_ } } }; + /** @}*/ } // namespace math } // namespace stan diff --git a/stan/math/opencl/kernel_generator/multi_result_kernel.hpp b/stan/math/opencl/kernel_generator/multi_result_kernel.hpp index a82d033c5f3..167cd44ed13 100644 --- a/stan/math/opencl/kernel_generator/multi_result_kernel.hpp +++ b/stan/math/opencl/kernel_generator/multi_result_kernel.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -326,25 +327,83 @@ class results_cl { * Incrementing \c results_ object by \c expressions_cl object * executes the kernel that evaluates expressions and increments results by * those expressions. + * @tparam AssignOp an optional `assign_op_cl` that dictates whether the + * object is assigned using standard or compound assign. * @tparam T_expressions types of expressions * @param exprs expressions */ - template > - void operator+=(const expressions_cl& exprs) { + void compound_assignment_impl(const expressions_cl& exprs) { index_apply([this, &exprs](auto... Is) { - auto tmp = std::tuple_cat(make_assignment_pair( + auto tmp = std::tuple_cat(make_assignment_pair( std::get(results_), std::get(exprs.expressions_))...); index_apply::value>( [this, &tmp](auto... Is2) { assignment_impl(std::make_tuple(std::make_pair( - std::get(tmp).first, - std::get(tmp).first + std::get(tmp).second)...)); + std::get(tmp).first, std::get(tmp).second)...)); }); }); } + /** + * Incrementing \c results_ object by \c expressions_cl object + * executes the kernel that evaluates expressions and increments results by + * those expressions. + * @tparam T_expressions types of expressions + * @param exprs expressions + */ + template > + void operator+=(const expressions_cl& exprs) { + compound_assignment_impl(exprs); + } + + /** + * Decrement \c results_ object by \c expressions_cl object + * executes the kernel that evaluates expressions and increments results by + * those expressions. + * @tparam T_expressions types of expressions + * @param exprs expressions + */ + template > + void operator-=(const expressions_cl& exprs) { + compound_assignment_impl(exprs); + } + + /** + * Elementwise divide \c results_ object by \c expressions_cl object + * executes the kernel that evaluates expressions and increments results by + * those expressions. + * @tparam T_expressions types of expressions + * @param exprs expressions + */ + template > + void operator/=(const expressions_cl& exprs) { + compound_assignment_impl(exprs); + } + + /** + * Elementwise multiply \c results_ object by \c expressions_cl object + * executes the kernel that evaluates expressions and increments results by + * those expressions. + * @tparam T_expressions types of expressions + * @param exprs expressions + */ + template > + void operator*=(const expressions_cl& exprs) { + compound_assignment_impl(exprs); + } + /** * Generates kernel source for evaluating given expressions into results held * by \c this. @@ -525,30 +584,44 @@ class results_cl { /** * Makes a std::pair of one result and one expression and wraps it into a * tuple. + * @tparam AssignOp an optional `assign_op_cl` that dictates whether the + * object is assigned using standard or compound assign. + * @tparam T_result An non scalar type that is normally an `result_cl` + * operation holding a `matrix_cl` + * @tparam T_expression An expression of set of operations on `matrix_cl` and + * scalar types. * @param result result * @param expression expression * @return a tuple of pair of result and expression */ - template , conjunction, std::is_arithmetic>>>* = nullptr> static auto make_assignment_pair(T_result&& result, T_expression&& expression) { - return std::make_tuple( - std::pair, as_operation_cl_t>( - as_operation_cl(std::forward(result)), - as_operation_cl(std::forward(expression)))); + return std::make_tuple(std::pair, + as_operation_cl_t>( + as_operation_cl(std::forward(result)), + as_operation_cl(std::forward(expression)))); } /** * If an expression does not need to be calculated this returns an empty tuple + * @tparam AssignOp an optional `assign_op_cl` that dictates whether the + * object is assigned using standard or compound assign. + * @tparam T_result An non scalar type that is normally an `result_cl` + * operation holding a `matrix_cl` + * @tparam T_expression An expression of set of operations on `matrix_cl` and + * scalar types. * @param result result * @param expression expression * @return a tuple of pair of result and expression */ - template >* = nullptr> static auto make_assignment_pair(T_result&& result, T_expression&& expression) { @@ -558,11 +631,16 @@ class results_cl { /** * Checks on scalars are done separately in this overload instead of in * kernel. + * @tparam AssignOp an optional `assign_op_cl` that dictates whether the + * object is assigned using standard or compound assign. + * @tparam T_check A scalar type + * @tparam T_pass An integral type * @param result result - check * @param pass bool scalar * @return an empty tuple */ - template >* = nullptr, require_integral_t* = nullptr> static std::tuple<> make_assignment_pair(T_check&& result, T_pass&& pass) { diff --git a/stan/math/opencl/kernel_generator/operation_cl.hpp b/stan/math/opencl/kernel_generator/operation_cl.hpp index ef2d4977c97..43a50b59654 100644 --- a/stan/math/opencl/kernel_generator/operation_cl.hpp +++ b/stan/math/opencl/kernel_generator/operation_cl.hpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -60,7 +61,7 @@ struct kernel_parts { args + other.args}; } - kernel_parts operator+=(const kernel_parts& other) { + kernel_parts& operator+=(const kernel_parts& other) { includes += other.includes; declarations += other.declarations; initialization += other.initialization; @@ -74,6 +75,24 @@ struct kernel_parts { } }; +inline std::ostream& operator<<(std::ostream& os, kernel_parts& parts) { + os << "args:" << std::endl; + os << parts.args.substr(0, parts.args.size() - 2) << std::endl; + os << "Decl:" << std::endl; + os << parts.declarations << std::endl; + os << "Init:" << std::endl; + os << parts.initialization << std::endl; + os << "body:" << std::endl; + os << parts.body << std::endl; + os << "body_suffix:" << std::endl; + os << parts.body_suffix << std::endl; + os << "reduction_1d:" << std::endl; + os << parts.reduction_1d << std::endl; + os << "reduction_2d:" << std::endl; + os << parts.reduction_2d << std::endl; + return os; +} + /** * Base for all kernel generator operations. * @tparam Derived derived type @@ -201,7 +220,7 @@ class operation_cl : public operation_cl_base { generated, generated_all, ng, row_index_name, col_index_name, false); kernel_parts out_parts = result.get_kernel_parts_lhs( generated, generated_all, ng, row_index_name, col_index_name); - out_parts.body += " = " + derived().var_name_ + ";\n"; + out_parts.body += assignment_op() + derived().var_name_ + ";\n"; parts += out_parts; return parts; } diff --git a/stan/math/opencl/prim/normal_lccdf.hpp b/stan/math/opencl/prim/normal_lccdf.hpp index e9e7f97c079..24a49a70f6f 100644 --- a/stan/math/opencl/prim/normal_lccdf.hpp +++ b/stan/math/opencl/prim/normal_lccdf.hpp @@ -82,13 +82,12 @@ return_type_t normal_lccdf( matrix_cl mu_deriv_cl; matrix_cl sigma_deriv_cl; - results(check_y_not_nan, check_mu_finite, check_sigma_positive, lccdf_cl, - y_deriv_cl, mu_deriv_cl, sigma_deriv_cl) - = expressions(y_not_nan_expr, mu_finite_expr, sigma_positive_expr, - lccdf_expr, calc_if::value>(y_deriv), + results(check_y_not_nan, check_mu_finite, check_sigma_positive) + = expressions(y_not_nan_expr, mu_finite_expr, sigma_positive_expr); + results(lccdf_cl, y_deriv_cl, mu_deriv_cl, sigma_deriv_cl) + = expressions(lccdf_expr, calc_if::value>(y_deriv), calc_if::value>(mu_deriv), calc_if::value>(sigma_deriv)); - T_partials_return lccdf = LOG_HALF + sum(from_matrix_cl(lccdf_cl)); auto ops_partials = make_partials_propagator(y_col, mu_col, sigma_col); diff --git a/stan/math/opencl/rev.hpp b/stan/math/opencl/rev.hpp index 7303a5d34e3..f48de0b806f 100644 --- a/stan/math/opencl/rev.hpp +++ b/stan/math/opencl/rev.hpp @@ -50,6 +50,7 @@ #include #include #include +#include #include #include #include diff --git a/stan/math/opencl/rev/adjoint_results.hpp b/stan/math/opencl/rev/adjoint_results.hpp index c42297b418d..4a2dede57d0 100644 --- a/stan/math/opencl/rev/adjoint_results.hpp +++ b/stan/math/opencl/rev/adjoint_results.hpp @@ -41,22 +41,24 @@ class adjoint_results_cl : protected results_cl { index_apply([&](auto... Is) { auto scalars = std::tuple_cat(select_scalar_assignments( std::get(this->results_), std::get(exprs.expressions_))...); - auto nonscalars_tmp = std::tuple_cat(select_nonscalar_assignments( - std::get(this->results_), std::get(exprs.expressions_))...); + auto nonscalars_tmp = std::tuple_cat( + select_nonscalar_assignments( + std::get(this->results_), + std::get(exprs.expressions_))...); index_apply::value>( [&](auto... Is_nonscal) { - auto nonscalars = std::make_tuple(std::make_pair( - std::get(nonscalars_tmp).first, - std::get(nonscalars_tmp).first - + std::get(nonscalars_tmp).second)...); + auto nonscalars = std::make_tuple( + std::make_pair(std::get(nonscalars_tmp).first, + std::get(nonscalars_tmp).second)...); index_apply::value>( [&](auto... Is_scal) { // evaluate all expressions this->assignment_impl(std::tuple_cat( nonscalars, - this->make_assignment_pair( + this->template make_assignment_pair< + assign_op_cl::plus_equals>( std::get<2>(std::get(scalars)), sum_2d(std::get<1>(std::get(scalars))))...)); @@ -102,6 +104,8 @@ class adjoint_results_cl : protected results_cl { /** * Selects assignments that have non-scalar var results. + * @tparam AssignOp an optional `assign_op_cl` that dictates whether the + * object is assigned using standard or compound assign. * @tparam T_result type of result. This overload is used for non-scalar vars. * @tparam T_expression type of expression * @param result result @@ -109,16 +113,18 @@ class adjoint_results_cl : protected results_cl { * @return pair of result and expression or empty tuple (if the result is * check or the expression is `calc_if`. */ - template * = nullptr, require_st_var* = nullptr> - auto select_nonscalar_assignments(const T_result& result, + auto select_nonscalar_assignments(T_result&& result, T_expression&& expression) { - return results_cl::make_assignment_pair( + return results_cl::template make_assignment_pair( result.adj(), std::forward(expression)); } /** * Selects assignments that have non-scalar var results. + * @tparam AssignOp an optional `assign_op_cl` that dictates whether the + * object is assigned using standard or compound assign. * @tparam T_result type of result. This overload is used for results that are * either scalars or not vars. * @tparam T_expression type of expression @@ -127,7 +133,7 @@ class adjoint_results_cl : protected results_cl { * @return empty tuple */ template < - typename T_result, typename T_expression, + assign_op_cl AssignOp, typename T_result, typename T_expression, std::enable_if_t::value || !is_var>::value>* = nullptr> auto select_nonscalar_assignments(T_result&& result, diff --git a/stan/math/opencl/rev/grad.hpp b/stan/math/opencl/rev/grad.hpp new file mode 100644 index 00000000000..f4a1dde1dc9 --- /dev/null +++ b/stan/math/opencl/rev/grad.hpp @@ -0,0 +1,30 @@ +#ifndef STAN_MATH_OPENCL_REV_FUN_GRAD_HPP +#define STAN_MATH_OPENCL_REV_FUN_GRAD_HPP +#ifdef STAN_OPENCL +#include + +namespace stan { +namespace math { + +/** + * Propagate chain rule to calculate gradients starting from + * the specified variable. Resizes the input vector to be the + * correct size. + * + * The grad() function does not itself recover any memory. use + * recover_memory() or + * recover_memory_nested() to recover memory. + * + * @param[in] v Value of function being differentiated + * @param[in] x Variables being differentiated with respect to + * @param[out] g Gradient, d/dx v, evaluated at x. + */ +inline void grad(var& v, var_value>& x, Eigen::VectorXd& g) { + grad(v.vi_); + g = from_matrix_cl(x.adj()); +} + +} // namespace math +} // namespace stan +#endif +#endif diff --git a/test/unit/math/opencl/kernel_generator/assignment_ops_test.cpp b/test/unit/math/opencl/kernel_generator/assignment_ops_test.cpp new file mode 100644 index 00000000000..3675dbd0a0f --- /dev/null +++ b/test/unit/math/opencl/kernel_generator/assignment_ops_test.cpp @@ -0,0 +1,84 @@ +#ifdef STAN_OPENCL +#include +#include +#include +#include +#include +#include +#include +#include + +TEST(KernelGenerator, plus_equals) { + using stan::math::from_matrix_cl; + using stan::math::matrix_cl; + using stan::math::to_matrix_cl; + using stan::math::var; + using stan::math::var_value; + Eigen::MatrixXd A = Eigen::MatrixXd::Random(10, 10); + Eigen::MatrixXd B = Eigen::MatrixXd::Random(10, 10); + Eigen::MatrixXd C = Eigen::MatrixXd::Random(10, 10); + matrix_cl A_cl = to_matrix_cl(A); + matrix_cl B_cl = to_matrix_cl(B); + matrix_cl C_cl = to_matrix_cl(C); + C += A + B; + results(C_cl) += expressions(A_cl + B_cl); + Eigen::MatrixXd C_cl_host = from_matrix_cl(C_cl); + EXPECT_MATRIX_EQ(C_cl_host, C) +} + +TEST(KernelGenerator, minus_equals) { + using stan::math::from_matrix_cl; + using stan::math::matrix_cl; + using stan::math::to_matrix_cl; + using stan::math::var; + using stan::math::var_value; + Eigen::MatrixXd A = Eigen::MatrixXd::Random(10, 10); + Eigen::MatrixXd B = Eigen::MatrixXd::Random(10, 10); + Eigen::MatrixXd C = Eigen::MatrixXd::Random(10, 10); + matrix_cl A_cl = to_matrix_cl(A); + matrix_cl B_cl = to_matrix_cl(B); + matrix_cl C_cl = to_matrix_cl(C); + C -= A + B; + results(C_cl) -= expressions(A_cl + B_cl); + Eigen::MatrixXd C_cl_host = from_matrix_cl(C_cl); + EXPECT_MATRIX_EQ(C_cl_host, C) +} + +TEST(KernelGenerator, divide_equals) { + using stan::math::from_matrix_cl; + using stan::math::matrix_cl; + using stan::math::to_matrix_cl; + using stan::math::var; + using stan::math::var_value; + Eigen::MatrixXd A = Eigen::MatrixXd::Random(10, 10); + Eigen::MatrixXd B = Eigen::MatrixXd::Random(10, 10); + Eigen::MatrixXd C = Eigen::MatrixXd::Random(10, 10); + matrix_cl A_cl = to_matrix_cl(A); + matrix_cl B_cl = to_matrix_cl(B); + matrix_cl C_cl = to_matrix_cl(C); + C.array() /= A.array() + B.array(); + results(C_cl) /= expressions(A_cl + B_cl); + Eigen::MatrixXd C_cl_host = from_matrix_cl(C_cl); + EXPECT_MATRIX_EQ(C_cl_host, C) +} + +TEST(KernelGenerator, times_equals) { + using stan::math::from_matrix_cl; + using stan::math::matrix_cl; + using stan::math::to_matrix_cl; + using stan::math::var; + using stan::math::var_value; + + Eigen::MatrixXd A = Eigen::MatrixXd::Random(10, 10); + Eigen::MatrixXd B = Eigen::MatrixXd::Random(10, 10); + Eigen::MatrixXd C = Eigen::MatrixXd::Random(10, 10); + matrix_cl A_cl = to_matrix_cl(A); + matrix_cl B_cl = to_matrix_cl(B); + matrix_cl C_cl = to_matrix_cl(C); + C.array() *= A.array() + B.array(); + results(C_cl) *= expressions(A_cl + B_cl); + Eigen::MatrixXd C_cl_host = from_matrix_cl(C_cl); + EXPECT_MATRIX_EQ(C_cl_host, C) +} + +#endif diff --git a/test/unit/math/opencl/rev/add_test.cpp b/test/unit/math/opencl/rev/add_test.cpp index 15c4d501b2f..f678fbc255c 100644 --- a/test/unit/math/opencl/rev/add_test.cpp +++ b/test/unit/math/opencl/rev/add_test.cpp @@ -76,4 +76,29 @@ TEST(OpenCLPrim, add_rev_exceptions) { EXPECT_THROW(stan::math::add(md11, md22), std::invalid_argument); } +TEST(OpenCLPrim, add_aliasing) { + stan::math::matrix_d d1(3, 3); + d1 << 1, 2, 3, 4, 5, 6, 7, 8, 9; + using stan::math::matrix_cl; + using stan::math::var; + using stan::math::var_value; + using varmat_cl = var_value>; + varmat_cl d11 = stan::math::to_matrix_cl(d1); + // Add the same matrix as the left and right hand side + var res = stan::math::sum(stan::math::add(d11, d11)); + res.grad(); + // Get back adjoints + Eigen::MatrixXd grad_res = stan::math::from_matrix_cl(d11.adj()); + stan::math::recover_memory(); + Eigen::Matrix d_host = d1; + // Same op as above but on the host + var res_host = stan::math::sum(stan::math::add(d_host, d_host)); + res_host.grad(); + Eigen::MatrixXd grad_res_host = d_host.adj(); + std::cout << "OpenCL Adjoints: " << std::endl; + std::cout << grad_res << std::endl; + std::cout << "CPU Adjoints: " << std::endl; + std::cout << grad_res_host << std::endl; +} + #endif diff --git a/test/unit/math/opencl/rev/grad_test.cpp b/test/unit/math/opencl/rev/grad_test.cpp new file mode 100644 index 00000000000..23d9ee330e7 --- /dev/null +++ b/test/unit/math/opencl/rev/grad_test.cpp @@ -0,0 +1,36 @@ +#ifdef STAN_OPENCL +#include +#include +#include +#include +#include + +TEST(OpenCLGradTest, exceptions) { + using stan::math::matrix_cl; + using stan::math::to_matrix_cl; + using stan::math::var; + using stan::math::var_value; + using varmat_cl = var_value>; + Eigen::VectorXd a(6); + a << 1, 2, 3, 4, 5, 6; + varmat_cl a_cl(to_matrix_cl(a)); + varmat_cl b_cl(a_cl.block(0, 0, 3, 1)); + varmat_cl c_cl(a_cl.block(3, 0, 3, 1)); + Eigen::VectorXd ret_grads_cl(6); + using stan::math::add; + using stan::math::subtract; + var ret = stan::math::sum(add(b_cl, b_cl)); + stan::math::grad(ret, a_cl, ret_grads_cl); + std::cout << "opencl grads: \n" << ret_grads_cl << std::endl; + stan::math::recover_memory(); + Eigen::Matrix a_host = a; + Eigen::Matrix b_host = a_host.segment(0, 3); + Eigen::Matrix c_host = a_host.segment(3, 3); + var ret_host = stan::math::sum(add(b_host, b_host)); + Eigen::VectorXd ret_grads_host(6); + stan::math::grad(ret_host, a_host, ret_grads_host); + std::cout << "host grads: \n" << ret_grads_host << std::endl; + stan::math::recover_memory(); +} + +#endif