Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/0123 complex spec #1720

Merged
merged 29 commits into from
Mar 3, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
994d363
rc for part 1 of complex PR
Feb 16, 2020
2d93950
Merge branch 'develop' of https://github.com/stan-dev/math into featu…
Feb 16, 2020
c679e61
cpplint fixes
Feb 16, 2020
5c13696
Merge branch 'develop' of https://github.com/stan-dev/math into featu…
Feb 18, 2020
879f3ac
cleaned up constructors
Feb 18, 2020
ed61d81
cpplint cleanup for unary ctor
Feb 19, 2020
fe1bb95
Merge branch 'develop' of https://github.com/stan-dev/math into featu…
Feb 21, 2020
622d294
resolve merge for scalar_type
Feb 25, 2020
1b18e5f
Merge branch 'develop' of https://github.com/stan-dev/math into featu…
Feb 25, 2020
db7728c
enhanced scalar_type_test
Feb 25, 2020
f1c33dd
removed unused old scalar_type_test
Feb 25, 2020
f5e0107
intermediate checkpoint
Feb 25, 2020
4bcad0c
resolve scalar_type_test conflicts
Feb 28, 2020
6da512c
real_return in serializer
Feb 28, 2020
3b66c6f
remove unused header includess
Feb 28, 2020
a01d73a
Merge commit '3315de826e658bf8179a119f35d2ac5a7a53ee1c' into HEAD
yashikno Feb 28, 2020
9d040b5
[Jenkins] auto-formatting by clang-format version 5.0.0-3~16.04.1 (ta…
stan-buildbot Feb 28, 2020
273533b
complex: cleans up ctors and dtors and uses inherited operators
SteveBronder Feb 28, 2020
95a84fd
[Jenkins] auto-formatting by clang-format version 5.0.0-3~16.04.1 (ta…
stan-buildbot Feb 28, 2020
92d3ffa
Add constexpr to complex constructors and operators
SteveBronder Feb 28, 2020
3944dc6
merge to develop
SteveBronder Feb 28, 2020
b3c1ef7
[Jenkins] auto-formatting by clang-format version 5.0.0-3~16.04.1 (ta…
stan-buildbot Feb 28, 2020
2296e3f
uncomment complex tests
SteveBronder Feb 28, 2020
590ea12
merge to remote
SteveBronder Feb 28, 2020
af5ffbe
[Jenkins] auto-formatting by clang-format version 5.0.0-3~16.04.1 (ta…
stan-buildbot Feb 28, 2020
39e305a
doc cleanup, test getter/setter
Feb 29, 2020
78ca46f
Merge branch 'develop' of https://github.com/stan-dev/math into featu…
Feb 29, 2020
3e89f98
remove constexpr from complex
Feb 29, 2020
1f18017
nolint qualifier on unary ctor
Feb 29, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions stan/math/fwd/core.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <stan/math/fwd/core/operator_unary_minus.hpp>
#include <stan/math/fwd/core/operator_unary_not.hpp>
#include <stan/math/fwd/core/operator_unary_plus.hpp>
#include <stan/math/fwd/core/std_complex.hpp>
#include <stan/math/fwd/core/std_numeric_limits.hpp>
#include <stan/math/fwd/core/std_iterator_traits.hpp>

Expand Down
68 changes: 68 additions & 0 deletions stan/math/fwd/core/std_complex.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#ifndef STAN_MATH_FWD_CORE_STD_COMPLEX_HPP
#define STAN_MATH_FWD_CORE_STD_COMPLEX_HPP

#include <stan/math/prim/core/complex_base.hpp>
#include <stan/math/fwd/core/fvar.hpp>
#include <stan/math/prim/meta.hpp>
#include <complex>

namespace std {

/**
* Specialization of the standard libary complex number type for
* reverse-mode autodiff type `stan::math::fvar<T>`.
*
* @tparam T forward-mode autodiff value type
*/
template <typename T>
class complex<stan::math::fvar<T>>
: public stan::math::complex_base<stan::math::fvar<T>> {
Comment on lines +18 to +19
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is a crtp should this be

public stan::math::complex_base<complex<stan::math::fvar<T>>>

So the type is recurrent in the base class?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. The complex (which would have to be std::complex) is redundant. I only take the value type T as the template parameter, but the "derived" class is std::complex<T>. If I suppoied all of complex<fvar<T>>, then I'd just have to go fishing the T out with traits again.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is definitely redundant, though if I use a pattern I like keeping it standard instead of 'ish. But whether a strict CRTP pattern here would be better is totally subjective.

then I'd just have to go fishing the T out with traits again.

Would argue that type traits change here would be fishing in a barrel

  using value_type = value_type_t<V>;

  /**
   * Derived complex type used for function return types.
   */
  using complex_type = V;

public:
using base_t = stan::math::complex_base<stan::math::fvar<T>>;

/**
* Construct a complex number with zero real and imaginary parts.
*/
complex() = default;

/**
* Construct a complex number with the specified real part and a zero
* imaginary part.
*
* @tparam Scalar real type (must be assignable to `value_type`)
* @param[in] re real part
*/
template <typename U, typename = stan::require_stan_scalar_t<U>>
complex(U&& re) : base_t(re) {} // NOLINT(runtime/explicit)

/**
* Construct a complex number from the specified real and imaginary
* parts.
*
* @tparam U type of real part
* @tparam V type of imaginary part
* @param[in] re real part
* @param[in] im imaginary part
*/
template <typename U, typename V>
complex(const U& re, const V& im) : base_t(re, im) {}

/**
* Set the real and imaginary parts to those of the specified
* complex number.
*
* @tparam U value type of argument
* @param[in] x complex number to set
* @return this
*/
template <typename U, typename = stan::require_arithmetic_t<U>>
auto& operator=(const std::complex<U>& x) {
this->re_ = x.real();
this->im_ = x.imag();
return *this;
}
};

} // namespace std

#endif
236 changes: 236 additions & 0 deletions stan/math/prim/core/complex_base.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
#ifndef STAN_MATH_PRIM_CORE_COMPLEX_BASE_HPP
#define STAN_MATH_PRIM_CORE_COMPLEX_BASE_HPP

#include <stan/math/prim/fun/square.hpp>
#include <stan/math/prim/meta.hpp>
#include <complex>

namespace stan {
namespace math {

/**
* Base class for complex numbers. Extending classes must be of
* of the form `complex<ValueType>`.
*
* @tparam ValueType type of real and imaginary parts
*/
template <typename ValueType>
class complex_base {
public:
/**
* Type of real and imaginary parts
*/
using value_type = ValueType;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is our standard to do underscore_naming for usings and CamelCase for template parameters?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's consistent with how I understand the code but it's weird to see if directly in a situation like this :/.


/**
* Derived complex type used for function return types
*/
using complex_type = std::complex<value_type>;

/**
* Construct a complex base with zero real and imaginary parts.
*/
complex_base() = default;

/**
* Construct a complex base with the specified real part and a zero
* imaginary part.
*
* @tparam U real type (assignable to `value_type`)
* @param[in] re real part
*/
template <typename U, typename = require_stan_scalar_t<U>>
complex_base(const U& re) : re_(re) {} // NOLINT(runtime/explicit)

/**
* Construct a complex base with the specified real and imaginary
* parts.
*
* @tparam U real type (assignable to `value_type`)
* @tparam V imaginary type (assignable to `value_type`)
* @param[in] re real part
* @param[in] im imaginary part
*/
template <typename U, typename V>
complex_base(const U& re, const V& im) : re_(re), im_(im) {}

/**
* Return the real part.
*
* @return real part
*/
value_type real() const { return re_; }

/**
* Set the real part to the specified value.
*
* @param[in] re real part
*/
void real(const value_type& re) { re_ = re; }

/**
* Return the imaginary part.
*
* @return imaginary part
*/
value_type imag() const { return im_; }

/**
* Set the imaginary part to the specified value.
*
* @param[in] im imaginary part
*/
void imag(const value_type& im) { im_ = im; }

/**
* Assign the specified value to the real part of this complex number
* and set imaginary part to zero.
*
* @tparam U argument type (assignable to `value_type`)
* @param[in] re real part
* @return this
*/
template <typename U, typename = require_stan_scalar_t<U>>
complex_type& operator=(U&& re) {
re_ = re;
im_ = 0;
return derived();
}

/**
* Add specified real value to real part.
*
* @tparam U argument type (assignable to `value_type`)
* @param[in] x real number to add
* @return this
*/
template <typename U>
complex_type& operator+=(const U& x) {
re_ += x;
return derived();
}

/**
* Adds specified complex number to this.
*
* @tparam U value type of argument (assignable to `value_type`)
* @param[in] other complex number to add
* @return this
*/
template <typename U>
complex_type& operator+=(const std::complex<U>& other) {
re_ += other.real();
im_ += other.imag();
return derived();
}

/**
* Subtracts specified real number from real part.
*
* @tparam U argument type (assignable to `value_type`)
* @param[in] x real number to subtract
* @return this
*/
template <typename U>
complex_type& operator-=(const U& x) {
re_ -= x;
return derived();
}

/**
* Subtracts specified complex number from this.
*
* @tparam U value type of argument (assignable to `value_type`)
* @param[in] other complex number to subtract
* @return this
*/
template <typename U>
complex_type& operator-=(const std::complex<U>& other) {
re_ -= other.real();
im_ -= other.imag();
return derived();
}

/**
* Multiplies this by the specified real number.
*
* @tparam U type of argument (assignable to `value_type`)
* @param[in] x real number to multiply
* @return this
*/
template <typename U>
complex_type& operator*=(const U& x) {
re_ *= x;
im_ *= x;
return derived();
}

/**
* Multiplies this by specified complex number.
*
* @tparam U value type of argument (assignable to `value_type`)
* @param[in] other complex number to multiply
* @return this
*/
template <typename U>
complex_type& operator*=(const std::complex<U>& other) {
value_type re_temp = re_ * other.real() - im_ * other.imag();
im_ = re_ * other.imag() + other.real() * im_;
re_ = re_temp;
return derived();
}

/**
* Divides this by the specified real number.
*
* @tparam U type of argument (assignable to `value_type`)
* @param[in] x real number to divide by
* @return this
*/
template <typename U>
complex_type& operator/=(const U& x) {
re_ /= x;
im_ /= x;
return derived();
}

/**
* Divides this by the specified complex number.
*
* @tparam U value type of argument (assignable to `value_type`)
* @param[in] other number to divide by
* @return this
*/
template <typename U>
complex_type& operator/=(const std::complex<U>& other) {
using stan::math::square;
value_type sum_sq_im = square(other.real()) + square(other.imag());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can/should these be auto?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the feeling about auto when there's a simple type we can use? I find the explicit types simpler if they're small. I'm not bothered either way, so let me know. Our existing code is all over the place on this issue.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I personally like auto so that type deduction just rides through instead of performing an implicit cast if there is a mismatch. I'm fine either way here

value_type re_temp = (re_ * other.real() + im_ * other.imag()) / sum_sq_im;
im_ = (im_ * other.real() - re_ * other.imag()) / sum_sq_im;
re_ = re_temp;
return derived();
}

protected:
/**
* Real part
*/
value_type re_{0};

/**
* Imaginary part
*/
value_type im_{0};

/**
* Return this complex base cast to the complex type.
*
* @return this complex base cast to the complex type
*/
complex_type& derived() { return static_cast<complex_type&>(*this); }
};

} // namespace math
} // namespace stan

#endif
1 change: 1 addition & 0 deletions stan/math/prim/meta/scalar_type.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ struct scalar_type<T, std::enable_if_t<is_eigen<T>::value>> {
};

/** \ingroup type_trait
*
* Template metaprogram defining the scalar type for values
* stored in a complex number.
*
Expand Down
1 change: 1 addition & 0 deletions stan/math/rev/core.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
#include <stan/math/rev/core/set_zero_all_adjoints.hpp>
#include <stan/math/rev/core/set_zero_all_adjoints_nested.hpp>
#include <stan/math/rev/core/start_nested.hpp>
#include <stan/math/rev/core/std_complex.hpp>
#include <stan/math/rev/core/std_isinf.hpp>
#include <stan/math/rev/core/std_isnan.hpp>
#include <stan/math/rev/core/std_numeric_limits.hpp>
Expand Down
66 changes: 66 additions & 0 deletions stan/math/rev/core/std_complex.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
#ifndef STAN_MATH_REV_CORE_STD_COMPLEX_HPP
#define STAN_MATH_REV_CORE_STD_COMPLEX_HPP

#include <stan/math/prim/meta.hpp>
#include <stan/math/prim/core/complex_base.hpp>
#include <stan/math/rev/core/var.hpp>
#include <cmath>
#include <complex>

namespace std {

/**
* Specialization of the standard libary complex number type for
* reverse-mode autodiff type `stan::math::var`.
*/
template <>
class complex<stan::math::var>
: public stan::math::complex_base<stan::math::var> {
public:
using base_t = stan::math::complex_base<stan::math::var>;

/**
* Construct a complex number with zero real and imaginary parts.
*/
complex() = default;

/**
* Construct a complex number from real and imaginary parts.
*
* @tparam U type of real part (assignable to `value_type`)
* @tparam V type of imaginary part (assignable to `value_type`)
* @param[in] re real part
* @param[in] im imaginary part
*/
template <typename U, typename V>
complex(const U& re, const V& im) : base_t(re, im) {}

/**
* Construct a complex number with specified real part and zero
* imaginary part.
*
* @tparam U type of real part (assignable to `value_type`)
* @param[in] re real part
*/
template <typename U, typename = stan::require_stan_scalar_t<U>>
complex(U&& re) : base_t(re) {} // NOLINT(runtime/explicit)

/**
* Set the real and imaginary components of this complex number to
* those of the specified complex number.
*
* @tparam U value type of argument (must be an arithmetic type)
* @param[in] x complex argument
* @return this
*/
template <typename U, typename = stan::require_arithmetic_t<U>>
auto& operator=(const std::complex<U>& x) {
re_ = x.real();
im_ = x.imag();
return *this;
}
};

} // namespace std

#endif
Loading