I’m putting this up for comment now. I still need to dot a few
Is and cross a few
Ts before it’s ready to go.
This is following on from
I believe I have a complete
std::complex implementation working in
stan-math on branch
For now the code and tests are in a single file, but will have to be split out for a PR:
test/unit/math/rev/core/std_complex_test.cpp(Warning: tests take 1m with clang++ and 15m with g++)
std::complex<T> in C++
For the spec, here’s a mostly complete overview:
spec of std::complex on cppreference (it’s missing at least one integer overload of
The key issue is that
std::complex<T> being instantiated is unspecified behavior. So what I’m providing is a complete specialization so nothing relies on unspecified behavior.
For a primer on the evils of function template specializations:
- Walter E. Brown. 2017. Thou Shalt Not Specialize std Function Templates!
What’s in the branch
Overloads of missing
stan::mathfor argument-dependent lookup; these are not related to
std::complexbut necessary for some of Eigen’s algorithms; also specializes
std::complex<fvar<U>>. The constructors initialize real and imaginary values to zero where necessary (this is roughly what’s suggested in the last bullet point of @syclik’s summary on the issue).
Code duplication in the
std::complex<T>class overloads was eliminated by factoring the class overloads into a CRTP-based base class. Code dupe in functions was eliminated by defining fully templated function templates for all complex operations in
stan::mathto which all other specializations and overloads delegate. I simplified signatures as much as possible to remove ambiguities while covering the combinatorics. There are notes on how that’s done as code comments, since it won’t be obvious from just reading the code (8 overloads should be required for ADL, but the specializations need to be redundantly overloaded to work in both g++ and clang++)
std::complexfunctions where available. Most functions are not class member functions. This is necessary to get around
libstdc++'s (used in
g++) poor coding of
std::complexfor argument-dependent lookup—they hardcode
std::qualifiers on many complex functions. clang++ works without any specializations.
Overloads of all functions defined in
stan::mathfor all possible complex instantiations with autodiff variables and for all combinations of pairs of operands (
is one of our autodiff types). These will be picked up by argument-dependent lookup. They will prevent overpromotion tocomplex` or to an autodiff type.
Some supporting metaprograms to deal with return types in complex template functions; these could potentially be merged into general things like
return_type_tbut I didn’t want to extend this mega-PR beyond what was necessary for
std::complex(my plan’s to enlist @Stevo15025’s help on integrating the metaprograms into the general Stan metaprogramming framework—for now they’re just coded to work simply when at least one complex type is involved)
Complete unit tests for constructors, standalone functions, and overloads. This uses the autodiff test framework to test derivatives at all levels of autodiff:
fvar<fvar<var>>. That makes sure everything gets instantiated with both
complex<T>for autodiff type
Tand mixed with
complex<double>and all other types like
Titself. Where autodiff’s not involved, operaitons like constructors and setters/getters are tested directly for reference equality.
The final tests are borrowed from the previous PR and instantiate three solvers at all levels of autodiff to make sure they get the right result
- Eigendecomposition for asymmetric matrices
- Complex Schur decomposition
Staging a PR
I plan to break this monolithic branch down in order to create a PR in three steps.
stan::math: basic Stan infrastructure that doesn’t depend on complex overloads
std: specializations of
stan::math: template functions operating on
std: specializations of
std::complexfunctions plus namespace