From 911084588a5411c9d78777c24e00a1ef25486c12 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Thu, 30 Aug 2018 11:52:43 +0300 Subject: [PATCH 01/21] building scaffolding for std::complex --- stan/math/rev/core/std_complex.hpp | 23 ++++ test/unit/math/rev/core/std_complex_test.cpp | 106 +++++++++++++++++++ 2 files changed, 129 insertions(+) create mode 100644 stan/math/rev/core/std_complex.hpp create mode 100644 test/unit/math/rev/core/std_complex_test.cpp diff --git a/stan/math/rev/core/std_complex.hpp b/stan/math/rev/core/std_complex.hpp new file mode 100644 index 00000000000..1d4af2333d3 --- /dev/null +++ b/stan/math/rev/core/std_complex.hpp @@ -0,0 +1,23 @@ +#ifndef STAN_MATH_REV_CORE_STD_COMPLEX_HPP +#define STAN_MATH_REV_CORE_STD_COMPLEX_HPP + +#include +#include +#include + +namespace std { + +/** + * Specialization of complex for var objects. + * + * This implementation of std::numeric_limits + * is used to treat var objects like doubles. + */ +/* +template <> +struct complex { + +};*/ + +} // namespace std +#endif diff --git a/test/unit/math/rev/core/std_complex_test.cpp b/test/unit/math/rev/core/std_complex_test.cpp new file mode 100644 index 00000000000..aac8d24b950 --- /dev/null +++ b/test/unit/math/rev/core/std_complex_test.cpp @@ -0,0 +1,106 @@ +#include +#include +#include + +// For a definition of the spec: +// https://en.cppreference.com/w/cpp/numeric/complex +class MathRev : public testing::Test { + public: + void SetUp() { stan::math::recover_memory(); } +}; + +TEST_F(MathRev, complex_constructor) { + std::complex x; + EXPECT_EQ(0, stan::math::ChainableStack::instance().var_stack_.size()); + stan::math::recover_memory(); + + std::complex y{0}; + EXPECT_EQ(1, stan::math::ChainableStack::instance().var_stack_.size()); + stan::math::recover_memory(); + + std::complex z{0, 0}; + EXPECT_EQ(2, stan::math::ChainableStack::instance().var_stack_.size()); + stan::math::recover_memory(); +} + +TEST_F(MathRev, assignment) { // operator= + ADD_FAILURE() << "not yet implemented"; +} + +TEST_F(MathRev, real_component) { ADD_FAILURE() << "not yet implemented"; } + +TEST_F(MathRev, imag_component) { ADD_FAILURE() << "not yet implemented"; } + +TEST_F(MathRev, member_operators) { + // operator+= + // operator-= + // operator/= + // operator*= + ADD_FAILURE() << "not yet implemented"; +} + +TEST_F(MathRev, unary_operators) { + // operator+ + // operator- + ADD_FAILURE() << "not yet implemented"; +} + +TEST_F(MathRev, arithmetic) { + // operator+ + // operator- + // operator* + // operator/ + ADD_FAILURE() << "not yet implemented"; +} + +TEST_F(MathRev, comparison) { + // complex and scalar; assume var and double as scalar? + // operator== + // operator!= +} + +TEST_F(MathRev, serialize_deserialize) { + // operator<< + // operator>> + ADD_FAILURE() << "not yet implemented"; +} + +TEST_F(MathRev, real) { + // real + ADD_FAILURE() << "not yet implemented"; +} + +TEST_F(MathRev, imag) { + // imag + ADD_FAILURE() << "not yet implemented"; +} + +TEST_F(MathRev, abs) { + // abs + ADD_FAILURE() << "not yet implemented"; +} + +TEST_F(MathRev, arg) { + // arg + ADD_FAILURE() << "not yet implemented"; +} + +TEST_F(MathRev, norm) { + // norm + ADD_FAILURE() << "not yet implemented"; +} + +TEST_F(MathRev, conj) { + // conj + ADD_FAILURE() << "not yet implemented"; +} + +TEST_F(MathRev, proj) { + // projg + ADD_FAILURE() << "not yet implemented"; +} + +TEST_F(MathRev, polar) { + // polar + ADD_FAILURE() << "not yet implemented"; +} From ae730bf7a1b1964215c0c274edfc6a071f3b951a Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Thu, 30 Aug 2018 15:07:27 +0300 Subject: [PATCH 02/21] adding constexpr to constructor --- stan/math/rev/core/var.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stan/math/rev/core/var.hpp b/stan/math/rev/core/var.hpp index 124f4562410..ff0339b7b86 100644 --- a/stan/math/rev/core/var.hpp +++ b/stan/math/rev/core/var.hpp @@ -62,7 +62,7 @@ class var { * dangling. Before an assignment, the behavior is thus undefined just * as for a basic double. */ - var() : vi_(static_cast(0U)) {} + constexpr var() : vi_(static_cast(0U)) {} /** * Construct a variable from a pointer to a variable implementation. From 7f1ecb2c2ea394c368e5a99c3624c7f29a93d591 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Thu, 30 Aug 2018 15:07:59 +0300 Subject: [PATCH 03/21] template specialization for std::complex and start of tests --- stan/math/rev/core/std_complex.hpp | 15 +++-- test/unit/math/rev/core/std_complex_test.cpp | 60 ++++++++++++++++++-- 2 files changed, 66 insertions(+), 9 deletions(-) diff --git a/stan/math/rev/core/std_complex.hpp b/stan/math/rev/core/std_complex.hpp index 1d4af2333d3..d476f9b78a2 100644 --- a/stan/math/rev/core/std_complex.hpp +++ b/stan/math/rev/core/std_complex.hpp @@ -10,14 +10,19 @@ namespace std { /** * Specialization of complex for var objects. * - * This implementation of std::numeric_limits - * is used to treat var objects like doubles. */ -/* template <> -struct complex { +constexpr complex::complex(const stan::math::var& re, + const stan::math::var& im) { + real(stan::math::is_uninitialized(re) ? stan::math::var{0.0} : re); + imag(stan::math::is_uninitialized(im) ? stan::math::var{0.0} : im); +} -};*/ +// struct complex { +// complex() +// : _M_real(0), _M_imag(0) { +// } +// }; } // namespace std #endif diff --git a/test/unit/math/rev/core/std_complex_test.cpp b/test/unit/math/rev/core/std_complex_test.cpp index aac8d24b950..c47c1bf199a 100644 --- a/test/unit/math/rev/core/std_complex_test.cpp +++ b/test/unit/math/rev/core/std_complex_test.cpp @@ -11,11 +11,13 @@ class MathRev : public testing::Test { TEST_F(MathRev, complex_constructor) { std::complex x; - EXPECT_EQ(0, stan::math::ChainableStack::instance().var_stack_.size()); + EXPECT_EQ(2, stan::math::ChainableStack::instance().var_stack_.size()); + EXPECT_FLOAT_EQ(0, x.real().val()); + EXPECT_FLOAT_EQ(0, x.imag().val()); stan::math::recover_memory(); std::complex y{0}; - EXPECT_EQ(1, stan::math::ChainableStack::instance().var_stack_.size()); + EXPECT_EQ(2, stan::math::ChainableStack::instance().var_stack_.size()); stan::math::recover_memory(); std::complex z{0, 0}; @@ -23,10 +25,59 @@ TEST_F(MathRev, complex_constructor) { stan::math::recover_memory(); } -TEST_F(MathRev, assignment) { // operator= - ADD_FAILURE() << "not yet implemented"; +/* +TEST_F(MathRev, assignment_double) { + double rhs = 1; + // stan::math::var rhs_v = 2; + // std::complex rhs_c1{3}; + // std::complex rhs_c2{4, 5}; + + std::complex lhs; + lhs = rhs; + EXPECT_FLOAT_EQ(1, lhs.real().val()); + EXPECT_FLOAT_EQ(0, lhs.imag().val()); + EXPECT_EQ(4, stan::math::ChainableStack::instance().var_stack_.size()); +} +*/ + +/* +TEST_F(MathRev, assignment_var) { + stan::math::var rhs = 2; + // std::complex rhs_c1{3}; + // std::complex rhs_c2{4, 5}; + + std::complex lhs; + lhs = rhs; + EXPECT_FLOAT_EQ(2, lhs.real().val()); + EXPECT_FLOAT_EQ(0, lhs.imag().val()); + EXPECT_EQ(3, stan::math::ChainableStack::instance().var_stack_.size()); +} +*/ + +TEST_F(MathRev, assignment_complex_real_only) { + std::complex rhs{3}; + + std::complex lhs; + lhs = rhs; + EXPECT_FLOAT_EQ(3, lhs.real().val()); + EXPECT_FLOAT_EQ(0, lhs.imag().val()); + EXPECT_EQ(4, stan::math::ChainableStack::instance().var_stack_.size()); + EXPECT_EQ(rhs.real().vi_, lhs.real().vi_); +} + +TEST_F(MathRev, assignment_complex) { + std::complex rhs{4, 5}; + + std::complex lhs; + lhs = rhs; + EXPECT_FLOAT_EQ(4, lhs.real().val()); + EXPECT_FLOAT_EQ(5, lhs.imag().val()); + EXPECT_EQ(4, stan::math::ChainableStack::instance().var_stack_.size()); + EXPECT_EQ(rhs.real().vi_, lhs.real().vi_); + EXPECT_EQ(rhs.imag().vi_, lhs.imag().vi_); } +/* TEST_F(MathRev, real_component) { ADD_FAILURE() << "not yet implemented"; } TEST_F(MathRev, imag_component) { ADD_FAILURE() << "not yet implemented"; } @@ -104,3 +155,4 @@ TEST_F(MathRev, polar) { // polar ADD_FAILURE() << "not yet implemented"; } +*/ From d38962556e15e121213d6f00fca8f8a9b57a0607 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Thu, 30 Aug 2018 17:12:50 +0300 Subject: [PATCH 04/21] specializing operator= for std::complex --- stan/math/rev/core/std_complex.hpp | 35 ++++++++++++++++---- test/unit/math/rev/core/std_complex_test.cpp | 16 +++------ 2 files changed, 34 insertions(+), 17 deletions(-) diff --git a/stan/math/rev/core/std_complex.hpp b/stan/math/rev/core/std_complex.hpp index d476f9b78a2..7d32737514c 100644 --- a/stan/math/rev/core/std_complex.hpp +++ b/stan/math/rev/core/std_complex.hpp @@ -8,8 +8,19 @@ namespace std { /** - * Specialization of complex for var objects. + * Specialization of the std::complex + * constructor. * + * The base template implementation will create uninitialized + * stan::math::vars for the default arguments by calling the empty + * constructor. This needs to be specialized because uninitialized + * vars can cause seg faults. + * + * Promotion from primitives to stan::math::var applies and this + * constructor will handle the primitivies. + * + * @param re real component + * @param im imaginary component */ template <> constexpr complex::complex(const stan::math::var& re, @@ -18,11 +29,23 @@ constexpr complex::complex(const stan::math::var& re, imag(stan::math::is_uninitialized(im) ? stan::math::var{0.0} : im); } -// struct complex { -// complex() -// : _M_real(0), _M_imag(0) { -// } -// }; +/** + * Specialization of operator= for stan::math::var. + * + * The base template implementation will leave the imaginary component + * as an uninitialized stan::math::var, which can cause seg faults. + * This implementation fixes that problem by forcing the imaginary + * component to be initialized to 0.0. + * + * + */ +template <> +complex& complex::operator=( + const stan::math::var& c) { + real(c); + imag(0.0); + return *this; +} } // namespace std #endif diff --git a/test/unit/math/rev/core/std_complex_test.cpp b/test/unit/math/rev/core/std_complex_test.cpp index c47c1bf199a..f652752a0b9 100644 --- a/test/unit/math/rev/core/std_complex_test.cpp +++ b/test/unit/math/rev/core/std_complex_test.cpp @@ -9,6 +9,7 @@ class MathRev : public testing::Test { void SetUp() { stan::math::recover_memory(); } }; +/* TEST_F(MathRev, complex_constructor) { std::complex x; EXPECT_EQ(2, stan::math::ChainableStack::instance().var_stack_.size()); @@ -24,13 +25,9 @@ TEST_F(MathRev, complex_constructor) { EXPECT_EQ(2, stan::math::ChainableStack::instance().var_stack_.size()); stan::math::recover_memory(); } - -/* +*/ TEST_F(MathRev, assignment_double) { double rhs = 1; - // stan::math::var rhs_v = 2; - // std::complex rhs_c1{3}; - // std::complex rhs_c2{4, 5}; std::complex lhs; lhs = rhs; @@ -38,21 +35,17 @@ TEST_F(MathRev, assignment_double) { EXPECT_FLOAT_EQ(0, lhs.imag().val()); EXPECT_EQ(4, stan::math::ChainableStack::instance().var_stack_.size()); } -*/ -/* TEST_F(MathRev, assignment_var) { stan::math::var rhs = 2; - // std::complex rhs_c1{3}; - // std::complex rhs_c2{4, 5}; std::complex lhs; lhs = rhs; EXPECT_FLOAT_EQ(2, lhs.real().val()); EXPECT_FLOAT_EQ(0, lhs.imag().val()); - EXPECT_EQ(3, stan::math::ChainableStack::instance().var_stack_.size()); + EXPECT_EQ(4, stan::math::ChainableStack::instance().var_stack_.size()); + EXPECT_EQ(rhs.vi_, lhs.real().vi_); } -*/ TEST_F(MathRev, assignment_complex_real_only) { std::complex rhs{3}; @@ -63,6 +56,7 @@ TEST_F(MathRev, assignment_complex_real_only) { EXPECT_FLOAT_EQ(0, lhs.imag().val()); EXPECT_EQ(4, stan::math::ChainableStack::instance().var_stack_.size()); EXPECT_EQ(rhs.real().vi_, lhs.real().vi_); + EXPECT_EQ(rhs.imag().vi_, lhs.imag().vi_); } TEST_F(MathRev, assignment_complex) { From d2332fd7b16eaa2e849792dc08867aa8d91761bc Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Fri, 31 Aug 2018 02:14:19 +0300 Subject: [PATCH 05/21] adding operator* and operator/ for std::complex --- stan/math/rev/core/std_complex.hpp | 13 ++ test/unit/math/rev/core/std_complex_test.cpp | 131 ++++++++++++++++++- 2 files changed, 138 insertions(+), 6 deletions(-) diff --git a/stan/math/rev/core/std_complex.hpp b/stan/math/rev/core/std_complex.hpp index 7d32737514c..c8f2fb921e0 100644 --- a/stan/math/rev/core/std_complex.hpp +++ b/stan/math/rev/core/std_complex.hpp @@ -3,6 +3,7 @@ #include #include +#include #include namespace std { @@ -47,5 +48,17 @@ complex& complex::operator=( return *this; } +template <> +complex operator/(const complex& z, + const complex& w) { + return complex{z.real() / w.real(), z.imag() / w.imag()}; +} + +template <> +complex operator*(const complex& z, + const complex& w) { + return complex{z.real() * w.real(), z.imag() * w.imag()}; +} + } // namespace std #endif diff --git a/test/unit/math/rev/core/std_complex_test.cpp b/test/unit/math/rev/core/std_complex_test.cpp index f652752a0b9..d910783e0ef 100644 --- a/test/unit/math/rev/core/std_complex_test.cpp +++ b/test/unit/math/rev/core/std_complex_test.cpp @@ -71,33 +71,152 @@ TEST_F(MathRev, assignment_complex) { EXPECT_EQ(rhs.imag().vi_, lhs.imag().vi_); } -/* -TEST_F(MathRev, real_component) { ADD_FAILURE() << "not yet implemented"; } +TEST_F(MathRev, real_component) { + std::complex c{1, 2}; + + EXPECT_EQ(1, c.real().val()); +} -TEST_F(MathRev, imag_component) { ADD_FAILURE() << "not yet implemented"; } +TEST_F(MathRev, imag_component) { + std::complex c{1, 2}; + + EXPECT_EQ(2, c.imag().val()); +} TEST_F(MathRev, member_operators) { + std::complex x{1, 2}, y{3, 4}; + // operator+= + EXPECT_EQ(4, stan::math::ChainableStack::instance().var_stack_.size()); + x += y; + EXPECT_FLOAT_EQ(4, x.real().val()); + EXPECT_FLOAT_EQ(6, x.imag().val()); + EXPECT_FLOAT_EQ(3, y.real().val()); + EXPECT_FLOAT_EQ(4, y.imag().val()); + EXPECT_EQ(6, stan::math::ChainableStack::instance().var_stack_.size()); + stan::math::recover_memory(); + // operator-= + x = std::complex{1, 2}; + y = std::complex{3, 4}; + EXPECT_EQ(4, stan::math::ChainableStack::instance().var_stack_.size()); + x -= y; + EXPECT_FLOAT_EQ(-2, x.real().val()); + EXPECT_FLOAT_EQ(-2, x.imag().val()); + EXPECT_FLOAT_EQ(3, y.real().val()); + EXPECT_FLOAT_EQ(4, y.imag().val()); + EXPECT_EQ(6, stan::math::ChainableStack::instance().var_stack_.size()); + stan::math::recover_memory(); + // operator/= + x = std::complex{1, 2}; + y = std::complex{3, 4}; + EXPECT_EQ(4, stan::math::ChainableStack::instance().var_stack_.size()); + x /= y; + EXPECT_FLOAT_EQ(1.0 / 3.0, x.real().val()); + EXPECT_FLOAT_EQ(0.5, x.imag().val()); + EXPECT_FLOAT_EQ(3, y.real().val()); + EXPECT_FLOAT_EQ(4, y.imag().val()); + EXPECT_EQ(6, stan::math::ChainableStack::instance().var_stack_.size()); + stan::math::recover_memory(); + // operator*= - ADD_FAILURE() << "not yet implemented"; + x = std::complex{1, 2}; + y = std::complex{3, 4}; + EXPECT_EQ(4, stan::math::ChainableStack::instance().var_stack_.size()); + x *= y; + EXPECT_FLOAT_EQ(3, x.real().val()); + EXPECT_FLOAT_EQ(8, x.imag().val()); + EXPECT_FLOAT_EQ(3, y.real().val()); + EXPECT_FLOAT_EQ(4, y.imag().val()); + EXPECT_EQ(6, stan::math::ChainableStack::instance().var_stack_.size()); + stan::math::recover_memory(); } TEST_F(MathRev, unary_operators) { + std::complex x{1, 2}; + EXPECT_EQ(2, stan::math::ChainableStack::instance().var_stack_.size()); + // operator+ + std::complex z = +x; + EXPECT_EQ(2, stan::math::ChainableStack::instance().var_stack_.size()); + EXPECT_FLOAT_EQ(1, z.real().val()); + EXPECT_FLOAT_EQ(2, z.imag().val()); + EXPECT_FLOAT_EQ(1, x.real().val()); + EXPECT_FLOAT_EQ(2, x.imag().val()); + stan::math::recover_memory(); + // operator- - ADD_FAILURE() << "not yet implemented"; + x = std::complex{1, 2}; + z = -x; + + EXPECT_EQ(4, stan::math::ChainableStack::instance().var_stack_.size()); + EXPECT_FLOAT_EQ(-1, z.real().val()); + EXPECT_FLOAT_EQ(-2, z.imag().val()); + EXPECT_FLOAT_EQ(1, x.real().val()); + EXPECT_FLOAT_EQ(2, x.imag().val()); + stan::math::recover_memory(); } TEST_F(MathRev, arithmetic) { + std::complex x{1, 2}, y{3, 4}; + EXPECT_EQ(4, stan::math::ChainableStack::instance().var_stack_.size()); + // operator+ + std::complex z = x - y; + EXPECT_EQ(6, stan::math::ChainableStack::instance().var_stack_.size()); + EXPECT_FLOAT_EQ(-2, z.real().val()); + EXPECT_FLOAT_EQ(-2, z.imag().val()); + EXPECT_FLOAT_EQ(1, x.real().val()); + EXPECT_FLOAT_EQ(2, x.imag().val()); + EXPECT_FLOAT_EQ(3, y.real().val()); + EXPECT_FLOAT_EQ(4, y.imag().val()); + stan::math::recover_memory(); + // operator- + x = std::complex{1, 2}; + y = std::complex{3, 4}; + z = x + y; + + EXPECT_EQ(6, stan::math::ChainableStack::instance().var_stack_.size()); + EXPECT_FLOAT_EQ(4, z.real().val()); + EXPECT_FLOAT_EQ(6, z.imag().val()); + EXPECT_FLOAT_EQ(1, x.real().val()); + EXPECT_FLOAT_EQ(2, x.imag().val()); + EXPECT_FLOAT_EQ(3, y.real().val()); + EXPECT_FLOAT_EQ(4, y.imag().val()); + stan::math::recover_memory(); + // operator* + x = std::complex{1, 2}; + y = std::complex{3, 4}; + z = x * y; + + EXPECT_EQ(6, stan::math::ChainableStack::instance().var_stack_.size()); + EXPECT_FLOAT_EQ(3, z.real().val()); + EXPECT_FLOAT_EQ(8, z.imag().val()); + EXPECT_FLOAT_EQ(1, x.real().val()); + EXPECT_FLOAT_EQ(2, x.imag().val()); + EXPECT_FLOAT_EQ(3, y.real().val()); + EXPECT_FLOAT_EQ(4, y.imag().val()); + stan::math::recover_memory(); + // operator/ - ADD_FAILURE() << "not yet implemented"; + x = std::complex{1, 2}; + y = std::complex{3, 4}; + z = x / y; + + EXPECT_EQ(6, stan::math::ChainableStack::instance().var_stack_.size()); + EXPECT_FLOAT_EQ(1.0 / 3.0, z.real().val()); + EXPECT_FLOAT_EQ(0.5, z.imag().val()); + EXPECT_FLOAT_EQ(1, x.real().val()); + EXPECT_FLOAT_EQ(2, x.imag().val()); + EXPECT_FLOAT_EQ(3, y.real().val()); + EXPECT_FLOAT_EQ(4, y.imag().val()); + stan::math::recover_memory(); } +/* TEST_F(MathRev, comparison) { // complex and scalar; assume var and double as scalar? // operator== From 4a28ee24c08f0f8c8caf4e644e3e1de3848fa66f Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Fri, 31 Aug 2018 02:38:36 +0300 Subject: [PATCH 06/21] adding operator== and operator!= for std::complex --- stan/math/rev/core/std_complex.hpp | 35 ++++++++ test/unit/math/rev/core/std_complex_test.cpp | 93 +++++++++++++++++++- 2 files changed, 126 insertions(+), 2 deletions(-) diff --git a/stan/math/rev/core/std_complex.hpp b/stan/math/rev/core/std_complex.hpp index c8f2fb921e0..23770325ba1 100644 --- a/stan/math/rev/core/std_complex.hpp +++ b/stan/math/rev/core/std_complex.hpp @@ -60,5 +60,40 @@ complex operator*(const complex& z, return complex{z.real() * w.real(), z.imag() * w.imag()}; } +template <> +inline bool operator==(const complex& x, + const complex& y) { + return x.real() == y.real() && x.imag() == y.imag(); + ; +} + +template <> +inline bool operator==(const complex& x, + const stan::math::var& y) { + return x.real() == y && x.imag() == 0; +} + +template <> +inline bool operator==(const stan::math::var& x, + const complex& y) { + return y == x; +} + +inline bool operator==(const complex& x, double y) { + return x.real() == y && x.imag() == 0; +} + +inline bool operator==(double x, const complex& y) { + return y == x; +} + +inline bool operator!=(const complex& x, double y) { + return !(x == y); +} + +inline bool operator!=(double x, const complex& y) { + return !(x == y); +} + } // namespace std #endif diff --git a/test/unit/math/rev/core/std_complex_test.cpp b/test/unit/math/rev/core/std_complex_test.cpp index d910783e0ef..b32eb842130 100644 --- a/test/unit/math/rev/core/std_complex_test.cpp +++ b/test/unit/math/rev/core/std_complex_test.cpp @@ -216,13 +216,102 @@ TEST_F(MathRev, arithmetic) { stan::math::recover_memory(); } -/* TEST_F(MathRev, comparison) { // complex and scalar; assume var and double as scalar? // operator== + std::complex x1{1, 2}, y1{3, 4}; + std::complex x2{1, 0}, y2{3, 0}; + double x_d = 1, y_d = 3; + stan::math::var x_v = 1, y_v = 3; + + const int stack_size + = stan::math::ChainableStack::instance().var_stack_.size(); + EXPECT_TRUE(x1 == x1); + EXPECT_EQ(stack_size, + stan::math::ChainableStack::instance().var_stack_.size()); + EXPECT_FALSE(x1 == x2); + EXPECT_EQ(stack_size, + stan::math::ChainableStack::instance().var_stack_.size()); + EXPECT_FALSE(x1 == y1); + EXPECT_EQ(stack_size, + stan::math::ChainableStack::instance().var_stack_.size()); + + EXPECT_FALSE(x1 == x_d); + EXPECT_EQ(stack_size, + stan::math::ChainableStack::instance().var_stack_.size()); + EXPECT_FALSE(x_d == x1); + EXPECT_EQ(stack_size, + stan::math::ChainableStack::instance().var_stack_.size()); + EXPECT_TRUE(x2 == x_d); + EXPECT_EQ(stack_size, + stan::math::ChainableStack::instance().var_stack_.size()); + EXPECT_TRUE(x_d == x2); + EXPECT_EQ(stack_size, + stan::math::ChainableStack::instance().var_stack_.size()); + EXPECT_FALSE(x1 == y_d); + EXPECT_EQ(stack_size, + stan::math::ChainableStack::instance().var_stack_.size()); + + EXPECT_FALSE(x1 == x_v); + EXPECT_EQ(stack_size, + stan::math::ChainableStack::instance().var_stack_.size()); + EXPECT_FALSE(x_v == x1); + EXPECT_EQ(stack_size, + stan::math::ChainableStack::instance().var_stack_.size()); + EXPECT_TRUE(x2 == x_v); + EXPECT_EQ(stack_size, + stan::math::ChainableStack::instance().var_stack_.size()); + EXPECT_TRUE(x_v == x2); + EXPECT_EQ(stack_size, + stan::math::ChainableStack::instance().var_stack_.size()); + EXPECT_FALSE(x1 == y_v); + EXPECT_EQ(stack_size, + stan::math::ChainableStack::instance().var_stack_.size()); + // operator!= + EXPECT_FALSE(x1 != x1); + EXPECT_EQ(stack_size, + stan::math::ChainableStack::instance().var_stack_.size()); + EXPECT_TRUE(x1 != x2); + EXPECT_EQ(stack_size, + stan::math::ChainableStack::instance().var_stack_.size()); + EXPECT_TRUE(x1 != y1); + EXPECT_EQ(stack_size, + stan::math::ChainableStack::instance().var_stack_.size()); + + EXPECT_TRUE(x1 != x_d); + EXPECT_EQ(stack_size, + stan::math::ChainableStack::instance().var_stack_.size()); + EXPECT_TRUE(x_d != x1); + EXPECT_EQ(stack_size, + stan::math::ChainableStack::instance().var_stack_.size()); + EXPECT_FALSE(x2 != x_d); + EXPECT_EQ(stack_size, + stan::math::ChainableStack::instance().var_stack_.size()); + EXPECT_FALSE(x_d != x2); + EXPECT_EQ(stack_size, + stan::math::ChainableStack::instance().var_stack_.size()); + EXPECT_TRUE(x1 != y_d); + EXPECT_EQ(stack_size, + stan::math::ChainableStack::instance().var_stack_.size()); + + EXPECT_TRUE(x1 != x_v); + EXPECT_EQ(stack_size, + stan::math::ChainableStack::instance().var_stack_.size()); + EXPECT_TRUE(x_v != x1); + EXPECT_EQ(stack_size, + stan::math::ChainableStack::instance().var_stack_.size()); + EXPECT_FALSE(x2 != x_v); + EXPECT_EQ(stack_size, + stan::math::ChainableStack::instance().var_stack_.size()); + EXPECT_FALSE(x_v != x2); + EXPECT_EQ(stack_size, + stan::math::ChainableStack::instance().var_stack_.size()); + EXPECT_TRUE(x1 != y_v); + EXPECT_EQ(stack_size, + stan::math::ChainableStack::instance().var_stack_.size()); } - +/* TEST_F(MathRev, serialize_deserialize) { // operator<< // operator>> From dc61fb96855b5c64feafd5199ca4551f391f9c04 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Fri, 31 Aug 2018 03:33:57 +0300 Subject: [PATCH 07/21] adding operator<< and operator>> for std::complex --- stan/math/rev/core/var.hpp | 18 ++++++- test/unit/math/rev/core/std_complex_test.cpp | 55 +++++++++++++++++--- 2 files changed, 65 insertions(+), 8 deletions(-) diff --git a/stan/math/rev/core/var.hpp b/stan/math/rev/core/var.hpp index ff0339b7b86..b5826431290 100644 --- a/stan/math/rev/core/var.hpp +++ b/stan/math/rev/core/var.hpp @@ -462,8 +462,7 @@ class var { inline var& operator/=(double b); /** - * Write the value of this auto-dif variable and its adjoint to - * the specified output stream. + * Write the value of this auto-diff variable and to the output stream. * * @param os Output stream to which to write. * @param v Variable to write. @@ -474,6 +473,21 @@ class var { return os << "uninitialized"; return os << v.val(); } + + /** + * Read the value of this auto-diff variable from + * the specified input stream. + * + * @param[in] is Input stream to read + * @param v Variable to write. + * @return Reference to the specified input stream. + */ + friend std::istream& operator>>(std::istream& is, var& v) { + double x; + is >> x; + v = x; + return is; + } }; } // namespace math diff --git a/test/unit/math/rev/core/std_complex_test.cpp b/test/unit/math/rev/core/std_complex_test.cpp index b32eb842130..d0896c26df9 100644 --- a/test/unit/math/rev/core/std_complex_test.cpp +++ b/test/unit/math/rev/core/std_complex_test.cpp @@ -311,23 +311,66 @@ TEST_F(MathRev, comparison) { EXPECT_EQ(stack_size, stan::math::ChainableStack::instance().var_stack_.size()); } -/* + TEST_F(MathRev, serialize_deserialize) { + std::stringstream msg; + + std::complex x{1, 2}; + int stack_size = stan::math::ChainableStack::instance().var_stack_.size(); + // operator<< + msg << x; + EXPECT_EQ("(1,2)", msg.str()); + EXPECT_EQ(stack_size, + stan::math::ChainableStack::instance().var_stack_.size()); + // operator>> - ADD_FAILURE() << "not yet implemented"; + std::complex y; + stack_size = stan::math::ChainableStack::instance().var_stack_.size(); + msg >> y; + EXPECT_EQ(1, y.real().val()); + EXPECT_EQ(2, y.imag().val()); + EXPECT_EQ(stack_size + 2, + stan::math::ChainableStack::instance().var_stack_.size()); } TEST_F(MathRev, real) { - // real - ADD_FAILURE() << "not yet implemented"; + std::complex x{1, 2}; + + EXPECT_EQ(2, stan::math::ChainableStack::instance().var_stack_.size()); + + EXPECT_EQ(1, x.real().val()); + EXPECT_EQ(2, stan::math::ChainableStack::instance().var_stack_.size()); + + EXPECT_NO_THROW(x.real(2)); + EXPECT_EQ(2, x.real().val()); + EXPECT_EQ(3, stan::math::ChainableStack::instance().var_stack_.size()); + + stan::math::var y = 3; + EXPECT_NO_THROW(x.real(y)); + EXPECT_EQ(y, x.real().val()); + EXPECT_EQ(4, stan::math::ChainableStack::instance().var_stack_.size()); } TEST_F(MathRev, imag) { - // imag - ADD_FAILURE() << "not yet implemented"; + std::complex x{1, 2}; + + EXPECT_EQ(2, stan::math::ChainableStack::instance().var_stack_.size()); + + EXPECT_EQ(2, x.imag().val()); + EXPECT_EQ(2, stan::math::ChainableStack::instance().var_stack_.size()); + + EXPECT_NO_THROW(x.imag(-1)); + EXPECT_EQ(-1, x.imag().val()); + EXPECT_EQ(3, stan::math::ChainableStack::instance().var_stack_.size()); + + stan::math::var y = 3; + EXPECT_NO_THROW(x.imag(y)); + EXPECT_EQ(y, x.imag().val()); + EXPECT_EQ(4, stan::math::ChainableStack::instance().var_stack_.size()); } +/* TEST_F(MathRev, abs) { // abs ADD_FAILURE() << "not yet implemented"; From 8d215dc446ebad676f5561ebd4e9643480581eb7 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Fri, 31 Aug 2018 09:11:46 +0300 Subject: [PATCH 08/21] adding isnan and isinf overloads for std::complex --- stan/math/rev/core/std_complex.hpp | 11 ++- test/unit/math/rev/core/std_complex_test.cpp | 77 +++++++++++++++++--- 2 files changed, 78 insertions(+), 10 deletions(-) diff --git a/stan/math/rev/core/std_complex.hpp b/stan/math/rev/core/std_complex.hpp index 23770325ba1..81614506d47 100644 --- a/stan/math/rev/core/std_complex.hpp +++ b/stan/math/rev/core/std_complex.hpp @@ -1,9 +1,10 @@ #ifndef STAN_MATH_REV_CORE_STD_COMPLEX_HPP #define STAN_MATH_REV_CORE_STD_COMPLEX_HPP -#include +#include #include #include +#include #include namespace std { @@ -95,5 +96,13 @@ inline bool operator!=(double x, const complex& y) { return !(x == y); } +inline int isinf(const std::complex& a) { + return stan::math::is_inf(a.real().val()); +} + +inline int isnan(const std::complex& a) { + return stan::math::is_nan(a.real().val()); +} + } // namespace std #endif diff --git a/test/unit/math/rev/core/std_complex_test.cpp b/test/unit/math/rev/core/std_complex_test.cpp index d0896c26df9..fb9db47590d 100644 --- a/test/unit/math/rev/core/std_complex_test.cpp +++ b/test/unit/math/rev/core/std_complex_test.cpp @@ -370,25 +370,84 @@ TEST_F(MathRev, imag) { EXPECT_EQ(4, stan::math::ChainableStack::instance().var_stack_.size()); } -/* TEST_F(MathRev, abs) { - // abs - ADD_FAILURE() << "not yet implemented"; + std::complex z{3, 4}; + stan::math::var f = abs(z); + EXPECT_EQ(5, f.val()); + std::vector x{real(z)}; + std::vector g; + f.grad(x, g); + EXPECT_FLOAT_EQ(0.6, g[0]); } TEST_F(MathRev, arg) { - // arg - ADD_FAILURE() << "not yet implemented"; + std::complex z{1, stan::math::pi()}; + stan::math::var f = arg(z); + EXPECT_EQ(f.val(), stan::math::atan2(std::imag(z), std::real(z))); +} + +TEST_F(MathRev, isinf) { + std::complex a{0, 0}; + std::complex b{0, 0}; + EXPECT_EQ(std::isinf(b), std::isinf(a)); + EXPECT_FALSE(std::isinf(a)); + + a.real(std::numeric_limits::infinity()); + a.imag(0); + b.real(std::numeric_limits::infinity()); + b.imag(0); + EXPECT_EQ(std::isinf(b), std::isinf(a)); + EXPECT_TRUE(std::isinf(a)); + + a.real(0); + a.imag(std::numeric_limits::infinity()); + b.real(0); + b.imag(std::numeric_limits::infinity()); + EXPECT_EQ(std::isinf(b), std::isinf(a)); + EXPECT_FALSE(std::isinf(a)); +} + +TEST_F(MathRev, isnan) { + std::complex a{0, 0}; + std::complex b{0, 0}; + EXPECT_EQ(std::isnan(b), std::isnan(a)); + EXPECT_FALSE(std::isnan(a)); + + a.real(std::numeric_limits::quiet_NaN()); + a.imag(0); + b.real(std::numeric_limits::quiet_NaN()); + b.imag(0); + EXPECT_EQ(std::isnan(b), std::isnan(a)); + EXPECT_TRUE(std::isnan(a)); + + a.real(0); + a.imag(std::numeric_limits::quiet_NaN()); + b.real(0); + b.imag(std::numeric_limits::quiet_NaN()); + EXPECT_EQ(std::isnan(b), std::isnan(a)); + EXPECT_FALSE(std::isnan(a)); } +/* TEST_F(MathRev, norm) { - // norm - ADD_FAILURE() << "not yet implemented"; + std::complex z{3, 4}; + stan::math::var f = norm(z); + EXPECT_EQ(f.val(), 25); + + std::vector x{imag(z)}; + std::vector g; + f.grad(x, g); + EXPECT_FLOAT_EQ(g[0], 8); } +*/ + +/* TEST_F(MathRev, conj) { - // conj - ADD_FAILURE() << "not yet implemented"; + std::complex z(1, 2); + stan::math::var f = conj(z); + EXPECT_EQ(real(f).val(), 1); + EXPECT_EQ(imag(f).val(), -2); } TEST_F(MathRev, proj) { From 99ae347bbd21af4705bcf345e6f93544eef1ad29 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Fri, 31 Aug 2018 10:56:21 +0300 Subject: [PATCH 09/21] adding more specializations and overloads for std::complex --- stan/math/rev/core/std_complex.hpp | 18 ++++++++++ test/unit/math/rev/core/std_complex_test.cpp | 38 +++++++++++++------- 2 files changed, 44 insertions(+), 12 deletions(-) diff --git a/stan/math/rev/core/std_complex.hpp b/stan/math/rev/core/std_complex.hpp index 81614506d47..32ef4083768 100644 --- a/stan/math/rev/core/std_complex.hpp +++ b/stan/math/rev/core/std_complex.hpp @@ -104,5 +104,23 @@ inline int isnan(const std::complex& a) { return stan::math::is_nan(a.real().val()); } +template <> +inline stan::math::var norm(const complex& c) { + return stan::math::square(c.real()) + stan::math::square(c.imag()); +} + +template <> +inline complex conj(const complex& c) { + return complex(c.real(), -c.imag()); +} + +template <> +inline complex proj(const complex& c) { + if (isinf(c.real()) || isinf(c.imag())) + return std::complex(stan::math::positive_infinity(), + copysign(0.0, c.imag().val())); + return c; +} + } // namespace std #endif diff --git a/test/unit/math/rev/core/std_complex_test.cpp b/test/unit/math/rev/core/std_complex_test.cpp index fb9db47590d..61b25d54050 100644 --- a/test/unit/math/rev/core/std_complex_test.cpp +++ b/test/unit/math/rev/core/std_complex_test.cpp @@ -9,7 +9,6 @@ class MathRev : public testing::Test { void SetUp() { stan::math::recover_memory(); } }; -/* TEST_F(MathRev, complex_constructor) { std::complex x; EXPECT_EQ(2, stan::math::ChainableStack::instance().var_stack_.size()); @@ -25,7 +24,7 @@ TEST_F(MathRev, complex_constructor) { EXPECT_EQ(2, stan::math::ChainableStack::instance().var_stack_.size()); stan::math::recover_memory(); } -*/ + TEST_F(MathRev, assignment_double) { double rhs = 1; @@ -428,7 +427,6 @@ TEST_F(MathRev, isnan) { EXPECT_FALSE(std::isnan(a)); } -/* TEST_F(MathRev, norm) { std::complex z{3, 4}; stan::math::var f = norm(z); @@ -439,24 +437,40 @@ TEST_F(MathRev, norm) { f.grad(x, g); EXPECT_FLOAT_EQ(g[0], 8); } -*/ - -/* TEST_F(MathRev, conj) { std::complex z(1, 2); - stan::math::var f = conj(z); + std::complex f = conj(z); EXPECT_EQ(real(f).val(), 1); EXPECT_EQ(imag(f).val(), -2); } TEST_F(MathRev, proj) { - // projg - ADD_FAILURE() << "not yet implemented"; + std::complex z1(1, 2); + std::complex f = proj(z1); + EXPECT_TRUE(f == z1) << f << std::endl; + + using stan::math::positive_infinity; + std::complex z2(positive_infinity(), -1); + f = proj(z2); + EXPECT_TRUE(f == std::complex(positive_infinity(), -0)) + << f << std::endl; + + using stan::math::negative_infinity; + std::complex z3(0, negative_infinity()); + f = proj(z3); + EXPECT_TRUE(f == std::complex(positive_infinity(), -0)) + << f << std::endl; + + std::complex z4(std::numeric_limits::quiet_NaN(), + -2.0); + f = proj(z4); + EXPECT_TRUE(stan::math::is_nan(f.real())); + EXPECT_FLOAT_EQ(-2.0, f.imag().val()); } TEST_F(MathRev, polar) { - // polar - ADD_FAILURE() << "not yet implemented"; + std::complex z = std::polar(1, 0); + stan::math::var f = arg(z); + EXPECT_EQ(f.val(), 0.0); } -*/ From ebaee2e85dbdeeed8719b2b2d28feff3a964d0ed Mon Sep 17 00:00:00 2001 From: Ben Date: Sat, 1 Sep 2018 17:10:59 +0200 Subject: [PATCH 10/21] Fixed operator* and operator/ implementation and tests (Issue #123) Fixed typo in var docs --- stan/math/rev/core/std_complex.hpp | 7 ++++-- stan/math/rev/core/var.hpp | 2 +- test/unit/math/rev/core/std_complex_test.cpp | 26 ++++++++++---------- 3 files changed, 19 insertions(+), 16 deletions(-) diff --git a/stan/math/rev/core/std_complex.hpp b/stan/math/rev/core/std_complex.hpp index 32ef4083768..6801cf46521 100644 --- a/stan/math/rev/core/std_complex.hpp +++ b/stan/math/rev/core/std_complex.hpp @@ -52,13 +52,16 @@ complex& complex::operator=( template <> complex operator/(const complex& z, const complex& w) { - return complex{z.real() / w.real(), z.imag() / w.imag()}; + return complex{z.real() * w.real() + z.imag() * w.imag(), + z.imag() * w.real() - z.real() * w.imag()} + / (square(w.real()) + square(w.imag())); } template <> complex operator*(const complex& z, const complex& w) { - return complex{z.real() * w.real(), z.imag() * w.imag()}; + return complex{z.real() * w.real() - z.imag() * w.imag(), + z.real() * w.imag() + z.imag() * w.real()}; } template <> diff --git a/stan/math/rev/core/var.hpp b/stan/math/rev/core/var.hpp index b5826431290..08385f3e45c 100644 --- a/stan/math/rev/core/var.hpp +++ b/stan/math/rev/core/var.hpp @@ -462,7 +462,7 @@ class var { inline var& operator/=(double b); /** - * Write the value of this auto-diff variable and to the output stream. + * Write the value of this auto-diff variable to the output stream. * * @param os Output stream to which to write. * @param v Variable to write. diff --git a/test/unit/math/rev/core/std_complex_test.cpp b/test/unit/math/rev/core/std_complex_test.cpp index 61b25d54050..dc0089248e0 100644 --- a/test/unit/math/rev/core/std_complex_test.cpp +++ b/test/unit/math/rev/core/std_complex_test.cpp @@ -112,23 +112,23 @@ TEST_F(MathRev, member_operators) { y = std::complex{3, 4}; EXPECT_EQ(4, stan::math::ChainableStack::instance().var_stack_.size()); x /= y; - EXPECT_FLOAT_EQ(1.0 / 3.0, x.real().val()); - EXPECT_FLOAT_EQ(0.5, x.imag().val()); + EXPECT_FLOAT_EQ(11.0 / 25.0, x.real().val()); + EXPECT_FLOAT_EQ(2.0 / 25.0, x.imag().val()); EXPECT_FLOAT_EQ(3, y.real().val()); EXPECT_FLOAT_EQ(4, y.imag().val()); - EXPECT_EQ(6, stan::math::ChainableStack::instance().var_stack_.size()); + EXPECT_EQ(15, stan::math::ChainableStack::instance().var_stack_.size()); stan::math::recover_memory(); - // operator*= + // operator* x = std::complex{1, 2}; y = std::complex{3, 4}; EXPECT_EQ(4, stan::math::ChainableStack::instance().var_stack_.size()); x *= y; - EXPECT_FLOAT_EQ(3, x.real().val()); - EXPECT_FLOAT_EQ(8, x.imag().val()); + EXPECT_FLOAT_EQ(-5, x.real().val()); + EXPECT_FLOAT_EQ(10.0, x.imag().val()); EXPECT_FLOAT_EQ(3, y.real().val()); EXPECT_FLOAT_EQ(4, y.imag().val()); - EXPECT_EQ(6, stan::math::ChainableStack::instance().var_stack_.size()); + EXPECT_EQ(10, stan::math::ChainableStack::instance().var_stack_.size()); stan::math::recover_memory(); } @@ -191,9 +191,9 @@ TEST_F(MathRev, arithmetic) { y = std::complex{3, 4}; z = x * y; - EXPECT_EQ(6, stan::math::ChainableStack::instance().var_stack_.size()); - EXPECT_FLOAT_EQ(3, z.real().val()); - EXPECT_FLOAT_EQ(8, z.imag().val()); + EXPECT_EQ(10, stan::math::ChainableStack::instance().var_stack_.size()); + EXPECT_FLOAT_EQ(-5, z.real().val()); + EXPECT_FLOAT_EQ(10, z.imag().val()); EXPECT_FLOAT_EQ(1, x.real().val()); EXPECT_FLOAT_EQ(2, x.imag().val()); EXPECT_FLOAT_EQ(3, y.real().val()); @@ -205,9 +205,9 @@ TEST_F(MathRev, arithmetic) { y = std::complex{3, 4}; z = x / y; - EXPECT_EQ(6, stan::math::ChainableStack::instance().var_stack_.size()); - EXPECT_FLOAT_EQ(1.0 / 3.0, z.real().val()); - EXPECT_FLOAT_EQ(0.5, z.imag().val()); + EXPECT_EQ(15, stan::math::ChainableStack::instance().var_stack_.size()); + EXPECT_FLOAT_EQ(11.0 / 25.0, z.real().val()); + EXPECT_FLOAT_EQ(2.0 / 25.0, z.imag().val()); EXPECT_FLOAT_EQ(1, x.real().val()); EXPECT_FLOAT_EQ(2, x.imag().val()); EXPECT_FLOAT_EQ(3, y.real().val()); From 9f6df8bc8b53e5235dd0c7fe8646ae8e890ea5a0 Mon Sep 17 00:00:00 2001 From: Ben Date: Sat, 1 Sep 2018 17:31:01 +0200 Subject: [PATCH 11/21] Added specializations of operator!= to complex to avoid segfaults (Issue #123) --- stan/math/rev/core/std_complex.hpp | 32 +++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/stan/math/rev/core/std_complex.hpp b/stan/math/rev/core/std_complex.hpp index 6801cf46521..e629c5f6ef1 100644 --- a/stan/math/rev/core/std_complex.hpp +++ b/stan/math/rev/core/std_complex.hpp @@ -49,12 +49,17 @@ complex& complex::operator=( return *this; } +template <> +inline stan::math::var norm(const complex& c) { + return stan::math::square(c.real()) + stan::math::square(c.imag()); +} + template <> complex operator/(const complex& z, const complex& w) { return complex{z.real() * w.real() + z.imag() * w.imag(), z.imag() * w.real() - z.real() * w.imag()} - / (square(w.real()) + square(w.imag())); + / norm(w); } template <> @@ -91,12 +96,29 @@ inline bool operator==(double x, const complex& y) { return y == x; } +template <> +inline bool operator!=(const complex& x, + const complex& y) { + return x.real() != y.real() || x.imag() != y.imag(); +} + +inline bool operator!=(const complex& x, + const stan::math::var& y) { + return (x.real() != y || x.imag() != 0); +} + +inline bool operator!=(const stan::math::var& x, + const complex& y) { + return y != x; +} + inline bool operator!=(const complex& x, double y) { - return !(x == y); + return (x.real() != y || x.imag() != 0); + ; } inline bool operator!=(double x, const complex& y) { - return !(x == y); + return y != x; } inline int isinf(const std::complex& a) { @@ -108,8 +130,8 @@ inline int isnan(const std::complex& a) { } template <> -inline stan::math::var norm(const complex& c) { - return stan::math::square(c.real()) + stan::math::square(c.imag()); +inline stan::math::var abs(const complex& c) { + return stan::math::sqrt(norm(c)); } template <> From 0fced70e7c2b49f4dadea33708122e42d8bc1973 Mon Sep 17 00:00:00 2001 From: Ben Date: Sun, 2 Sep 2018 11:32:06 +0200 Subject: [PATCH 12/21] Added specializations for std::complex operator*= and operator/= (Issue #123) --- stan/math/rev/core/std_complex.hpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/stan/math/rev/core/std_complex.hpp b/stan/math/rev/core/std_complex.hpp index e629c5f6ef1..5c95fa48b91 100644 --- a/stan/math/rev/core/std_complex.hpp +++ b/stan/math/rev/core/std_complex.hpp @@ -69,6 +69,22 @@ complex operator*(const complex& z, z.real() * w.imag() + z.imag() * w.real()}; } +template <> +template <> +complex& complex::operator/=( + const complex& y) { + *this = *this / y; + return *this; +} + +template <> +template <> +complex& complex::operator*=( + const complex& y) { + *this = *this * y; + return *this; +} + template <> inline bool operator==(const complex& x, const complex& y) { From 52353e68e5280eb9d3f3d7e85d3ececcb9fdeb4e Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Wed, 12 Sep 2018 23:48:37 -0400 Subject: [PATCH 13/21] moving definition of std::complex and including it in stan/math/rev/scal.hpp --- stan/math/rev/scal.hpp | 1 + stan/math/rev/{core => scal/fun}/std_complex.hpp | 12 ++++++++++-- .../math/rev/{core => scal/fun}/std_complex_test.cpp | 2 +- 3 files changed, 12 insertions(+), 3 deletions(-) rename stan/math/rev/{core => scal/fun}/std_complex.hpp (90%) rename test/unit/math/rev/{core => scal/fun}/std_complex_test.cpp (99%) diff --git a/stan/math/rev/scal.hpp b/stan/math/rev/scal.hpp index 96c43309e2d..cedf76fa4b2 100644 --- a/stan/math/rev/scal.hpp +++ b/stan/math/rev/scal.hpp @@ -92,6 +92,7 @@ #include #include #include +#include #include #include #include diff --git a/stan/math/rev/core/std_complex.hpp b/stan/math/rev/scal/fun/std_complex.hpp similarity index 90% rename from stan/math/rev/core/std_complex.hpp rename to stan/math/rev/scal/fun/std_complex.hpp index 5c95fa48b91..a4bacbdfbeb 100644 --- a/stan/math/rev/core/std_complex.hpp +++ b/stan/math/rev/scal/fun/std_complex.hpp @@ -1,10 +1,11 @@ -#ifndef STAN_MATH_REV_CORE_STD_COMPLEX_HPP -#define STAN_MATH_REV_CORE_STD_COMPLEX_HPP +#ifndef STAN_MATH_REV_SCAL_FUN_STD_COMPLEX_HPP +#define STAN_MATH_REV_SCAL_FUN_STD_COMPLEX_HPP #include #include #include #include +#include #include namespace std { @@ -21,6 +22,13 @@ namespace std { * Promotion from primitives to stan::math::var applies and this * constructor will handle the primitivies. * + * This file is located in the stan/math/rev/scal/fun folder + * intentionally. The implementation depends on templated functions + * being template instantiated in the correct order. Although the rest + * of the std implementations are in stan/math/rev/core, this one is + * different because it requires functions defined for + * stan::math::var. + * * @param re real component * @param im imaginary component */ diff --git a/test/unit/math/rev/core/std_complex_test.cpp b/test/unit/math/rev/scal/fun/std_complex_test.cpp similarity index 99% rename from test/unit/math/rev/core/std_complex_test.cpp rename to test/unit/math/rev/scal/fun/std_complex_test.cpp index dc0089248e0..ad44c0ce2c8 100644 --- a/test/unit/math/rev/core/std_complex_test.cpp +++ b/test/unit/math/rev/scal/fun/std_complex_test.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include // For a definition of the spec: From 2d7bb4884f5250a8713b0d5fd97ce30e4814e870 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Thu, 13 Sep 2018 00:11:39 -0400 Subject: [PATCH 14/21] cpplint --- stan/math/rev/scal/fun/std_complex.hpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/stan/math/rev/scal/fun/std_complex.hpp b/stan/math/rev/scal/fun/std_complex.hpp index a4bacbdfbeb..d8bd0610c51 100644 --- a/stan/math/rev/scal/fun/std_complex.hpp +++ b/stan/math/rev/scal/fun/std_complex.hpp @@ -97,7 +97,6 @@ template <> inline bool operator==(const complex& x, const complex& y) { return x.real() == y.real() && x.imag() == y.imag(); - ; } template <> @@ -138,7 +137,6 @@ inline bool operator!=(const stan::math::var& x, inline bool operator!=(const complex& x, double y) { return (x.real() != y || x.imag() != 0); - ; } inline bool operator!=(double x, const complex& y) { From 966121e1a5e82fa4fc71098bee462171b2ae41c0 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Thu, 13 Sep 2018 00:27:02 -0400 Subject: [PATCH 15/21] cpplint --- test/unit/math/rev/scal/fun/std_complex_test.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/unit/math/rev/scal/fun/std_complex_test.cpp b/test/unit/math/rev/scal/fun/std_complex_test.cpp index ad44c0ce2c8..81829ef1b9c 100644 --- a/test/unit/math/rev/scal/fun/std_complex_test.cpp +++ b/test/unit/math/rev/scal/fun/std_complex_test.cpp @@ -1,6 +1,8 @@ #include #include #include +#include +#include // For a definition of the spec: // https://en.cppreference.com/w/cpp/numeric/complex From f456764eaa707c833d14aece18e8f5b0deeb9b89 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Thu, 13 Sep 2018 00:48:58 -0400 Subject: [PATCH 16/21] removing std qualifier where it's not necessary --- stan/math/rev/scal/fun/std_complex.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/stan/math/rev/scal/fun/std_complex.hpp b/stan/math/rev/scal/fun/std_complex.hpp index d8bd0610c51..294fc2dd025 100644 --- a/stan/math/rev/scal/fun/std_complex.hpp +++ b/stan/math/rev/scal/fun/std_complex.hpp @@ -143,11 +143,11 @@ inline bool operator!=(double x, const complex& y) { return y != x; } -inline int isinf(const std::complex& a) { +inline int isinf(const complex& a) { return stan::math::is_inf(a.real().val()); } -inline int isnan(const std::complex& a) { +inline int isnan(const complex& a) { return stan::math::is_nan(a.real().val()); } @@ -164,8 +164,8 @@ inline complex conj(const complex& c) { template <> inline complex proj(const complex& c) { if (isinf(c.real()) || isinf(c.imag())) - return std::complex(stan::math::positive_infinity(), - copysign(0.0, c.imag().val())); + return complex(stan::math::positive_infinity(), + copysign(0.0, c.imag().val())); return c; } From 1593d896f822da1a2c132db7724bf4a166564a7d Mon Sep 17 00:00:00 2001 From: Ben Date: Thu, 13 Sep 2018 14:23:17 +0200 Subject: [PATCH 17/21] Moved all Bgoodrich's tests over. Added sqrt and pow (Issue #123) --- stan/math/rev/scal/fun/std_complex.hpp | 25 ++++++++++++ test/unit/math/rev/scal/fun/abs_test.cpp | 10 +++++ test/unit/math/rev/scal/fun/acos_test.cpp | 11 ++++++ test/unit/math/rev/scal/fun/acosh_test.cpp | 11 ++++++ test/unit/math/rev/scal/fun/arg_test.cpp | 10 +++++ test/unit/math/rev/scal/fun/asin_test.cpp | 11 ++++++ test/unit/math/rev/scal/fun/asinh_test.cpp | 11 ++++++ test/unit/math/rev/scal/fun/atan_test.cpp | 11 ++++++ test/unit/math/rev/scal/fun/atanh_test.cpp | 11 ++++++ test/unit/math/rev/scal/fun/cos_test.cpp | 11 ++++++ test/unit/math/rev/scal/fun/cosh_test.cpp | 11 ++++++ test/unit/math/rev/scal/fun/exp_test.cpp | 12 ++++++ test/unit/math/rev/scal/fun/log10_test.cpp | 11 ++++++ test/unit/math/rev/scal/fun/log_test.cpp | 13 +++++++ test/unit/math/rev/scal/fun/sin_test.cpp | 11 ++++++ test/unit/math/rev/scal/fun/sinh_test.cpp | 11 ++++++ .../math/rev/scal/fun/std_complex_test.cpp | 39 +++++++++++++++++++ test/unit/math/rev/scal/fun/tan_test.cpp | 11 ++++++ test/unit/math/rev/scal/fun/tanh_test.cpp | 11 ++++++ 19 files changed, 252 insertions(+) create mode 100644 test/unit/math/rev/scal/fun/arg_test.cpp diff --git a/stan/math/rev/scal/fun/std_complex.hpp b/stan/math/rev/scal/fun/std_complex.hpp index 294fc2dd025..a955ef8e1fc 100644 --- a/stan/math/rev/scal/fun/std_complex.hpp +++ b/stan/math/rev/scal/fun/std_complex.hpp @@ -7,6 +7,7 @@ #include #include #include +#include namespace std { @@ -156,6 +157,30 @@ inline stan::math::var abs(const complex& c) { return stan::math::sqrt(norm(c)); } +template <> +inline complex pow(const complex& x, + const complex& y) { + return (x == 0.0) ? complex(0.0, 0.0) + : std::exp(y * std::log(x)); +} + +template <> +inline complex sqrt(const complex& c) { + if (c.real() == 0.0) { + stan::math::var t = sqrt(abs(c.imag()) / 2.0); + return complex(t, + ((c.imag() > 0.0) - (c.imag() < 0.0)) * t); + } else { + stan::math::var t = sqrt(2.0 * (abs(c) + abs(c.real()))); + if (c.real() > 0.0) { + return complex(t / 2.0, (c.imag() / t)); + } else { + return complex( + abs(c.imag()) / t, ((c.imag() > 0) - (c.imag() < 0)) * t / 2.0); + } + } +} + template <> inline complex conj(const complex& c) { return complex(c.real(), -c.imag()); diff --git a/test/unit/math/rev/scal/fun/abs_test.cpp b/test/unit/math/rev/scal/fun/abs_test.cpp index 1789568a751..4870529381c 100644 --- a/test/unit/math/rev/scal/fun/abs_test.cpp +++ b/test/unit/math/rev/scal/fun/abs_test.cpp @@ -78,3 +78,13 @@ TEST(AgradRev, check_varis_on_stack) { AVAR a = 0.68; test::check_varis_on_stack(stan::math::abs(a)); } + +TEST(AgradRev, abs_complex) { + std::complex z = std::complex(3, 4); + auto f = abs(z); + EXPECT_EQ(5, f.val()); + AVEC x = createAVEC(real(z)); + VEC g; + f.grad(x, g); + EXPECT_FLOAT_EQ(0.6, g[0]); +} diff --git a/test/unit/math/rev/scal/fun/acos_test.cpp b/test/unit/math/rev/scal/fun/acos_test.cpp index 830e92d00b5..abd07f5c236 100644 --- a/test/unit/math/rev/scal/fun/acos_test.cpp +++ b/test/unit/math/rev/scal/fun/acos_test.cpp @@ -74,3 +74,14 @@ TEST(AgradRev, check_varis_on_stack) { AVAR a = 0.68; test::check_varis_on_stack(stan::math::acos(a)); } + +TEST(AgradRev, complex) { + stan::math::var x = 2.0 / 3; + double h = 1e-8; + std::complex z(x, h); + auto f = acos(z); + AVEC v = createAVEC(real(z)); + VEC g; + real(f).grad(v, g); + EXPECT_FLOAT_EQ(g[0], imag(f).val() / h); +} diff --git a/test/unit/math/rev/scal/fun/acosh_test.cpp b/test/unit/math/rev/scal/fun/acosh_test.cpp index 0d70add0e8a..36facf3764e 100644 --- a/test/unit/math/rev/scal/fun/acosh_test.cpp +++ b/test/unit/math/rev/scal/fun/acosh_test.cpp @@ -61,3 +61,14 @@ TEST(AgradRev, check_varis_on_stack) { AVAR a = 1.3; test::check_varis_on_stack(stan::math::acosh(a)); } + +TEST(AgradRev, complex) { + stan::math::var x = 3.0 / 2; + double h = 1e-8; + std::complex z(x, h); + auto f = acosh(z); + AVEC v = createAVEC(real(z)); + VEC g; + real(f).grad(v, g); + EXPECT_FLOAT_EQ(g[0], imag(f).val() / h); +} diff --git a/test/unit/math/rev/scal/fun/arg_test.cpp b/test/unit/math/rev/scal/fun/arg_test.cpp new file mode 100644 index 00000000000..d0dc22244b5 --- /dev/null +++ b/test/unit/math/rev/scal/fun/arg_test.cpp @@ -0,0 +1,10 @@ +#include +#include + +TEST(AgradRev, arg_test) { + std::complex z + = std::complex(1, stan::math::pi()); + auto f = arg(z); + using stan::math::atan2; + EXPECT_EQ(f.val(), atan2(std::imag(z), std::real(z))); +} diff --git a/test/unit/math/rev/scal/fun/asin_test.cpp b/test/unit/math/rev/scal/fun/asin_test.cpp index 80f72444ed3..f01aed5e0ac 100644 --- a/test/unit/math/rev/scal/fun/asin_test.cpp +++ b/test/unit/math/rev/scal/fun/asin_test.cpp @@ -74,3 +74,14 @@ TEST(AgradRev, check_varis_on_stack) { AVAR a = 0.68; test::check_varis_on_stack(stan::math::asin(a)); } + +TEST(AgradRev, complex) { + stan::math::var x = 2.0 / 3; + double h = 1e-8; + std::complex z(x, h); + auto f = asin(z); + AVEC v = createAVEC(real(z)); + VEC g; + real(f).grad(v, g); + EXPECT_FLOAT_EQ(g[0], imag(f).val() / h); +} diff --git a/test/unit/math/rev/scal/fun/asinh_test.cpp b/test/unit/math/rev/scal/fun/asinh_test.cpp index e215c6ab5f7..d66086e67e4 100644 --- a/test/unit/math/rev/scal/fun/asinh_test.cpp +++ b/test/unit/math/rev/scal/fun/asinh_test.cpp @@ -61,3 +61,14 @@ TEST(AgradRev, check_varis_on_stack) { AVAR a = 0.2; test::check_varis_on_stack(stan::math::asinh(a)); } + +TEST(AgradRev, complex) { + stan::math::var x = 2.0 / 3; + double h = 1e-8; + std::complex z(x, h); + auto f = asinh(z); + AVEC v = createAVEC(real(z)); + VEC g; + real(f).grad(v, g); + EXPECT_FLOAT_EQ(g[0], imag(f).val() / h); +} diff --git a/test/unit/math/rev/scal/fun/atan_test.cpp b/test/unit/math/rev/scal/fun/atan_test.cpp index f1c62c404ca..d18b4180af8 100644 --- a/test/unit/math/rev/scal/fun/atan_test.cpp +++ b/test/unit/math/rev/scal/fun/atan_test.cpp @@ -62,3 +62,14 @@ TEST(AgradRev, check_varis_on_stack) { AVAR a = 1; test::check_varis_on_stack(stan::math::atan(a)); } + +TEST(AgradRev, complex) { + stan::math::var x = 2.0 / 3; + double h = 1e-8; + std::complex z(x, h); + auto f = atan(z); + AVEC v = createAVEC(real(z)); + VEC g; + real(f).grad(v, g); + EXPECT_FLOAT_EQ(g[0], imag(f).val() / h); +} diff --git a/test/unit/math/rev/scal/fun/atanh_test.cpp b/test/unit/math/rev/scal/fun/atanh_test.cpp index 35847145ba8..8b135e5d3a9 100644 --- a/test/unit/math/rev/scal/fun/atanh_test.cpp +++ b/test/unit/math/rev/scal/fun/atanh_test.cpp @@ -61,3 +61,14 @@ TEST(AgradRev, check_varis_on_stack) { AVAR a = 0.3; test::check_varis_on_stack(stan::math::atanh(a)); } + +TEST(AgradRev, complex) { + stan::math::var x = 2.0 / 3; + double h = 1e-8; + std::complex z(x, h); + auto f = atanh(z); + AVEC v = createAVEC(real(z)); + VEC g; + real(f).grad(v, g); + EXPECT_FLOAT_EQ(g[0], imag(f).val() / h); +} diff --git a/test/unit/math/rev/scal/fun/cos_test.cpp b/test/unit/math/rev/scal/fun/cos_test.cpp index 172d18a95e8..132a378a22f 100644 --- a/test/unit/math/rev/scal/fun/cos_test.cpp +++ b/test/unit/math/rev/scal/fun/cos_test.cpp @@ -51,3 +51,14 @@ TEST(AgradRev, check_varis_on_stack) { AVAR a = 0.49; test::check_varis_on_stack(stan::math::cos(a)); } + +TEST(AgradRev, complex) { + stan::math::var x = 2.0 / 3; + double h = 1e-8; + std::complex z(x, h); + auto f = cos(z); + AVEC v = createAVEC(real(z)); + VEC g; + real(f).grad(v, g); + EXPECT_FLOAT_EQ(g[0], imag(f).val() / h); +} diff --git a/test/unit/math/rev/scal/fun/cosh_test.cpp b/test/unit/math/rev/scal/fun/cosh_test.cpp index 87a814c3ec7..6b1196d683e 100644 --- a/test/unit/math/rev/scal/fun/cosh_test.cpp +++ b/test/unit/math/rev/scal/fun/cosh_test.cpp @@ -67,3 +67,14 @@ TEST(AgradRev, check_varis_on_stack) { AVAR a = 0.68; test::check_varis_on_stack(stan::math::cosh(a)); } + +TEST(AgradRev, complex) { + stan::math::var x = 2.0 / 3; + double h = 1e-8; + std::complex z(x, h); + auto f = cosh(z); + AVEC v = createAVEC(real(z)); + VEC g; + real(f).grad(v, g); + EXPECT_FLOAT_EQ(g[0], imag(f).val() / h); +} diff --git a/test/unit/math/rev/scal/fun/exp_test.cpp b/test/unit/math/rev/scal/fun/exp_test.cpp index 98a9dfd3e99..399ee2e3a2f 100644 --- a/test/unit/math/rev/scal/fun/exp_test.cpp +++ b/test/unit/math/rev/scal/fun/exp_test.cpp @@ -30,3 +30,15 @@ TEST(AgradRev, check_varis_on_stack) { AVAR a(6.0); test::check_varis_on_stack(stan::math::exp(a)); } + +TEST(AgradRev, euler) { + std::complex z + = std::complex(0.0, stan::math::pi()); + std::complex f = exp(z); + EXPECT_FLOAT_EQ(-1, real(f).val()); + EXPECT_FLOAT_EQ(1, 1 + imag(f).val()); + AVEC x = createAVEC(imag(z)); + VEC g; + real(f).grad(x, g); + EXPECT_FLOAT_EQ(1 + g[0], 1); +} diff --git a/test/unit/math/rev/scal/fun/log10_test.cpp b/test/unit/math/rev/scal/fun/log10_test.cpp index 9ab86536e3e..b72f7aeeb24 100644 --- a/test/unit/math/rev/scal/fun/log10_test.cpp +++ b/test/unit/math/rev/scal/fun/log10_test.cpp @@ -29,3 +29,14 @@ TEST(AgradRev, check_varis_on_stack) { stan::math::var x = 4.0; test::check_varis_on_stack(stan::math::log10(x)); } + +TEST(AgradRev, complex) { + stan::math::var x = stan::math::pi(); + double h = 1e-8; + std::complex z(x, h); + auto f = log10(z); + AVEC v = createAVEC(real(z)); + VEC g; + real(f).grad(v, g); + EXPECT_FLOAT_EQ(g[0], imag(f).val() / h); +} diff --git a/test/unit/math/rev/scal/fun/log_test.cpp b/test/unit/math/rev/scal/fun/log_test.cpp index 7d8fc58f1e9..244ab6dbafc 100644 --- a/test/unit/math/rev/scal/fun/log_test.cpp +++ b/test/unit/math/rev/scal/fun/log_test.cpp @@ -46,3 +46,16 @@ TEST(AgradRev, check_varis_on_stack) { AVAR a(5.0); test::check_varis_on_stack(stan::math::log(a)); } + +TEST(AgradRev, complex) { + stan::math::var x = stan::math::pi(); + std::complex z(x, 2.0 / 3); + EXPECT_TRUE(log(conj(z)) == conj(log(z))); + double h = 1e-8; + z = std::complex(x, h); + auto f = log(z); + AVEC v = createAVEC(real(z)); + VEC g; + real(f).grad(v, g); + EXPECT_FLOAT_EQ(g[0], imag(f).val() / h); +} diff --git a/test/unit/math/rev/scal/fun/sin_test.cpp b/test/unit/math/rev/scal/fun/sin_test.cpp index 879d1a29167..96ebb74103c 100644 --- a/test/unit/math/rev/scal/fun/sin_test.cpp +++ b/test/unit/math/rev/scal/fun/sin_test.cpp @@ -51,3 +51,14 @@ TEST(AgradRev, check_varis_on_stack) { AVAR a = 0.49; test::check_varis_on_stack(stan::math::sin(a)); } + +TEST(AgradRev, complex) { + stan::math::var x = 2.0 / 3; + double h = 1e-8; + std::complex z(x, h); + auto f = sin(z); + AVEC v = createAVEC(real(z)); + VEC g; + real(f).grad(v, g); + EXPECT_FLOAT_EQ(g[0], imag(f).val() / h); +} diff --git a/test/unit/math/rev/scal/fun/sinh_test.cpp b/test/unit/math/rev/scal/fun/sinh_test.cpp index fb2f8ff1508..686faf6f2bb 100644 --- a/test/unit/math/rev/scal/fun/sinh_test.cpp +++ b/test/unit/math/rev/scal/fun/sinh_test.cpp @@ -66,3 +66,14 @@ TEST(AgradRev, check_varis_on_stack) { AVAR a = 0.68; test::check_varis_on_stack(stan::math::sinh(a)); } + +TEST(AgradRev, complex) { + stan::math::var x = 2.0 / 3; + double h = 1e-8; + std::complex z(x, h); + auto f = sinh(z); + AVEC v = createAVEC(real(z)); + VEC g; + real(f).grad(v, g); + EXPECT_FLOAT_EQ(g[0], imag(f).val() / h); +} diff --git a/test/unit/math/rev/scal/fun/std_complex_test.cpp b/test/unit/math/rev/scal/fun/std_complex_test.cpp index 81829ef1b9c..cb1cda2d9da 100644 --- a/test/unit/math/rev/scal/fun/std_complex_test.cpp +++ b/test/unit/math/rev/scal/fun/std_complex_test.cpp @@ -476,3 +476,42 @@ TEST_F(MathRev, polar) { stan::math::var f = arg(z); EXPECT_EQ(f.val(), 0.0); } + +TEST_F(MathRev, sqrt_conj) { + std::complex z(-5, 12); + EXPECT_TRUE(std::sqrt(std::conj(z)) == std::conj(std::sqrt(z))); +} + +TEST_F(MathRev, sqrt_negative_real) { + std::complex z(-5, 12); + std::complex zsqrt = sqrt(z); + EXPECT_FLOAT_EQ(2, zsqrt.real().val()); + EXPECT_FLOAT_EQ(3, zsqrt.imag().val()); +} + +TEST_F(MathRev, sqrt_positive_real) { + std::complex z(9, 40); + std::complex zsqrt = sqrt(z); + EXPECT_FLOAT_EQ(5, zsqrt.real().val()); + EXPECT_FLOAT_EQ(4, zsqrt.imag().val()); +} + +TEST_F(MathRev, sqrt_zero_real) { + std::complex z(0, 8); + std::complex zsqrt = sqrt(z); + EXPECT_FLOAT_EQ(2, zsqrt.real().val()); + EXPECT_FLOAT_EQ(2, zsqrt.imag().val()); +} + +TEST_F(MathRev, pow) { + std::complex i(0, 1); + auto f = pow(i, i); + EXPECT_EQ(real(f).val(), exp(-stan::math::pi() / 2)); +} + +TEST_F(MathRev, pow_zero) { + std::complex i(0, 0); + auto f = pow(i, i); + EXPECT_EQ(0, real(f).val()); + EXPECT_EQ(0, imag(f).val()); +} diff --git a/test/unit/math/rev/scal/fun/tan_test.cpp b/test/unit/math/rev/scal/fun/tan_test.cpp index 7632639bdf7..23349974bca 100644 --- a/test/unit/math/rev/scal/fun/tan_test.cpp +++ b/test/unit/math/rev/scal/fun/tan_test.cpp @@ -53,3 +53,14 @@ TEST(AgradRev, check_varis_on_stack) { AVAR a = 0.68; test::check_varis_on_stack(stan::math::tan(a)); } + +TEST(AgradRev, complex) { + stan::math::var x = 2.0 / 3; + double h = 1e-8; + std::complex z(x, h); + auto f = tan(z); + AVEC v = createAVEC(real(z)); + VEC g; + real(f).grad(v, g); + EXPECT_FLOAT_EQ(g[0], imag(f).val() / h); +} diff --git a/test/unit/math/rev/scal/fun/tanh_test.cpp b/test/unit/math/rev/scal/fun/tanh_test.cpp index 2620da9d11d..571ed0aa595 100644 --- a/test/unit/math/rev/scal/fun/tanh_test.cpp +++ b/test/unit/math/rev/scal/fun/tanh_test.cpp @@ -66,3 +66,14 @@ TEST(AgradRev, check_varis_on_stack) { AVAR a = 0.68; test::check_varis_on_stack(stan::math::tanh(a)); } + +TEST(AgradRev, complex) { + stan::math::var x = 2.0 / 3; + double h = 1e-8; + std::complex z(x, h); + auto f = tanh(z); + AVEC v = createAVEC(real(z)); + VEC g; + real(f).grad(v, g); + EXPECT_FLOAT_EQ(g[0], imag(f).val() / h); +} From 2d00372e580030444faf69ddf06aa1b10a36219f Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Thu, 13 Sep 2018 11:33:45 -0400 Subject: [PATCH 18/21] adding implementation of std::exp(std::complex) --- stan/math/rev/scal/fun/std_complex.hpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/stan/math/rev/scal/fun/std_complex.hpp b/stan/math/rev/scal/fun/std_complex.hpp index a955ef8e1fc..08dff7012f0 100644 --- a/stan/math/rev/scal/fun/std_complex.hpp +++ b/stan/math/rev/scal/fun/std_complex.hpp @@ -157,6 +157,12 @@ inline stan::math::var abs(const complex& c) { return stan::math::sqrt(norm(c)); } +template<> +inline complex exp(const complex& z) { + stan::math::var exp_x = exp(z.real()); + return complex(exp_x * cos(z.imag()), exp_x * sin(z.imag())); +} + template <> inline complex pow(const complex& x, const complex& y) { From 2937e721f0b6654835016a6bd9857fdb00cf5df5 Mon Sep 17 00:00:00 2001 From: Stan Jenkins Date: Thu, 13 Sep 2018 11:34:35 -0400 Subject: [PATCH 19/21] [Jenkins] auto-formatting by clang-format version 6.0.0 (tags/google/stable/2017-11-14) --- stan/math/rev/scal/fun/std_complex.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stan/math/rev/scal/fun/std_complex.hpp b/stan/math/rev/scal/fun/std_complex.hpp index 08dff7012f0..f21d9e31906 100644 --- a/stan/math/rev/scal/fun/std_complex.hpp +++ b/stan/math/rev/scal/fun/std_complex.hpp @@ -157,7 +157,7 @@ inline stan::math::var abs(const complex& c) { return stan::math::sqrt(norm(c)); } -template<> +template <> inline complex exp(const complex& z) { stan::math::var exp_x = exp(z.real()); return complex(exp_x * cos(z.imag()), exp_x * sin(z.imag())); From bbbd7c59317316ade5e0ab0f626f9506315992f2 Mon Sep 17 00:00:00 2001 From: Daniel Lee Date: Tue, 22 Jan 2019 23:47:04 -0500 Subject: [PATCH 20/21] updating std complex var --- stan/math/rev/scal/fun/std_complex.hpp | 27 ++++++++++++++++++- .../math/rev/scal/fun/std_complex_test.cpp | 6 ++--- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/stan/math/rev/scal/fun/std_complex.hpp b/stan/math/rev/scal/fun/std_complex.hpp index f21d9e31906..c7f2b18f845 100644 --- a/stan/math/rev/scal/fun/std_complex.hpp +++ b/stan/math/rev/scal/fun/std_complex.hpp @@ -78,6 +78,19 @@ complex operator*(const complex& z, z.real() * w.imag() + z.imag() * w.real()}; } +complex operator*(const complex& z, + double w) { + return complex{z.real() * w, + z.imag() * w}; +} + +complex operator*(double z, + const complex& w) { + return complex{z * w.real(), + z * w.imag()}; +} + + template <> template <> complex& complex::operator/=( @@ -166,7 +179,19 @@ inline complex exp(const complex& z) { template <> inline complex pow(const complex& x, const complex& y) { - return (x == 0.0) ? complex(0.0, 0.0) + return (x == 0.0) ? complex(1.0, 0.0) + : std::exp(y * std::log(x)); +} + +inline complex pow(const complex& x, + double y) { + return (x == 0.0) ? complex(1.0, 0.0) + : std::exp(y * std::log(x)); +} + +inline complex pow(double x, + const complex& y) { + return (x == 0.0) ? complex(1.0, 0.0) : std::exp(y * std::log(x)); } diff --git a/test/unit/math/rev/scal/fun/std_complex_test.cpp b/test/unit/math/rev/scal/fun/std_complex_test.cpp index cb1cda2d9da..0b2a830b53f 100644 --- a/test/unit/math/rev/scal/fun/std_complex_test.cpp +++ b/test/unit/math/rev/scal/fun/std_complex_test.cpp @@ -505,13 +505,13 @@ TEST_F(MathRev, sqrt_zero_real) { TEST_F(MathRev, pow) { std::complex i(0, 1); - auto f = pow(i, i); + std::complex f = pow(i, i); EXPECT_EQ(real(f).val(), exp(-stan::math::pi() / 2)); } TEST_F(MathRev, pow_zero) { std::complex i(0, 0); - auto f = pow(i, i); - EXPECT_EQ(0, real(f).val()); + std::complex f = pow(i, i); + EXPECT_EQ(1, real(f).val()); EXPECT_EQ(0, imag(f).val()); } From d5d5c667b6f7c0c65ab831897d1e07e2d5bf6219 Mon Sep 17 00:00:00 2001 From: Stan Jenkins Date: Wed, 23 Jan 2019 04:47:39 +0000 Subject: [PATCH 21/21] [Jenkins] auto-formatting by clang-format version 5.0.0-3~16.04.1 (tags/RELEASE_500/final) --- stan/math/rev/scal/fun/std_complex.hpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/stan/math/rev/scal/fun/std_complex.hpp b/stan/math/rev/scal/fun/std_complex.hpp index c7f2b18f845..db592598313 100644 --- a/stan/math/rev/scal/fun/std_complex.hpp +++ b/stan/math/rev/scal/fun/std_complex.hpp @@ -80,17 +80,14 @@ complex operator*(const complex& z, complex operator*(const complex& z, double w) { - return complex{z.real() * w, - z.imag() * w}; + return complex{z.real() * w, z.imag() * w}; } complex operator*(double z, const complex& w) { - return complex{z * w.real(), - z * w.imag()}; + return complex{z * w.real(), z * w.imag()}; } - template <> template <> complex& complex::operator/=(