From 64ec7c36f1d646e8bb6b9e0387f65c26063f8fc2 Mon Sep 17 00:00:00 2001 From: octoflar Date: Sat, 15 Jun 2024 00:11:45 +0200 Subject: [PATCH] Update: GNU code style --- src/main/cxx-filtered/config.h | 49 +- src/main/cxx/apps/azafran.cxx | 44 +- src/main/cxx/apps/curcuma.cxx | 44 +- src/main/cxx/apps/ecom.cxx | 24 +- src/main/cxx/apps/edat.cxx | 24 +- src/main/cxx/apps/elog.cxx | 24 +- src/main/cxx/apps/emes.cxx | 33 +- src/main/cxx/apps/emod.cxx | 33 +- src/main/cxx/apps/ezip.cxx | 98 +- src/main/cxx/apps/mascada.cxx | 49 +- src/main/cxx/apps/oregano.cxx | 45 +- src/main/cxx/core/base.h | 314 +++--- src/main/cxx/core/dataio.cxx | 217 ++-- src/main/cxx/core/dataio.h | 76 +- src/main/cxx/core/decompose.cxx | 372 ++++--- src/main/cxx/core/decompose.h | 378 +++---- src/main/cxx/core/deviates.h | 145 +-- src/main/cxx/core/equations.cxx | 43 +- src/main/cxx/core/equations.h | 249 +++-- src/main/cxx/core/exitcodes.h | 39 +- src/main/cxx/core/integrator.h | 1139 ++++++++++---------- src/main/cxx/core/model.h | 1272 ++++++++++++---------- src/main/cxx/core/optimize.h | 634 +++++------ src/main/cxx/core/optimizer.cxx | 174 +-- src/main/cxx/core/optimizer.h | 1417 +++++++++++++------------ src/main/cxx/core/profiles.cxx | 278 +++-- src/main/cxx/core/profiles.h | 994 ++++++++--------- src/main/cxx/core/random.h | 932 ++++++++-------- src/main/cxx/core/readline.cxx | 20 +- src/main/cxx/core/readline.h | 719 +++++++------ src/main/cxx/core/runner.cxx | 98 +- src/main/cxx/core/runner.h | 561 +++++----- src/main/cxx/core/section.cxx | 557 +++++----- src/main/cxx/core/section.h | 738 +++++++------ src/main/cxx/util/airtovac.cxx | 82 +- src/main/cxx/util/helicorr.cxx | 95 +- src/main/cxx/util/vactoair.cxx | 76 +- src/test/cxx/core/decompose_test.cxx | 476 +++++---- src/test/cxx/core/integrator_test.cxx | 201 ++-- src/test/cxx/core/optimizer_test.cxx | 553 ++++++---- src/test/cxx/core/profiles_test.cxx | 184 ++-- src/test/cxx/core/random_test.cxx | 165 +-- src/test/cxx/unittest.h | 426 ++++---- 43 files changed, 7713 insertions(+), 6378 deletions(-) diff --git a/src/main/cxx-filtered/config.h b/src/main/cxx-filtered/config.h index e216b69..4e79226 100644 --- a/src/main/cxx-filtered/config.h +++ b/src/main/cxx-filtered/config.h @@ -8,40 +8,43 @@ #include -namespace especia { +namespace especia +{ - /// The project name. - const std::string project_name = "@PROJECT_NAME@"; // NOLINT +/// The project name. +const std::string project_name = "@PROJECT_NAME@"; // NOLINT - /// The project title. - const std::string project_title = "@PROJECT_TITLE@"; // NOLINT +/// The project title. +const std::string project_title = "@PROJECT_TITLE@"; // NOLINT - /// The project version. - const std::string project_version = "@PROJECT_VERSION@"; // NOLINT +/// The project version. +const std::string project_version = "@PROJECT_VERSION@"; // NOLINT - /// The project version control tag. - const std::string project_version_tag = "@PROJECT_VERSION_TAG@"; // NOLINT +/// The project version control tag. +const std::string project_version_tag = "@PROJECT_VERSION_TAG@"; // NOLINT - /// The project digital object identifier (DOI). - const std::string project_doi = "@PROJECT_DOI@"; // NOLINT +/// The project digital object identifier (DOI). +const std::string project_doi = "@PROJECT_DOI@"; // NOLINT - /// A project digital object identifier (DOI) HTML snippet. - const std::string project_doi_html = "@PROJECT_DOI_HTML@"; // NOLINT +/// A project digital object identifier (DOI) HTML snippet. +const std::string project_doi_html = "@PROJECT_DOI_HTML@"; // NOLINT - /// The project URL. - const std::string project_url = "@PROJECT_URL@"; // NOLINT +/// The project URL. +const std::string project_url = "@PROJECT_URL@"; // NOLINT - /// The project name and version identifier. - const std::string project_long_name = "@PROJECT_NAME@-@PROJECT_VERSION@ @PROJECT_VERSION_TAG@"; // NOLINT +/// The project name and version identifier. +const std::string project_long_name + = "@PROJECT_NAME@-@PROJECT_VERSION@ @PROJECT_VERSION_TAG@"; // NOLINT - /// The composite name of the operating system the project is compiled for. - const std::string system_name = "@CMAKE_SYSTEM@"; // NOLINT +/// The composite name of the operating system the project is compiled for. +const std::string system_name = "@CMAKE_SYSTEM@"; // NOLINT - /// The vendor of the C++ compiler used to compile the project. - const std::string cxx_compiler = "@CMAKE_CXX_COMPILER_ID@"; // NOLINT +/// The vendor of the C++ compiler used to compile the project. +const std::string cxx_compiler = "@CMAKE_CXX_COMPILER_ID@"; // NOLINT - /// The version of the C++ compiler used to compile the project. - const std::string cxx_compiler_version = "@CMAKE_CXX_COMPILER_VERSION@"; // NOLINT +/// The version of the C++ compiler used to compile the project. +const std::string cxx_compiler_version + = "@CMAKE_CXX_COMPILER_VERSION@"; // NOLINT } #endif // ESPECIA_CONFIG_H diff --git a/src/main/cxx/apps/azafran.cxx b/src/main/cxx/apps/azafran.cxx index e15b719..f33b7dd 100644 --- a/src/main/cxx/apps/azafran.cxx +++ b/src/main/cxx/apps/azafran.cxx @@ -11,7 +11,6 @@ using namespace std; - /// Especia to infer the variation of the fine-structure constant. /// /// @param argc The number of command line arguments. @@ -26,23 +25,34 @@ using namespace std; /// @param argv[7] The trace modulus. /// @return an exit code /// -/// @remark Usage: azafran {seed} {parents} {population} {step} {accuracy} {stop} {trace} < {model file} [> {result file}] +/// @remark Usage: azafran {seed} {parents} {population} {step} {accuracy} +/// {stop} {trace} < {model file} [> {result file}] /// -/// @attention A usage message is written to standard output, if no command line arguments (excluding the -/// program name) are supplied. In this case the returned exit code is zero. -int main(int argc, char *argv[]) { - typedef especia::Model Model; +/// @attention A usage message is written to standard output, if no command +/// line arguments (excluding the program name) are supplied. In this case the +/// returned exit code is zero. +int +main (int argc, char *argv[]) +{ + typedef especia::Model Model; - try { - return especia::Runner(argc, argv).run(); - } catch (logic_error &e) { - cerr << e.what() << endl; - return especia::Exit_Codes::logic_error; - } catch (runtime_error &e) { - cerr << e.what() << endl; - return especia::Exit_Codes::runtime_error; - } catch (exception &e) { - cerr << e.what() << endl; - return especia::Exit_Codes::unspecific_exception; + try + { + return especia::Runner (argc, argv).run (); + } + catch (logic_error &e) + { + cerr << e.what () << endl; + return especia::Exit_Codes::logic_error; + } + catch (runtime_error &e) + { + cerr << e.what () << endl; + return especia::Exit_Codes::runtime_error; + } + catch (exception &e) + { + cerr << e.what () << endl; + return especia::Exit_Codes::unspecific_exception; } } diff --git a/src/main/cxx/apps/curcuma.cxx b/src/main/cxx/apps/curcuma.cxx index 6ae12f3..033e0a3 100644 --- a/src/main/cxx/apps/curcuma.cxx +++ b/src/main/cxx/apps/curcuma.cxx @@ -11,7 +11,6 @@ using namespace std; - /// Especia to analyse intergalactic metal lines. /// /// @param argc The number of command line arguments. @@ -25,23 +24,34 @@ using namespace std; /// @param argv[7] The trace modulus. /// @return an exit code /// -/// @remark Usage: curcuma {seed} {parents} {population} {step} {accuracy} {stop} {trace} < {model file} [> {result file}] +/// @remark Usage: curcuma {seed} {parents} {population} {step} {accuracy} +/// {stop} {trace} < {model file} [> {result file}] /// -/// @attention A usage message is written to standard output, if no command line arguments (excluding the -/// program name) are supplied. In this case the returned exit code is zero. -int main(int argc, char *argv[]) { - typedef especia::Model Model; +/// @attention A usage message is written to standard output, if no command +/// line arguments (excluding the program name) are supplied. In this case the +/// returned exit code is zero. +int +main (int argc, char *argv[]) +{ + typedef especia::Model Model; - try { - return especia::Runner(argc, argv).run(); - } catch (logic_error &e) { - cerr << e.what() << endl; - return especia::Exit_Codes::logic_error; - } catch (runtime_error &e) { - cerr << e.what() << endl; - return especia::Exit_Codes::runtime_error; - } catch (exception &e) { - cerr << e.what() << endl; - return especia::Exit_Codes::unspecific_exception; + try + { + return especia::Runner (argc, argv).run (); + } + catch (logic_error &e) + { + cerr << e.what () << endl; + return especia::Exit_Codes::logic_error; + } + catch (runtime_error &e) + { + cerr << e.what () << endl; + return especia::Exit_Codes::runtime_error; + } + catch (exception &e) + { + cerr << e.what () << endl; + return especia::Exit_Codes::unspecific_exception; } } diff --git a/src/main/cxx/apps/ecom.cxx b/src/main/cxx/apps/ecom.cxx index a2ae889..1c97ffb 100644 --- a/src/main/cxx/apps/ecom.cxx +++ b/src/main/cxx/apps/ecom.cxx @@ -13,16 +13,22 @@ using namespace std; /// @return an exit code. /// /// @remark Usage: ecom < {result file} [> {target file}] -int main() { - bool found = false; - string s; +int +main () +{ + bool found = false; + string s; - while (getline(cin, s)) { - if (found and s != "") { - cout << s << endl; - } else { - found = (s == ""); + while (getline (cin, s)) + { + if (found and s != "") + { + cout << s << endl; + } + else + { + found = (s == ""); } } - return 0; + return 0; } diff --git a/src/main/cxx/apps/edat.cxx b/src/main/cxx/apps/edat.cxx index 1eca578..e67974e 100644 --- a/src/main/cxx/apps/edat.cxx +++ b/src/main/cxx/apps/edat.cxx @@ -13,17 +13,23 @@ using namespace std; /// @return an exit code. /// /// @remark Usage: edat < {result file} [> {target file}] -int main() { - bool found = false; - string s; +int +main () +{ + bool found = false; + string s; - while (getline(cin, s)) { - if (found and s != "") { - cout << s << endl; - } else { - found = (s == ""); + while (getline (cin, s)) + { + if (found and s != "") + { + cout << s << endl; + } + else + { + found = (s == ""); } } - return 0; + return 0; } diff --git a/src/main/cxx/apps/elog.cxx b/src/main/cxx/apps/elog.cxx index 5a04d0e..574c162 100644 --- a/src/main/cxx/apps/elog.cxx +++ b/src/main/cxx/apps/elog.cxx @@ -13,17 +13,23 @@ using namespace std; /// @return an exit code. /// /// @remark Usage: elog < {result file} [> {target file}] -int main() { - bool found = false; - string s; +int +main () +{ + bool found = false; + string s; - while (getline(cin, s)) { - if (found and s != "") { - cout << s << endl; - } else { - found = (s == ""); + while (getline (cin, s)) + { + if (found and s != "") + { + cout << s << endl; + } + else + { + found = (s == ""); } } - return 0; + return 0; } diff --git a/src/main/cxx/apps/emes.cxx b/src/main/cxx/apps/emes.cxx index 283de64..f846af5 100644 --- a/src/main/cxx/apps/emes.cxx +++ b/src/main/cxx/apps/emes.cxx @@ -13,21 +13,28 @@ using namespace std; /// @return an exit code. /// /// @remark Usage: emes < {result file} [> {target file}] -int main() { - bool found = false; - string s; +int +main () +{ + bool found = false; + string s; - while (getline(cin, s)) { - if (found and not (s == "" - // for compatibility with former versions - or s == "")) { - cout << s << endl; - } else { - found = (s == "" - // for compatibility with former versions - or s == ""); + while (getline (cin, s)) + { + if (found + and not(s == "" + // for compatibility with former versions + or s == "")) + { + cout << s << endl; + } + else + { + found = (s == "" + // for compatibility with former versions + or s == ""); } } - return 0; + return 0; } diff --git a/src/main/cxx/apps/emod.cxx b/src/main/cxx/apps/emod.cxx index 45b2889..f262f39 100644 --- a/src/main/cxx/apps/emod.cxx +++ b/src/main/cxx/apps/emod.cxx @@ -13,21 +13,28 @@ using namespace std; /// @return an exit code. /// /// @remark Usage: emod < {result file} [> {target file}] -int main() { - bool found = false; - string s; +int +main () +{ + bool found = false; + string s; - while (getline(cin, s)) { - if (found and not (s == "" - // for compatibility with former versions - or s == "" or s == "")) { - cout << s << endl; - } else { - found = (s == "" - // for compatibility with former versions - or s == "" or s == ""); + while (getline (cin, s)) + { + if (found + and not(s == "" + // for compatibility with former versions + or s == "" or s == "")) + { + cout << s << endl; + } + else + { + found = (s == "" + // for compatibility with former versions + or s == "" or s == ""); } } - return 0; + return 0; } diff --git a/src/main/cxx/apps/ezip.cxx b/src/main/cxx/apps/ezip.cxx index 010fee7..2f3ff5a 100644 --- a/src/main/cxx/apps/ezip.cxx +++ b/src/main/cxx/apps/ezip.cxx @@ -18,8 +18,12 @@ using especia::real; /// /// @param os The output stream. /// @param program_name The program name. -void write_usage_message(ostream &os, const string &program_name) { - os << "usage: " << program_name << " {flux file} {uncertainty file} [lines to skip] [> {target file}]" << endl; +void +write_usage_message (ostream &os, const string &program_name) +{ + os << "usage: " << program_name + << " {flux file} {uncertainty file} [lines to skip] [> {target file}]" + << endl; } /// Utility to merge separate spectral flux and uncertainty data files. @@ -33,56 +37,74 @@ void write_usage_message(ostream &os, const string &program_name) { /// /// @c argv[2] The path name of the flux uncertainty data file. /// -/// @c argv[3] The number of lines to skip at the beginning (optional, default = 0). +/// @c argv[3] The number of lines to skip at the beginning (optional, default +/// = 0). /// @endparblock /// @return an exit code. /// -/// @remark Usage: ezip {flux file} {uncertainty file} [lines to skip] [> {target file}] -int main(int argc, char *argv[]) { - const string program_name(argv[0]); +/// @remark Usage: ezip {flux file} {uncertainty file} [lines to skip] [> +/// {target file}] +int +main (int argc, char *argv[]) +{ + const string program_name (argv[0]); - if (argc == 1) { - write_usage_message(cout, program_name); - return 0; + if (argc == 1) + { + write_usage_message (cout, program_name); + return 0; } - try { - if (argc != 3 and argc != 4) { - throw invalid_argument("Error: an invalid number of arguments was supplied"); + try + { + if (argc != 3 and argc != 4) + { + throw invalid_argument ( + "Error: an invalid number of arguments was supplied"); } - natural skip = 0; + natural skip = 0; - if (argc == 4) { - skip = especia::convert(string(argv[2])); + if (argc == 4) + { + skip = especia::convert (string (argv[2])); } - valarray x; - valarray y; - valarray z; + valarray x; + valarray y; + valarray z; - ifstream fxy(argv[1]); - ifstream fxz(argv[2]); + ifstream fxy (argv[1]); + ifstream fxz (argv[2]); - especia::get(fxy, x, y, skip); - especia::get(fxz, x, z, skip); + especia::get (fxy, x, y, skip); + especia::get (fxz, x, z, skip); - fxy.close(); - fxz.close(); + fxy.close (); + fxz.close (); - if (fxy and fxz and y.size() == z.size()) { - especia::put(cout, x, y, z); - } else { - throw runtime_error("Error: an input error occurred"); + if (fxy and fxz and y.size () == z.size ()) + { + especia::put (cout, x, y, z); } - return 0; - } catch (logic_error &e) { - cerr << e.what() << endl; - return especia::Exit_Codes::logic_error; - } catch (runtime_error &e) { - cerr << e.what() << endl; - return especia::Exit_Codes::runtime_error; - } catch (exception &e) { - cerr << e.what() << endl; - return especia::Exit_Codes::unspecific_exception; + else + { + throw runtime_error ("Error: an input error occurred"); + } + return 0; + } + catch (logic_error &e) + { + cerr << e.what () << endl; + return especia::Exit_Codes::logic_error; + } + catch (runtime_error &e) + { + cerr << e.what () << endl; + return especia::Exit_Codes::runtime_error; + } + catch (exception &e) + { + cerr << e.what () << endl; + return especia::Exit_Codes::unspecific_exception; } } diff --git a/src/main/cxx/apps/mascada.cxx b/src/main/cxx/apps/mascada.cxx index 0741604..2c5cbb2 100644 --- a/src/main/cxx/apps/mascada.cxx +++ b/src/main/cxx/apps/mascada.cxx @@ -1,5 +1,6 @@ /// @file mascada.cxx -/// Especia for intergalactic metal and damped H I, He I, II lines (high accuracy). +/// Especia for intergalactic metal and damped H I, He I, II lines (high +/// accuracy). /// @author Ralf Quast /// @date 2023 /// @copyright MIT License @@ -11,7 +12,6 @@ using namespace std; - /// Especia to analyse intergalactic metal and damped H I, He I, II lines /// with high accuracy. /// @@ -27,23 +27,36 @@ using namespace std; /// @param argv[7] The trace modulus. /// @return an exit code /// -/// @remark Usage: mascada {seed} {parents} {population} {step} {accuracy} {stop} {trace} < {model file} [> {result file}] +/// @remark Usage: mascada {seed} {parents} {population} {step} {accuracy} +/// {stop} {trace} < {model file} [> {result file}] /// -/// @attention A usage message is written to standard output, if no command line arguments (excluding the -/// program name) are supplied. In this case the returned exit code is zero. -int main(int argc, char *argv[]) { - typedef especia::Model> Model; +/// @attention A usage message is written to standard output, if no command +/// line arguments (excluding the program name) are supplied. In this case the +/// returned exit code is zero. +int +main (int argc, char *argv[]) +{ + typedef especia::Model< + especia::Intergalactic_Voigt > + Model; - try { - return especia::Runner(argc, argv).run(); - } catch (logic_error &e) { - cerr << e.what() << endl; - return especia::Exit_Codes::logic_error; - } catch (runtime_error &e) { - cerr << e.what() << endl; - return especia::Exit_Codes::runtime_error; - } catch (exception &e) { - cerr << e.what() << endl; - return especia::Exit_Codes::unspecific_exception; + try + { + return especia::Runner (argc, argv).run (); + } + catch (logic_error &e) + { + cerr << e.what () << endl; + return especia::Exit_Codes::logic_error; + } + catch (runtime_error &e) + { + cerr << e.what () << endl; + return especia::Exit_Codes::runtime_error; + } + catch (exception &e) + { + cerr << e.what () << endl; + return especia::Exit_Codes::unspecific_exception; } } diff --git a/src/main/cxx/apps/oregano.cxx b/src/main/cxx/apps/oregano.cxx index 2964816..2c9ded1 100644 --- a/src/main/cxx/apps/oregano.cxx +++ b/src/main/cxx/apps/oregano.cxx @@ -11,7 +11,6 @@ using namespace std; - /// Especia to analyse intergalactic metal and damped H I, He I, II lines. /// /// @param argc The number of command line arguments. @@ -26,23 +25,35 @@ using namespace std; /// @param argv[7] The trace modulus. /// @return an exit code /// -/// @remark Usage: oregano {seed} {parents} {population} {step} {accuracy} {stop} {trace} < {model file} [> {result file}] +/// @remark Usage: oregano {seed} {parents} {population} {step} {accuracy} +/// {stop} {trace} < {model file} [> {result file}] /// -/// @attention A usage message is written to standard output, if no command line arguments (excluding the -/// program name) are supplied. In this case the returned exit code is zero. -int main(int argc, char *argv[]) { - typedef especia::Model> Model; +/// @attention A usage message is written to standard output, if no command +/// line arguments (excluding the program name) are supplied. In this case the +/// returned exit code is zero. +int +main (int argc, char *argv[]) +{ + typedef especia::Model > + Model; - try { - return especia::Runner(argc, argv).run(); - } catch (logic_error &e) { - cerr << e.what() << endl; - return especia::Exit_Codes::logic_error; - } catch (runtime_error &e) { - cerr << e.what() << endl; - return especia::Exit_Codes::runtime_error; - } catch (exception &e) { - cerr << e.what() << endl; - return especia::Exit_Codes::unspecific_exception; + try + { + return especia::Runner (argc, argv).run (); + } + catch (logic_error &e) + { + cerr << e.what () << endl; + return especia::Exit_Codes::logic_error; + } + catch (runtime_error &e) + { + cerr << e.what () << endl; + return especia::Exit_Codes::runtime_error; + } + catch (exception &e) + { + cerr << e.what () << endl; + return especia::Exit_Codes::unspecific_exception; } } diff --git a/src/main/cxx/core/base.h b/src/main/cxx/core/base.h index 26a5407..4eff1b5 100644 --- a/src/main/cxx/core/base.h +++ b/src/main/cxx/core/base.h @@ -13,156 +13,186 @@ #include #include -namespace especia { - - /// The type of integer numbers including zero (denoted in maths as set Z). - typedef int integer; - - /// The type of natural numbers including zero (denoted in maths as set N). - typedef unsigned natural; - - /// The type of real numbers (denoted in maths as set R). - typedef double real; - - /// The type of binary numbers with 32 binary digits. - typedef uint32_t word32; - - /// The type of binary numbers with 64 binary digits. - typedef uint64_t word64; - - /// The class of continuous univariate functions @c f(x) whose derivative exists and is continous. - template - class C1 { - public: - /// The first argument is the abscissa @c x, the second argument returns the value of @c f(x), - /// and the third argument returns the value of the first derivative @c f'(x). - typedef void (&type)(const T &, T &, T &); - - private: - /// The private constructor prevents instantiation. - C1() = default; - }; - - /// Pi. - const real pi = real(3.141592653589793238462643383279502884197169399375L); - - /// The square root of Pi. - const real sqrt_of_pi = real(1.772453850905516027298167483341145182797549456123L); - - /// The square root of the natural logarithm of 2. - const real sqrt_of_ln_two = real(0.832554611157697756353164644895201047630588852264L); - - /// The electric constant (F m-1). *NIST SP 961 (Sept/2015)* - const real electric_constant = real(8.854187817E-12); - - /// The electron mass (kg). *NIST SP 961 (Sept/2015)* - const real electron_mass = real(9.10938356E-31); - - /// The elementary charge (C). *NIST SP 961 (Sept/2015)* - const real elementary_charge = real(1.6021766208E-19); - - /// SI prefix. The spectral resolution of an instrument is expressed in units of this number. - const real kilo = real(1.0E+03); - - /// SI prefix. - const real milli = real(1.0E-03); - - /// SI prefix. The variation of the fine-structure constant is expressed in units of this number. - const real micro = real(1.0E-06); +namespace especia +{ + +/// The type of integer numbers including zero (denoted in maths as set Z). +typedef int integer; + +/// The type of natural numbers including zero (denoted in maths as set N). +typedef unsigned natural; + +/// The type of real numbers (denoted in maths as set R). +typedef double real; + +/// The type of binary numbers with 32 binary digits. +typedef uint32_t word32; + +/// The type of binary numbers with 64 binary digits. +typedef uint64_t word64; + +/// The class of continuous univariate functions @c f(x) whose derivative +/// exists and is continous. +template class C1 +{ +public: + /// The first argument is the abscissa @c x, the second argument returns the + /// value of @c f(x), and the third argument returns the value of the first + /// derivative @c f'(x). + typedef void (&type) (const T &, T &, T &); + +private: + /// The private constructor prevents instantiation. + C1 () = default; +}; + +/// Pi. +const real pi = real (3.141592653589793238462643383279502884197169399375L); + +/// The square root of Pi. +/// +const real sqrt_of_pi + = real (1.772453850905516027298167483341145182797549456123L); + +/// The square root of the natural logarithm of 2. +/// +const real sqrt_of_ln_two + = real (0.832554611157697756353164644895201047630588852264L); + +/// The electric constant (F m-1). *NIST SP 961 (Sept/2015)* +const real electric_constant = real (8.854187817E-12); + +/// The electron mass (kg). *NIST SP 961 (Sept/2015)* +const real electron_mass = real (9.10938356E-31); + +/// The elementary charge (C). *NIST SP 961 (Sept/2015)* +const real elementary_charge = real (1.6021766208E-19); + +/// SI prefix. The spectral resolution of an instrument is expressed in units +/// of this number. +const real kilo = real (1.0E+03); + +/// SI prefix. +const real milli = real (1.0E-03); + +/// SI prefix. The variation of the fine-structure constant is expressed in +/// units of this number. +const real micro = real (1.0E-06); + +/// The speed of light in vacuum (m s-1). *NIST SP 961 (Sept/2015)* +const real speed_of_light = real (299792458.0); + +/// Converts a numeric character string into a number. +/// +/// @tparam T The number type. +/// +/// @param s The string. +/// @return the number. +/// +/// @throw invalid_argument when the string cannot be converted into a number +/// of requested type. +template +T +convert (const std::string &s) +{ + using std::invalid_argument; + using std::istringstream; + + istringstream iss (s); + T t; + + if (iss >> t) + { + return t; + } - /// The speed of light in vacuum (m s-1). *NIST SP 961 (Sept/2015)* - const real speed_of_light = real(299792458.0); + throw invalid_argument ("especia::convert(): Error: the expression '" + s + + "' cannot be converted into a number"); +} - /// Converts a numeric character string into a number. - /// - /// @tparam T The number type. - /// - /// @param s The string. - /// @return the number. - /// - /// @throw invalid_argument when the string cannot be converted into a number of requested type. - template - T convert(const std::string &s) { - using std::invalid_argument; - using std::istringstream; +/// Returns the L-2 norm of a vector. +/// +/// @tparam T The number type. +/// +/// @param[in] n The dimension of the vector. +/// @param[in] x The vector. +/// @return the L-2 norm of the vector. +template +T +norm (natural n, const T x[]) +{ + using std::inner_product; + using std::sqrt; + + return sqrt (inner_product (x, x + n, x, T (0.0))); +} - istringstream iss(s); - T t; +/// Returns the photon redshift as a function of relative radial velocity +/// between observer and emitter. +/// +/// @param[in] v The relative radial velocity between observer and emitter (m +/// s-1). +/// @return the photon redshift. +template +T +redshift (const T &v) +{ + return std::sqrt ((T (1.0) + v / T (speed_of_light)) + / (T (1.0) - v / T (speed_of_light))) + - T (1.0); +} - if (iss >> t) { - return t; +/// Solves the equation f(x) = c by means of Newton's method. +/// +/// @tparam T The number type. +/// +/// @param[in] f The function. +/// @param[in] c The constant on the right-hand side of the equation. +/// @param[in] x The initial guess of the solution. +/// @param[in] accuracy_goal The accuracy goal. +/// @param[in] max_iteration The maximum number of iterations (optional). +/// +/// @return the solution to the equation f(x) = c. +/// +/// @throw runtime_error when the accuracy goal was not reached within the +/// prescribed number of iterations. +template +T +solve (typename C1::type &f, T c, T x, T accuracy_goal, + natural max_iteration = 100) +{ + using std::abs; + using std::runtime_error; + + T d, y, z; + + for (natural i = 0; i < max_iteration; ++i) + { + f (x, y, z); + d = (y - c) / z; + x -= d; + if (abs (d) < accuracy_goal * x) + { + return x; } - - throw invalid_argument( - "especia::convert(): Error: the expression '" + s + "' cannot be converted into a number"); - } - - /// Returns the L-2 norm of a vector. - /// - /// @tparam T The number type. - /// - /// @param[in] n The dimension of the vector. - /// @param[in] x The vector. - /// @return the L-2 norm of the vector. - template - T norm(natural n, const T x[]) { - using std::inner_product; - using std::sqrt; - - return sqrt(inner_product(x, x + n, x, T(0.0))); } - /// Returns the photon redshift as a function of relative radial velocity between observer and emitter. - /// - /// @param[in] v The relative radial velocity between observer and emitter (m s-1). - /// @return the photon redshift. - template - T redshift(const T &v) { - return std::sqrt((T(1.0) + v / T(speed_of_light)) / (T(1.0) - v / T(speed_of_light))) - T(1.0); - } - - /// Solves the equation f(x) = c by means of Newton's method. - /// - /// @tparam T The number type. - /// - /// @param[in] f The function. - /// @param[in] c The constant on the right-hand side of the equation. - /// @param[in] x The initial guess of the solution. - /// @param[in] accuracy_goal The accuracy goal. - /// @param[in] max_iteration The maximum number of iterations (optional). - /// - /// @return the solution to the equation f(x) = c. - /// - /// @throw runtime_error when the accuracy goal was not reached within the prescribed number of iterations. - template - T solve(typename C1::type &f, T c, T x, T accuracy_goal, natural max_iteration = 100) { - using std::abs; - using std::runtime_error; - - T d, y, z; - - for (natural i = 0; i < max_iteration; ++i) { - f(x, y, z); - d = (y - c) / z; - x -= d; - if (abs(d) < accuracy_goal * x) { - return x; - } - } - - throw runtime_error("especia::solve() Error: the required accuracy goal was not reached"); - } + throw runtime_error ( + "especia::solve() Error: the required accuracy goal was not reached"); +} - /// Returns the square of a number. - /// - /// @tparam T The number type. - /// - /// @param[in] x The number. - /// @return the square of the number. - template - T sq(const T &x) { - return x * x; - } +/// Returns the square of a number. +/// +/// @tparam T The number type. +/// +/// @param[in] x The number. +/// @return the square of the number. +template +T +sq (const T &x) +{ + return x * x; +} } diff --git a/src/main/cxx/core/dataio.cxx b/src/main/cxx/core/dataio.cxx index 98aaebd..f5f4276 100644 --- a/src/main/cxx/core/dataio.cxx +++ b/src/main/cxx/core/dataio.cxx @@ -12,130 +12,161 @@ using namespace std; -istream &especia::get(istream &is, valarray &x, valarray &y, natural skip) { - const size_t room = 20000; +istream & +especia::get (istream &is, valarray &x, valarray &y, natural skip) +{ + const size_t room = 20000; - vector u; - vector v; + vector u; + vector v; - u.reserve(room); - v.reserve(room); + u.reserve (room); + v.reserve (room); - size_t n = 0; - string s; + size_t n = 0; + string s; - while (getline(is, s)) { - if (skip <= 0) { - istringstream ist(s); - real a, b; + while (getline (is, s)) + { + if (skip <= 0) + { + istringstream ist (s); + real a, b; - if (ist >> a >> b) { - u.push_back(a); - v.push_back(b); + if (ist >> a >> b) + { + u.push_back (a); + v.push_back (b); - ++n; - } else { - is.setstate(ios_base::badbit | ios_base::failbit); + ++n; + } + else + { + is.setstate (ios_base::badbit | ios_base::failbit); - return is; + return is; } - } else { - --skip; + } + else + { + --skip; } } - if (n > 0 and is.eof()) { - x.resize(n); - y.resize(n); + if (n > 0 and is.eof ()) + { + x.resize (n); + y.resize (n); - copy(u.begin(), u.end(), &x[0]); - copy(v.begin(), v.end(), &y[0]); + copy (u.begin (), u.end (), &x[0]); + copy (v.begin (), v.end (), &y[0]); - is.clear(is.rdstate() & ~ios_base::failbit); - } else { - is.setstate(ios_base::failbit); + is.clear (is.rdstate () & ~ios_base::failbit); + } + else + { + is.setstate (ios_base::failbit); } - return is; + return is; } -istream &especia::get(istream &is, valarray &x, valarray &y, valarray &z, natural skip) { - const size_t room = 20000; - - vector u; - vector v; - vector w; - - u.reserve(room); - v.reserve(room); - w.reserve(room); - - size_t n = 0; - string s; - - while (getline(is, s)) { - if (skip <= 0) { - istringstream ist(s); - real a, b, c; - - if (ist >> a >> b) { - u.push_back(a); - v.push_back(b); - if (ist >> c) - w.push_back(c); - - ++n; - } else { - is.setstate(ios_base::badbit | ios_base::failbit); +istream & +especia::get (istream &is, valarray &x, valarray &y, + valarray &z, natural skip) +{ + const size_t room = 20000; + + vector u; + vector v; + vector w; + + u.reserve (room); + v.reserve (room); + w.reserve (room); + + size_t n = 0; + string s; + + while (getline (is, s)) + { + if (skip <= 0) + { + istringstream ist (s); + real a, b, c; + + if (ist >> a >> b) + { + u.push_back (a); + v.push_back (b); + if (ist >> c) + w.push_back (c); + + ++n; + } + else + { + is.setstate (ios_base::badbit | ios_base::failbit); - return is; + return is; } - } else { - --skip; + } + else + { + --skip; } } - if (n > 0 and is.eof()) { - x.resize(n); - y.resize(n); - z.resize(n); + if (n > 0 and is.eof ()) + { + x.resize (n); + y.resize (n); + z.resize (n); - copy(u.begin(), u.end(), &x[0]); - copy(v.begin(), v.end(), &y[0]); - copy(w.begin(), w.end(), &z[0]); + copy (u.begin (), u.end (), &x[0]); + copy (v.begin (), v.end (), &y[0]); + copy (w.begin (), w.end (), &z[0]); - is.clear(is.rdstate() & ~ios_base::failbit); - } else { - is.setstate(ios_base::failbit); + is.clear (is.rdstate () & ~ios_base::failbit); + } + else + { + is.setstate (ios_base::failbit); } - return is; + return is; } -ostream &especia::put(ostream &os, const valarray &x, const valarray &y, const valarray &z) { - if (os) { - const natural p = 6; // precision - const natural w = 14; // width - - const ios_base::fmtflags f = os.flags(); - - os.setf(ios_base::right, ios_base::adjustfield); - os.precision(p); - - for (size_t i = 0; i < x.size(); ++i) { - os.setf(ios_base::fixed, ios_base::floatfield); - os << setw(w) << x[i]; - os.setf(ios_base::scientific, ios_base::floatfield); - os << setw(w) << y[i]; - if (z.size() > 0) { - os << setw(w) << z[i]; +ostream & +especia::put (ostream &os, const valarray &x, const valarray &y, + const valarray &z) +{ + if (os) + { + const natural p = 6; // precision + const natural w = 14; // width + + const ios_base::fmtflags f = os.flags (); + + os.setf (ios_base::right, ios_base::adjustfield); + os.precision (p); + + for (size_t i = 0; i < x.size (); ++i) + { + os.setf (ios_base::fixed, ios_base::floatfield); + os << setw (w) << x[i]; + os.setf (ios_base::scientific, ios_base::floatfield); + os << setw (w) << y[i]; + if (z.size () > 0) + { + os << setw (w) << z[i]; } - os << '\n'; + os << '\n'; } - os.flush(); - os.flags(f); + os.flush (); + os.flags (f); } - return os; + return os; } diff --git a/src/main/cxx/core/dataio.h b/src/main/cxx/core/dataio.h index c2bf2d7..ad13c86 100644 --- a/src/main/cxx/core/dataio.h +++ b/src/main/cxx/core/dataio.h @@ -11,43 +11,45 @@ #include "base.h" -namespace especia { - - /// Reads spectroscopic data from an input stream (two-column format). - /// - /// @param[in,out] is The input stream. - /// @param[out] x The wavelength data (previous content is overwritten). - /// @param[out] y The spectral flux or uncertainty data (previous content is overwritten). - /// @param[in] skip The number of leading lines to skip (optional). - /// - /// @return the input stream. - std::istream & - get(std::istream &is, std::valarray &x, std::valarray &y, natural skip = 0); - - /// Reads spectroscopic data from an input stream. - /// - /// @param[in,out] is The input stream. - /// @param[out] x The wavelength data (previous content is overwritten). - /// @param[out] y The spectral flux data (previous content is overwritten). - /// @param[out] z The spectral flux uncertainty data (previous content is overwritten). - /// @param[in] skip The number of leading lines to skip (optional). - /// - /// @return the input stream. - std::istream & - get(std::istream &is, std::valarray &x, std::valarray &y, - std::valarray &z, natural skip = 0); - - /// Writes spectroscopic data to an output stream. - /// - /// @param[in,out] os The output stream. - /// @param[in] x The wavelength data. - /// @param[in] y The spectral flux data. - /// @param[in] z The spectral flux uncertainty data. - /// - /// @return the output stream. - std::ostream & - put(std::ostream &os, const std::valarray &x, const std::valarray &y, - const std::valarray &z); +namespace especia +{ + +/// Reads spectroscopic data from an input stream (two-column format). +/// +/// @param[in,out] is The input stream. +/// @param[out] x The wavelength data (previous content is overwritten). +/// @param[out] y The spectral flux or uncertainty data (previous content is +/// overwritten). +/// @param[in] skip The number of leading lines to skip (optional). +/// +/// @return the input stream. +std::istream &get (std::istream &is, std::valarray &x, + std::valarray &y, natural skip = 0); + +/// Reads spectroscopic data from an input stream. +/// +/// @param[in,out] is The input stream. +/// @param[out] x The wavelength data (previous content is overwritten). +/// @param[out] y The spectral flux data (previous content is overwritten). +/// @param[out] z The spectral flux uncertainty data (previous content is +/// overwritten). +/// @param[in] skip The number of leading lines to skip (optional). +/// +/// @return the input stream. +std::istream &get (std::istream &is, std::valarray &x, + std::valarray &y, std::valarray &z, + natural skip = 0); + +/// Writes spectroscopic data to an output stream. +/// +/// @param[in,out] os The output stream. +/// @param[in] x The wavelength data. +/// @param[in] y The spectral flux data. +/// @param[in] z The spectral flux uncertainty data. +/// +/// @return the output stream. +std::ostream &put (std::ostream &os, const std::valarray &x, + const std::valarray &y, const std::valarray &z); } diff --git a/src/main/cxx/core/decompose.cxx b/src/main/cxx/core/decompose.cxx index e9d885d..fd3be1a 100644 --- a/src/main/cxx/core/decompose.cxx +++ b/src/main/cxx/core/decompose.cxx @@ -15,81 +15,48 @@ using std::string; using std::swap; using std::valarray; -using especia::real; using especia::integer; - +using especia::real; #define LAPACK_NAME_DOUBLE(x) d##x##_ #define LAPACK_NAME_SINGLE(x) s##x##_ -#define LAPACK_NAME_R_TYPE(x) LAPACK_NAME_DOUBLE(x) - -extern "C" { -/// Interface to LAPACK routine @c [DS]LAMCH. -real LAPACK_NAME_R_TYPE(lamch)(const char &cmach); - -/// Interface to LAPACK routine @c [DS]SYEVD. -void LAPACK_NAME_R_TYPE(syevd)(const char &job, - const char &uplo, - const integer &n, - real A[], - const integer &lda, - real w[], - real work[], - const integer &lwork, - integer iwork[], - const integer &liwork, - integer &info); - -/// Interface to LAPACK routine @c [DS]SYEVR. -void LAPACK_NAME_R_TYPE(syevr)(const char &job, - const char &range, - const char &uplo, - const integer &n, - real A[], - const integer &lda, - const real &vl, - const real &vu, - const integer &il, - const integer &iu, - const real &abstol, - integer &m, - real w[], - real Z[], - const integer &ldz, - integer isupp[], - real work[], - const integer &lwork, - integer iwork[], - const integer &liwork, - integer &info); - -/// Interface to LAPACK routine @c [DS]SYEVX. -void LAPACK_NAME_R_TYPE(syevx)(const char &job, - const char &range, - const char &uplo, - const integer &n, - real A[], - const integer &lda, - const real &vl, - const real &vu, - const integer &il, - const integer &iu, - const real &abstol, - integer &m, - real w[], - real Z[], - const integer &ldz, - real work[], - const integer &lwork, - integer iwork[], - integer ifail[], - integer &info); +#define LAPACK_NAME_R_TYPE(x) LAPACK_NAME_DOUBLE (x) + +extern "C" +{ + /// Interface to LAPACK routine @c [DS]LAMCH. + real LAPACK_NAME_R_TYPE (lamch) (const char &cmach); + + /// Interface to LAPACK routine @c [DS]SYEVD. + void LAPACK_NAME_R_TYPE (syevd) (const char &job, const char &uplo, + const integer &n, real A[], + const integer &lda, real w[], real work[], + const integer &lwork, integer iwork[], + const integer &liwork, integer &info); + + /// Interface to LAPACK routine @c [DS]SYEVR. + void LAPACK_NAME_R_TYPE (syevr) ( + const char &job, const char &range, const char &uplo, const integer &n, + real A[], const integer &lda, const real &vl, const real &vu, + const integer &il, const integer &iu, const real &abstol, integer &m, + real w[], real Z[], const integer &ldz, integer isupp[], real work[], + const integer &lwork, integer iwork[], const integer &liwork, + integer &info); + + /// Interface to LAPACK routine @c [DS]SYEVX. + void LAPACK_NAME_R_TYPE (syevx) ( + const char &job, const char &range, const char &uplo, const integer &n, + real A[], const integer &lda, const real &vl, const real &vu, + const integer &il, const integer &iu, const real &abstol, integer &m, + real w[], real Z[], const integer &ldz, real work[], + const integer &lwork, integer iwork[], integer ifail[], integer &info); } /// The LAPACK job parameter (here: compute eigenvalues and eigenvectors). static const char job = 'V'; -/// The LAPACK range parameter (here: compute all eigenvalues and eigenvectors). +/// The LAPACK range parameter (here: compute all eigenvalues and +/// eigenvectors). static const char range = 'A'; /// The LAPACK matrix store parameter (here: use the upper triangular part) @@ -107,169 +74,226 @@ static const integer il = 0; /// The LAPACK upper range index (here: not used). static const integer iu = 0; +especia::D_Decompose::D_Decompose (natural m) + : n (integer (m)), work (), iwork () +{ + lapack_inquire (n, lwork, liwork); -especia::D_Decompose::D_Decompose(natural m) : n(integer(m)), work(), iwork() { - lapack_inquire(n, lwork, liwork); - - work.resize(static_cast(lwork)); - iwork.resize(static_cast(liwork)); + work.resize (static_cast (lwork)); + iwork.resize (static_cast (liwork)); } -especia::D_Decompose::~D_Decompose() = default; +especia::D_Decompose::~D_Decompose () = default; -void especia::D_Decompose::operator()(const real A[], real Z[], real w[]) const { - copy(&A[0], &A[n * n], Z); +void +especia::D_Decompose::operator() (const real A[], real Z[], real w[]) const +{ + copy (&A[0], &A[n * n], Z); - lapack_do(Z, w); + lapack_do (Z, w); } -void especia::D_Decompose::lapack_do(real Z[], real w[]) const { - integer info = 0; +void +especia::D_Decompose::lapack_do (real Z[], real w[]) const +{ + integer info = 0; - LAPACK_NAME_R_TYPE(syevd)(job, uplo, n, &Z[0], n, w, &work[0], lwork, &iwork[0], liwork, info); + LAPACK_NAME_R_TYPE (syevd) + (job, uplo, n, &Z[0], n, w, &work[0], lwork, &iwork[0], liwork, info); - if (info == 0) { - // ok - } else if (info > 0) { - throw runtime_error(message_int_err); - } else { - throw invalid_argument(message_ill_arg); + if (info == 0) + { + // ok + } + else if (info > 0) + { + throw runtime_error (message_int_err); + } + else + { + throw invalid_argument (message_ill_arg); } } -void especia::D_Decompose::lapack_inquire(integer n, integer &lwork, integer &liwork) { - integer info; - real work; +void +especia::D_Decompose::lapack_inquire (integer n, integer &lwork, + integer &liwork) +{ + integer info; + real work; - LAPACK_NAME_R_TYPE(syevd)(job, uplo, n, nullptr, n, nullptr, &work, -1, &liwork, -1, info); + LAPACK_NAME_R_TYPE (syevd) + (job, uplo, n, nullptr, n, nullptr, &work, -1, &liwork, -1, info); - if (info == 0) { - lwork = static_cast(work); - } else if (info > 0) { - throw runtime_error(message_int_err); - } else { - throw invalid_argument(message_ill_arg); + if (info == 0) + { + lwork = static_cast (work); + } + else if (info > 0) + { + throw runtime_error (message_int_err); + } + else + { + throw invalid_argument (message_ill_arg); } } const string especia::D_Decompose::message_int_err = // NOLINT - "especia::D_Decompose() Error: internal error in LAPACK"; + "especia::D_Decompose() Error: internal error in LAPACK"; const string especia::D_Decompose::message_ill_arg = // NOLINT - "especia::D_Decompose() Error: illegal argument(s) in call to LAPACK"; + "especia::D_Decompose() Error: illegal argument(s) in call to LAPACK"; +especia::R_Decompose::R_Decompose (natural m) + : n (integer (m)), work (), iwork (), isupp (2 * max (1, m)), + awork (m * m) +{ + lapack_inquire (n, lwork, liwork); -especia::R_Decompose::R_Decompose(natural m) - : n(integer(m)), work(), iwork(), isupp(2 * max(1, m)), awork(m * m) { - lapack_inquire(n, lwork, liwork); - - work.resize(static_cast(lwork)); - iwork.resize(static_cast(liwork)); + work.resize (static_cast (lwork)); + iwork.resize (static_cast (liwork)); } -especia::R_Decompose::~R_Decompose() = default; +especia::R_Decompose::~R_Decompose () = default; -void especia::R_Decompose::operator()(const real A[], real Z[], real w[]) const { - copy(&A[0], &A[n * n], &awork[0]); +void +especia::R_Decompose::operator() (const real A[], real Z[], real w[]) const +{ + copy (&A[0], &A[n * n], &awork[0]); - lapack_do(Z, w); + lapack_do (Z, w); } -void especia::R_Decompose::lapack_do(real Z[], real w[]) const { - integer m = 0; - integer info = 0; +void +especia::R_Decompose::lapack_do (real Z[], real w[]) const +{ + integer m = 0; + integer info = 0; - LAPACK_NAME_R_TYPE(syevr)(job, range, uplo, n, &awork[0], n, vl, vu, il, iu, abstol, m, w, Z, n, - &isupp[0], &work[0], lwork, &iwork[0], liwork, - info); + LAPACK_NAME_R_TYPE (syevr) + (job, range, uplo, n, &awork[0], n, vl, vu, il, iu, abstol, m, w, Z, n, + &isupp[0], &work[0], lwork, &iwork[0], liwork, info); - if (info == 0) { - // ok - } else if (info > 0) { - throw runtime_error(message_int_err); - } else { - throw invalid_argument(message_ill_arg); + if (info == 0) + { + // ok + } + else if (info > 0) + { + throw runtime_error (message_int_err); + } + else + { + throw invalid_argument (message_ill_arg); } } -void especia::R_Decompose::lapack_inquire(integer n, integer &lwork, integer &liwork) { - integer info; - integer m; - real work; - - LAPACK_NAME_R_TYPE(syevr)(job, range, uplo, n, nullptr, n, vl, vu, il, iu, abstol, m, nullptr, nullptr, n, - nullptr, &work, -1, &liwork, -1, - info); - - if (info == 0) { - lwork = static_cast(work); - } else if (info > 0) { - throw runtime_error(message_int_err); - } else { - throw invalid_argument(message_ill_arg); +void +especia::R_Decompose::lapack_inquire (integer n, integer &lwork, + integer &liwork) +{ + integer info; + integer m; + real work; + + LAPACK_NAME_R_TYPE (syevr) + (job, range, uplo, n, nullptr, n, vl, vu, il, iu, abstol, m, nullptr, + nullptr, n, nullptr, &work, -1, &liwork, -1, info); + + if (info == 0) + { + lwork = static_cast (work); + } + else if (info > 0) + { + throw runtime_error (message_int_err); + } + else + { + throw invalid_argument (message_ill_arg); } } -const real especia::R_Decompose::abstol = LAPACK_NAME_R_TYPE(lamch)('S'); // NOLINT +const real especia::R_Decompose::abstol + = LAPACK_NAME_R_TYPE (lamch) ('S'); // NOLINT const string especia::R_Decompose::message_int_err = // NOLINT - "especia::R_Decompose() Error: internal error in LAPACK"; + "especia::R_Decompose() Error: internal error in LAPACK"; const string especia::R_Decompose::message_ill_arg = // NOLINT - "especia::R_Decompose() Error: illegal argument(s) in call to LAPACK"; + "especia::R_Decompose() Error: illegal argument(s) in call to LAPACK"; +especia::X_Decompose::X_Decompose (natural m) + : n (integer (m)), work (), iwork (5 * m), ifail (m), awork (m * m) +{ + lapack_inquire (n, lwork); -especia::X_Decompose::X_Decompose(natural m) : n(integer(m)), work(), iwork(5 * m), ifail(m), awork(m * m) { - lapack_inquire(n, lwork); - - work.resize(static_cast(lwork)); + work.resize (static_cast (lwork)); } -especia::X_Decompose::~X_Decompose() = default; +especia::X_Decompose::~X_Decompose () = default; -void especia::X_Decompose::operator()(const real A[], real Z[], real w[]) const { - copy(&A[0], &A[n * n], &awork[0]); +void +especia::X_Decompose::operator() (const real A[], real Z[], real w[]) const +{ + copy (&A[0], &A[n * n], &awork[0]); - lapack_do(Z, w); + lapack_do (Z, w); } -void especia::X_Decompose::lapack_do(real Z[], real w[]) const { - integer m = 0; - integer info = 0; +void +especia::X_Decompose::lapack_do (real Z[], real w[]) const +{ + integer m = 0; + integer info = 0; - LAPACK_NAME_R_TYPE(syevx)(job, range, uplo, n, &awork[0], n, vl, vu, il, iu, abstol, m, w, Z, n, - &work[0], lwork, &iwork[0], &ifail[0], - info); + LAPACK_NAME_R_TYPE (syevx) + (job, range, uplo, n, &awork[0], n, vl, vu, il, iu, abstol, m, w, Z, n, + &work[0], lwork, &iwork[0], &ifail[0], info); - if (info == 0) { - // ok - } else if (info > 0) { - throw runtime_error(message_int_err); - } else { - throw invalid_argument(message_ill_arg); + if (info == 0) + { + // ok + } + else if (info > 0) + { + throw runtime_error (message_int_err); + } + else + { + throw invalid_argument (message_ill_arg); } } -void especia::X_Decompose::lapack_inquire(integer n, integer &lwork) { - integer info; - integer m; - real work; - - LAPACK_NAME_R_TYPE(syevx)(job, range, uplo, n, nullptr, n, vl, vu, il, iu, abstol, m, nullptr, nullptr, n, - &work, -1, nullptr, nullptr, - info); - - if (info == 0) { - lwork = static_cast(work); - } else if (info > 0) { - throw runtime_error(message_int_err); - } else { - throw invalid_argument(message_ill_arg); +void +especia::X_Decompose::lapack_inquire (integer n, integer &lwork) +{ + integer info; + integer m; + real work; + + LAPACK_NAME_R_TYPE (syevx) + (job, range, uplo, n, nullptr, n, vl, vu, il, iu, abstol, m, nullptr, + nullptr, n, &work, -1, nullptr, nullptr, info); + + if (info == 0) + { + lwork = static_cast (work); + } + else if (info > 0) + { + throw runtime_error (message_int_err); + } + else + { + throw invalid_argument (message_ill_arg); } } -const real especia::X_Decompose::abstol = real(2) * LAPACK_NAME_R_TYPE(lamch)('S'); // NOLINT +const real especia::X_Decompose::abstol + = real (2) * LAPACK_NAME_R_TYPE (lamch) ('S'); // NOLINT const string especia::X_Decompose::message_int_err = // NOLINT - "especia::X_Decompose() Error: internal error in LAPACK"; + "especia::X_Decompose() Error: internal error in LAPACK"; const string especia::X_Decompose::message_ill_arg = // NOLINT - "especia::X_Decompose() Error: illegal argument(s) in call to LAPACK"; + "especia::X_Decompose() Error: illegal argument(s) in call to LAPACK"; #undef LAPACK_NAME_R_TYPE #undef LAPACK_NAME_SINGLE diff --git a/src/main/cxx/core/decompose.h b/src/main/cxx/core/decompose.h index 6a9e588..22b2edc 100644 --- a/src/main/cxx/core/decompose.h +++ b/src/main/cxx/core/decompose.h @@ -12,194 +12,198 @@ #include "base.h" -namespace especia { - - /// Class to solve symmetric eigenproblems. Calls the LAPACK driver routine - /// @c [DS]SYEVD (divide and conquer). - /// - /// @remark The divide and conquer algorithm makes very mild assumptions - /// about floating point arithmetics. - /// - /// @remark This algorithm is O(n^3). - class D_Decompose { - public: - /// Constructs a new instance of this class for the problem dimension supplied as argument. - /// - /// @param[in] m The problem dimension. - explicit D_Decompose(natural m); - - /// The destructor. - ~D_Decompose(); - - /// Solves a symmetric eigenproblem. - /// - /// @param[in] A The symmetric matrix (row-major, lower triangular). - /// @param[out] Z The transformation matrix (row-major). - /// @param[out] w The eigenvalues, in ascending order. - /// - /// @throw invalid_argument when LAPACK was called with illegal arguments. - /// @throw runtime_error when an internal LAPACK error occurred. - void - operator()(const real A[], real Z[], real w[]) const; - - private: - void lapack_do(real Z[], real w[]) const; - - /// The problem dimension. - const integer n; - - /// The numeric workspace size. - integer lwork; - - /// The integer workspace size. - integer liwork; - - /// The numeric workspace array. - mutable std::valarray work; - - /// The integer workspace array. - mutable std::valarray iwork; - - /// Queries the optimal workspace size. - /// - /// @param[in] n The problem dimension. - /// @param[out] lwork The size of the numeric workspace. - /// @param[out] liwork The size of the integer workspace. - static void lapack_inquire(integer n, integer &lwork, integer &liwork); - - static const std::string message_int_err; - static const std::string message_ill_arg; - }; - - /// Class to solve symmetric eigenproblems. Calls the LAPACK driver routine - /// @c [DS]SYEVR (relatively robust representations). - /// - /// @attention Requires an environment that implements IEEE arithmetics and - /// handles NaN and infinities in the default manner. - /// - /// @remark This algorithm is O(n^2). - class R_Decompose { - public: - /// Constructs a new instance of this class for the problem dimension supplied as argument. - /// - /// @param[in] m The problem dimension. - explicit R_Decompose(natural m); - - /// The destructor. - ~R_Decompose(); - - /// Solves a symmetric eigenproblem. - /// - /// @param[in] A The symmetric matrix (row-major, lower triangular). - /// @param[out] Z The transformation matrix (row-major). - /// @param[out] w The eigenvalues, in ascending order. - /// - /// @throw invalid_argument when LAPACK was called with illegal arguments. - /// @throw runtime_error when an internal LAPACK error occurred. - void - operator()(const real A[], real Z[], real w[]) const; - - private: - void lapack_do(real Z[], real w[]) const; - - /// The problem dimension. - const integer n; - - /// The numeric workspace size. - integer lwork; - - /// The integer workspace size. - integer liwork; - - /// The numeric workspace array. - mutable std::valarray work; - - /// The integer workspace array. - mutable std::valarray iwork; - - /// A workspace array. - mutable std::valarray isupp; - - /// A workspace array. - mutable std::valarray awork; - - /// Queries the optimal workspace size. - /// - /// @param[in] n The problem dimension. - /// @param[out] lwork The size of the numeric workspace. - /// @param[out] liwork The size of the integer workspace. - static void lapack_inquire(integer n, integer &lwork, integer &liwork); - - /// The absolute accuracy of eigenvalues computed. Yields the most accurate results - /// when set to the 'safe minimum'. - static const real abstol; - - static const std::string message_int_err; - static const std::string message_ill_arg; - }; - - /// Class to solve symmetric eigenproblems. Calls the LAPACK driver routine - /// @c [DS]SYEVX (inverse iteration). - /// - /// @remark This algorithm is O(n^3). - class X_Decompose { - public: - /// Constructs a new instance of this class for the problem dimension supplied as argument. - /// - /// @param[in] m The problem dimension. - explicit X_Decompose(natural m); - - /// The destructor. - ~X_Decompose(); - - /// Solves a symmetric eigenproblem. - /// - /// @param[in] A The symmetric matrix (row-major, lower triangular). - /// @param[out] Z The transformation matrix (row-major). - /// @param[out] w The eigenvalues, in ascending order. - /// - /// @throw invalid_argument when LAPACK was called with illegal arguments. - /// @throw runtime_error when an internal LAPACK error occurred. - void - operator()(const real A[], real Z[], real w[]) const; - - private: - void lapack_do(real Z[], real w[]) const; - - /// The problem dimension. - const integer n; - - /// The numeric workspace size. - integer lwork; - - /// The numeric workspace array. - mutable std::valarray work; - - /// The integer workspace array. - mutable std::valarray iwork; - - /// A workspace array. - mutable std::valarray ifail; - - /// A workspace array. - mutable std::valarray awork; - - /// Inquires the optimal workspace size. - /// - /// @param[in] n The problem dimension. - /// @param[out] lwork The size of the numeric workspace. - static void lapack_inquire(integer n, integer &lwork); - - /// The absolute accuracy of eigenvalues computed. Yields the most accurate results - /// when set to twice the 'safe minimum'. - static const real abstol; - - static const std::string message_int_err; - static const std::string message_ill_arg; - }; +namespace especia +{ + +/// Class to solve symmetric eigenproblems. Calls the LAPACK driver routine +/// @c [DS]SYEVD (divide and conquer). +/// +/// @remark The divide and conquer algorithm makes very mild assumptions +/// about floating point arithmetics. +/// +/// @remark This algorithm is O(n^3). +class D_Decompose +{ +public: + /// Constructs a new instance of this class for the problem dimension + /// supplied as argument. + /// + /// @param[in] m The problem dimension. + explicit D_Decompose (natural m); + + /// The destructor. + ~D_Decompose (); + + /// Solves a symmetric eigenproblem. + /// + /// @param[in] A The symmetric matrix (row-major, lower triangular). + /// @param[out] Z The transformation matrix (row-major). + /// @param[out] w The eigenvalues, in ascending order. + /// + /// @throw invalid_argument when LAPACK was called with illegal arguments. + /// @throw runtime_error when an internal LAPACK error occurred. + void operator() (const real A[], real Z[], real w[]) const; + +private: + void lapack_do (real Z[], real w[]) const; + + /// The problem dimension. + const integer n; + + /// The numeric workspace size. + integer lwork; + + /// The integer workspace size. + integer liwork; + + /// The numeric workspace array. + mutable std::valarray work; + + /// The integer workspace array. + mutable std::valarray iwork; + + /// Queries the optimal workspace size. + /// + /// @param[in] n The problem dimension. + /// @param[out] lwork The size of the numeric workspace. + /// @param[out] liwork The size of the integer workspace. + static void lapack_inquire (integer n, integer &lwork, integer &liwork); + + static const std::string message_int_err; + static const std::string message_ill_arg; +}; + +/// Class to solve symmetric eigenproblems. Calls the LAPACK driver routine +/// @c [DS]SYEVR (relatively robust representations). +/// +/// @attention Requires an environment that implements IEEE arithmetics and +/// handles NaN and infinities in the default manner. +/// +/// @remark This algorithm is O(n^2). +class R_Decompose +{ +public: + /// Constructs a new instance of this class for the problem dimension + /// supplied as argument. + /// + /// @param[in] m The problem dimension. + explicit R_Decompose (natural m); + + /// The destructor. + ~R_Decompose (); + + /// Solves a symmetric eigenproblem. + /// + /// @param[in] A The symmetric matrix (row-major, lower triangular). + /// @param[out] Z The transformation matrix (row-major). + /// @param[out] w The eigenvalues, in ascending order. + /// + /// @throw invalid_argument when LAPACK was called with illegal arguments. + /// @throw runtime_error when an internal LAPACK error occurred. + void operator() (const real A[], real Z[], real w[]) const; + +private: + void lapack_do (real Z[], real w[]) const; + + /// The problem dimension. + const integer n; + + /// The numeric workspace size. + integer lwork; + + /// The integer workspace size. + integer liwork; + + /// The numeric workspace array. + mutable std::valarray work; + + /// The integer workspace array. + mutable std::valarray iwork; + + /// A workspace array. + mutable std::valarray isupp; + + /// A workspace array. + mutable std::valarray awork; + + /// Queries the optimal workspace size. + /// + /// @param[in] n The problem dimension. + /// @param[out] lwork The size of the numeric workspace. + /// @param[out] liwork The size of the integer workspace. + static void lapack_inquire (integer n, integer &lwork, integer &liwork); + + /// The absolute accuracy of eigenvalues computed. Yields the most accurate + /// results when set to the 'safe minimum'. + static const real abstol; + + static const std::string message_int_err; + static const std::string message_ill_arg; +}; + +/// Class to solve symmetric eigenproblems. Calls the LAPACK driver routine +/// @c [DS]SYEVX (inverse iteration). +/// +/// @remark This algorithm is O(n^3). +class X_Decompose +{ +public: + /// Constructs a new instance of this class for the problem dimension + /// supplied as argument. + /// + /// @param[in] m The problem dimension. + explicit X_Decompose (natural m); + + /// The destructor. + ~X_Decompose (); + + /// Solves a symmetric eigenproblem. + /// + /// @param[in] A The symmetric matrix (row-major, lower triangular). + /// @param[out] Z The transformation matrix (row-major). + /// @param[out] w The eigenvalues, in ascending order. + /// + /// @throw invalid_argument when LAPACK was called with illegal arguments. + /// @throw runtime_error when an internal LAPACK error occurred. + void operator() (const real A[], real Z[], real w[]) const; + +private: + void lapack_do (real Z[], real w[]) const; + + /// The problem dimension. + const integer n; + + /// The numeric workspace size. + integer lwork; + + /// The numeric workspace array. + mutable std::valarray work; + + /// The integer workspace array. + mutable std::valarray iwork; + + /// A workspace array. + mutable std::valarray ifail; + + /// A workspace array. + mutable std::valarray awork; + + /// Inquires the optimal workspace size. + /// + /// @param[in] n The problem dimension. + /// @param[out] lwork The size of the numeric workspace. + static void lapack_inquire (integer n, integer &lwork); + + /// The absolute accuracy of eigenvalues computed. Yields the most accurate + /// results when set to twice the 'safe minimum'. + static const real abstol; + + static const std::string message_int_err; + static const std::string message_ill_arg; +}; - /// The default algorithm to solve symmetric eigenproblems. - typedef R_Decompose Decompose; +/// The default algorithm to solve symmetric eigenproblems. +typedef R_Decompose Decompose; } diff --git a/src/main/cxx/core/deviates.h b/src/main/cxx/core/deviates.h index 4c795f4..8450b81 100644 --- a/src/main/cxx/core/deviates.h +++ b/src/main/cxx/core/deviates.h @@ -10,74 +10,83 @@ #include "base.h" -namespace especia { - - /// A class template to generate random normal deviates. - /// - /// The algorithm uses the polar method (e.g. Knuth, 1998, - /// Sec. 3.4.1, Algorithm P) to generate standard normally distributed random - /// deviates. - /// - /// Further reading: - /// - /// D. Knuth (1998). - /// *The art of computer programming 2. Seminumerical algorithms.* - /// Addison Wesley Longman, ISBN 0-201-89684-2. - /// - /// @tparam U The strategy to generate random uniform deviates. - template - class Normal_Deviate { - public: - /// Constructs a new instance of this class from a seed. - /// - /// @param[in] seed The seed. - explicit Normal_Deviate(word64 seed = 9600629759793949339ull) : uniform_deviate(seed) { - } - - /// Constructs a new instance of this class from a uniform deviate. - /// - /// @param[in] u The instance of this class to be copied. - explicit Normal_Deviate(const U &u) : uniform_deviate(u) { - } - - /// The destructor. - ~Normal_Deviate() = default; - - /// Returns a normal random number. - /// - /// @return a normal random number. - real operator()() const { - using std::log; - using std::sqrt; - - status = !status; - - if (status) { - real t; - - do { - x = 2.0 * uniform_deviate() - 1.0; - y = 2.0 * uniform_deviate() - 1.0; - t = x * x + y * y; - } while (t >= 1.0 or t == 0.0); - - t = sqrt(-2.0 * (log(t) / t)); - x *= t; - y *= t; - - return x; - } else { - return y; - } - } - - private: - const U uniform_deviate; - - mutable bool status = false; - mutable real x = 0.0; - mutable real y = 0.0; - }; +namespace especia +{ + +/// A class template to generate random normal deviates. +/// +/// The algorithm uses the polar method (e.g. Knuth, 1998, +/// Sec. 3.4.1, Algorithm P) to generate standard normally distributed random +/// deviates. +/// +/// Further reading: +/// +/// D. Knuth (1998). +/// *The art of computer programming 2. Seminumerical algorithms.* +/// Addison Wesley Longman, ISBN 0-201-89684-2. +/// +/// @tparam U The strategy to generate random uniform deviates. +template class Normal_Deviate +{ +public: + /// Constructs a new instance of this class from a seed. + /// + /// @param[in] seed The seed. + explicit Normal_Deviate (word64 seed = 9600629759793949339ull) + : uniform_deviate (seed) + { + } + + /// Constructs a new instance of this class from a uniform deviate. + /// + /// @param[in] u The instance of this class to be copied. + explicit Normal_Deviate (const U &u) : uniform_deviate (u) {} + + /// The destructor. + ~Normal_Deviate () = default; + + /// Returns a normal random number. + /// + /// @return a normal random number. + real + operator() () const + { + using std::log; + using std::sqrt; + + status = !status; + + if (status) + { + real t; + + do + { + x = 2.0 * uniform_deviate () - 1.0; + y = 2.0 * uniform_deviate () - 1.0; + t = x * x + y * y; + } + while (t >= 1.0 or t == 0.0); + + t = sqrt (-2.0 * (log (t) / t)); + x *= t; + y *= t; + + return x; + } + else + { + return y; + } + } + +private: + const U uniform_deviate; + + mutable bool status = false; + mutable real x = 0.0; + mutable real y = 0.0; +}; } diff --git a/src/main/cxx/core/equations.cxx b/src/main/cxx/core/equations.cxx index 04abba9..69f43ec 100644 --- a/src/main/cxx/core/equations.cxx +++ b/src/main/cxx/core/equations.cxx @@ -5,27 +5,38 @@ /// @copyright MIT License #include "equations.h" -void especia::Equations::birch94(const real &x, real &y, real &z) { - const real n = 1.0 + 8.34254E-05 + 2.406147E-08 / (130.0E-06 - x * x) + 1.5998E-10 / (38.9E-06 - x * x); - const real m = (4.812294E-08 * x) / sq(130.0E-06 - x * x) + (3.1996E-10 * x) / sq(38.9E-06 - x * x); +void +especia::Equations::birch94 (const real &x, real &y, real &z) +{ + const real n = 1.0 + 8.34254E-05 + 2.406147E-08 / (130.0E-06 - x * x) + + 1.5998E-10 / (38.9E-06 - x * x); + const real m = (4.812294E-08 * x) / sq (130.0E-06 - x * x) + + (3.1996E-10 * x) / sq (38.9E-06 - x * x); - y = x * n; - z = n + x * m; + y = x * n; + z = n + x * m; } -void especia::Equations::edlen53(const real &x, real &y, real &z) { - const real n = 1.0 + 6.43280E-05 + 2.5540E-10 / (0.0000410 - x * x) + 2.949810E-08 / (0.000146 - x * x); - const real m = (5.1080E-10 * x) / sq(0.0000410 - x * x) + (5.89962E-08 * x) / sq(0.000146 - x * x); +void +especia::Equations::edlen53 (const real &x, real &y, real &z) +{ + const real n = 1.0 + 6.43280E-05 + 2.5540E-10 / (0.0000410 - x * x) + + 2.949810E-08 / (0.000146 - x * x); + const real m = (5.1080E-10 * x) / sq (0.0000410 - x * x) + + (5.89962E-08 * x) / sq (0.000146 - x * x); - y = x * n; - z = n + x * m; + y = x * n; + z = n + x * m; } -void especia::Equations::edlen66(const real &x, real &y, real &z) { - const real n = 1.0 + 8.34213E-05 + 1.5997E-10 / (0.0000389 - x * x) + 2.406030E-08 / (0.000130 - x * x); - const real m = (3.1994E-10 * x) / sq(0.0000389 - x * x) + (4.81206E-08 * x) / sq(0.000130 - x * x); +void +especia::Equations::edlen66 (const real &x, real &y, real &z) +{ + const real n = 1.0 + 8.34213E-05 + 1.5997E-10 / (0.0000389 - x * x) + + 2.406030E-08 / (0.000130 - x * x); + const real m = (3.1994E-10 * x) / sq (0.0000389 - x * x) + + (4.81206E-08 * x) / sq (0.000130 - x * x); - y = x * n; - z = n + x * m; + y = x * n; + z = n + x * m; } - diff --git a/src/main/cxx/core/equations.h b/src/main/cxx/core/equations.h index a1b0884..f569b3d 100644 --- a/src/main/cxx/core/equations.h +++ b/src/main/cxx/core/equations.h @@ -8,124 +8,151 @@ #include "base.h" -namespace especia { +namespace especia +{ - /// Equations from scientific literature. - class Equations { - public: - /// Used to convert photon wavelength in vacuum to photon wavelength in air. - /// - /// Further reading: - /// - /// Donald C. Morton (2000). - /// *Atomic Data for Resonance Absorption Lines. II. Wavelengths Longward of the Lyman Limit for Heavy Elements.* - /// Astrophys. J. Suppl. Ser., 130, 2, 403. - /// - /// K. P. Birch and M. J. Downs (1994) - /// *Correction to the Updated Edlén Equation for the Refractive Index of Air* - /// Metrologia, 31, 4, 315. - /// - /// @param[in] x The wavenumber in vacuum (nm-1). - /// @return the wavenumber in air (nm-1). - /// - /// @attention The function uses wavenumber (nm-1) := 10.0 / wavelength (Angstrom) as input and output. - inline static real birch94(const real &x) { - return (1.0 + 8.34254E-05 + 2.406147E-08 / (130.0E-06 - x * x) + 1.5998E-10 / (38.9E-06 - x * x)) * x; - } +/// Equations from scientific literature. +class Equations +{ +public: + /// Used to convert photon wavelength in vacuum to photon wavelength in air. + /// + /// Further reading: + /// + /// Donald C. Morton (2000). + /// *Atomic Data for Resonance Absorption Lines. II. Wavelengths Longward of + /// the Lyman Limit for Heavy Elements.* Astrophys. J. Suppl. Ser., 130, 2, + /// 403. + /// + /// K. P. Birch and M. J. Downs (1994) + /// *Correction to the Updated Edlén Equation for the Refractive Index of + /// Air* Metrologia, 31, 4, 315. + /// + /// @param[in] x The wavenumber in vacuum (nm-1). + /// @return the wavenumber in air (nm-1). + /// + /// @attention The function uses wavenumber (nm-1) := 10.0 / wavelength + /// (Angstrom) as input and output. + inline static real + birch94 (const real &x) + { + return (1.0 + 8.34254E-05 + 2.406147E-08 / (130.0E-06 - x * x) + + 1.5998E-10 / (38.9E-06 - x * x)) + * x; + } - /// Used to convert photon wavelength in air to photon wavelength in vacuum (by means of Newton's method). - /// - /// Further reading: - /// - /// Donald C. Morton (2000). - /// *Atomic Data for Resonance Absorption Lines. II. Wavelengths Longward of the Lyman Limit for Heavy Elements.* - /// Astrophys. J. Suppl. Ser., 130, 2, 403. - /// - /// K. P. Birch and M. J. Downs (1994) - /// *Correction to the Updated Edlén Equation for the Refractive Index of Air* - /// Metrologia, 31, 4, 315. - /// - /// @param[in] x The wavenumber in vacuum (nm-1). - /// @param[out] y The wavenumber in air (nm-1). - /// @param[out] z The derivative of @c y with respect to @c x. - /// - /// @attention The function uses wavenumber (nm-1) := 10.0 / wavelength (Angstrom) as input and output. - static void birch94(const real &x, real &y, real &z); + /// Used to convert photon wavelength in air to photon wavelength in vacuum + /// (by means of Newton's method). + /// + /// Further reading: + /// + /// Donald C. Morton (2000). + /// *Atomic Data for Resonance Absorption Lines. II. Wavelengths Longward of + /// the Lyman Limit for Heavy Elements.* Astrophys. J. Suppl. Ser., 130, 2, + /// 403. + /// + /// K. P. Birch and M. J. Downs (1994) + /// *Correction to the Updated Edlén Equation for the Refractive Index of + /// Air* Metrologia, 31, 4, 315. + /// + /// @param[in] x The wavenumber in vacuum (nm-1). + /// @param[out] y The wavenumber in air (nm-1). + /// @param[out] z The derivative of @c y with respect to @c x. + /// + /// @attention The function uses wavenumber (nm-1) := 10.0 / wavelength + /// (Angstrom) as input and output. + static void birch94 (const real &x, real &y, real &z); - /// Used to convert photon wavelength in vacuum to photon wavelength in air. - /// - /// Further reading: - /// - /// B. Edlén (1953). - /// *The dispersion of standard air.* - /// Journal of the Optical Society of America, 43, 5, 339. - /// - /// @param[in] x The wavenumber in vacuum (nm-1). - /// @return the wavenumber in air (nm-1). - /// - /// @attention The function uses wavenumber (nm-1) := 10.0 / wavelength (Angstrom) as input and output. - /// - /// @remark This formula is the IAU standard for the vacuum to standard air corrections (see resolution - /// No. C15, Commission 44, XXI General Assembly in 1991). - inline static real edlen53(const real &x) { - return (1.0 + 6.43280E-05 + 2.5540E-10 / (0.0000410 - x * x) + 2.949810E-08 / (0.000146 - x * x)) * x; - } + /// Used to convert photon wavelength in vacuum to photon wavelength in air. + /// + /// Further reading: + /// + /// B. Edlén (1953). + /// *The dispersion of standard air.* + /// Journal of the Optical Society of America, 43, 5, 339. + /// + /// @param[in] x The wavenumber in vacuum (nm-1). + /// @return the wavenumber in air (nm-1). + /// + /// @attention The function uses wavenumber (nm-1) := 10.0 / wavelength + /// (Angstrom) as input and output. + /// + /// @remark This formula is the IAU standard for the vacuum to standard air + /// corrections (see resolution No. C15, Commission 44, XXI General Assembly + /// in 1991). + inline static real + edlen53 (const real &x) + { + return (1.0 + 6.43280E-05 + 2.5540E-10 / (0.0000410 - x * x) + + 2.949810E-08 / (0.000146 - x * x)) + * x; + } - /// Used to convert photon wavelength in air to photon wavelength in vacuum (by means of Newton's method). - /// - /// Further reading: - /// - /// B. Edlén (1953). - /// *The dispersion of standard air.* - /// Journal of the Optical Society of America, 43, 5, 339. - /// - /// @param[in] x The wavenumber in vacuum (nm-1). - /// @param[out] y The wavenumber in air (nm-1). - /// @param[out] z The derivative of @c y with respect to @c x. - /// - /// @attention The function uses wavenumber (nm-1) := 10.0 / wavelength (Angstrom) as input and output. - /// - /// @remark This formula is the IAU standard for the vacuum to standard air corrections (see resolution - /// No. C15, Commission 44, XXI General Assembly in 1991). - static void edlen53(const real &x, real &y, real &z); + /// Used to convert photon wavelength in air to photon wavelength in vacuum + /// (by means of Newton's method). + /// + /// Further reading: + /// + /// B. Edlén (1953). + /// *The dispersion of standard air.* + /// Journal of the Optical Society of America, 43, 5, 339. + /// + /// @param[in] x The wavenumber in vacuum (nm-1). + /// @param[out] y The wavenumber in air (nm-1). + /// @param[out] z The derivative of @c y with respect to @c x. + /// + /// @attention The function uses wavenumber (nm-1) := 10.0 / wavelength + /// (Angstrom) as input and output. + /// + /// @remark This formula is the IAU standard for the vacuum to standard air + /// corrections (see resolution No. C15, Commission 44, XXI General Assembly + /// in 1991). + static void edlen53 (const real &x, real &y, real &z); - /// Used to convert photon wavelength in vacuum to photon wavelength in air. - /// - /// Further reading: - /// - /// B. Edlén (1966). - /// *The refractive index of air.* - /// Metrologia, 2, 2, 71-80. - /// - /// - /// @param[in] x The wavenumber in vacuum (nm-1). - /// @return the wavenumber in air (nm-1). - /// - /// @attention The function uses wavenumber (nm-1) := 10.0 / wavelength (Angstrom) as input and output. - inline static real edlen66(const real &x) { - return (1.0 + 8.34213E-05 + 1.5997E-10 / (0.0000389 - x * x) + 2.406030E-08 / (0.000130 - x * x)) * x; - } + /// Used to convert photon wavelength in vacuum to photon wavelength in air. + /// + /// Further reading: + /// + /// B. Edlén (1966). + /// *The refractive index of air.* + /// Metrologia, 2, 2, 71-80. + /// + /// + /// @param[in] x The wavenumber in vacuum (nm-1). + /// @return the wavenumber in air (nm-1). + /// + /// @attention The function uses wavenumber (nm-1) := 10.0 / wavelength + /// (Angstrom) as input and output. + inline static real + edlen66 (const real &x) + { + return (1.0 + 8.34213E-05 + 1.5997E-10 / (0.0000389 - x * x) + + 2.406030E-08 / (0.000130 - x * x)) + * x; + } - /// Used to convert photon wavelength in air to photon wavelength in vacuum (by means of Newton's method). - /// - /// Further reading: - /// - /// B. Edlén (1966). - /// *The refractive index of air.* - /// Metrologia, 2, 2, 71-80. - /// - /// - /// @param[in] x The wavenumber in vacuum (nm-1). - /// @param[out] y The wavenumber in air (nm-1). - /// @param[out] z The derivative of @c y with respect to @c x. - /// - /// @attention The function uses wavenumber (nm-1) := 10.0 / wavelength (Angstrom) as input and output. - static void edlen66(const real &x, real &y, real &z); + /// Used to convert photon wavelength in air to photon wavelength in vacuum + /// (by means of Newton's method). + /// + /// Further reading: + /// + /// B. Edlén (1966). + /// *The refractive index of air.* + /// Metrologia, 2, 2, 71-80. + /// + /// + /// @param[in] x The wavenumber in vacuum (nm-1). + /// @param[out] y The wavenumber in air (nm-1). + /// @param[out] z The derivative of @c y with respect to @c x. + /// + /// @attention The function uses wavenumber (nm-1) := 10.0 / wavelength + /// (Angstrom) as input and output. + static void edlen66 (const real &x, real &y, real &z); - private: - /// The private constructor prevents instantiation. - Equations() = default; - }; +private: + /// The private constructor prevents instantiation. + Equations () = default; +}; } diff --git a/src/main/cxx/core/exitcodes.h b/src/main/cxx/core/exitcodes.h index a62e86c..cb450b6 100644 --- a/src/main/cxx/core/exitcodes.h +++ b/src/main/cxx/core/exitcodes.h @@ -6,30 +6,33 @@ #ifndef ESPECIA_EXITCODES_H #define ESPECIA_EXITCODES_H -namespace especia { +namespace especia +{ - /// Application error exit codes. - class Exit_Codes { - public: - /// The optimization stopped due to an underflow of the mutation variance (exit code = 1). - static const int optimization_underflow = 00001; +/// Application error exit codes. +class Exit_Codes +{ +public: + /// The optimization stopped due to an underflow of the mutation variance + /// (exit code = 1). + static const int optimization_underflow = 00001; - /// The optimization failed to reach the required accuracy goal within the prescribed - /// number of generations (exit code = 2). - static const int optimization_stopped = 00002; + /// The optimization failed to reach the required accuracy goal within the + /// prescribed number of generations (exit code = 2). + static const int optimization_stopped = 00002; - /// A logic error occurred (exit code = 8). - static const int logic_error = 00010; + /// A logic error occurred (exit code = 8). + static const int logic_error = 00010; - /// A runtime error occurred (exit code = 16). - static const int runtime_error = 00020; + /// A runtime error occurred (exit code = 16). + static const int runtime_error = 00020; - /// An unspecific exception occurred (exit code = 64). - static const int unspecific_exception = 00100; + /// An unspecific exception occurred (exit code = 64). + static const int unspecific_exception = 00100; - private: - Exit_Codes() = default; // private constructor prevents instantiation - }; +private: + Exit_Codes () = default; // private constructor prevents instantiation +}; } diff --git a/src/main/cxx/core/integrator.h b/src/main/cxx/core/integrator.h index 3fdd0b9..6e717c4 100644 --- a/src/main/cxx/core/integrator.h +++ b/src/main/cxx/core/integrator.h @@ -13,589 +13,646 @@ #include "base.h" -namespace especia { - - /// Numerical integration by means of recursive monotone stable quadrature - /// formulas. +namespace especia +{ + +/// Numerical integration by means of recursive monotone stable quadrature +/// formulas. +/// +/// Further reading: +/// +/// Favati, P.; Lotti, G.; and Romani, F. (1991). +/// *Interpolary Integration Formulas for Optimal Composition.* +/// ACM Trans. Math. Software 17, 207-217. +/// https://doi.org/10.1145/108556.108571 +/// Favati, P.; Lotti, G.; and Romani, F. (1991). +/// *Algorithm 691: Improving QUADPACK Automatic Integration Routines.* +/// ACM Trans. Math. Software 17, 218-232. +/// https://doi.org/10.1145/108556.108580 +/// +/// @tparam T The number type used for quadrature calculations. The quadrature +/// weight and abscissa values are defined with a precision of 48 decimal +/// digits. +template class Integrator +{ +public: + /// The recursive monotone stable quadrature formulas. + enum Formula + { + /// The formula for integration with 13 quadrature points. + Q13, + /// The formula for integration with 19 quadrature points. + Q19, + /// The formula for integration with 27 quadrature points. + Q27, + /// The formula for integration with 41 quadrature points. + Q41 + }; + + /// Constructs a new integrator, based on the formulas supplied as argument. + /// + /// @param[in] p The formula with less quadrature points. + /// @param[in] q The formula with more quadrature points. + explicit Integrator (Formula p = Q19, Formula q = Q27) : p (p), q (q) {} + + /// The destructor. + ~Integrator () = default; + + /// Computes the integral of a function, with the limits supplied as + /// argument, i.e. + /// @f[ \int_{a}^{b} f(x) dx @f]. + /// + /// @tparam F The integrand type. + /// + /// @param[in] f The integrand. + /// @param[in] a The lower limit of integration. + /// @param[in] b The upper limit of integration. + /// @param[in] accuracy_goal The (absolute) accuracy goal. + /// @param[in] max_iteration The maximum number of iterations. + /// @return the value of the integral. + template + T + integrate (const F &f, T a, T b, T accuracy_goal = T (1.0E-6), + natural max_iteration = 100) const + { + Integrator::Partition > partition (f, a, b, p, q); + + for (natural i = 0; i < max_iteration; ++i) + { + if (partition.absolute_error () < accuracy_goal) + { + break; + } + partition.refine (); + } + + return partition.result (); + } + + /// Computes the value of the positive-infinite integral of a function, i.e. + /// @f[ \int_{0}^{\infty} f(x) dx @f]. + /// + /// Makes the variable transformation @f$ u = \exp(-x) @f$ and computes + /// @f[ \int_{0}^{1} \frac{f(-\log(u))}{u} du @f]. + /// + /// @tparam F The integrand type. + /// + /// @param[in] f The integrand. + /// @param[in] accuracy_goal The (absolute) accuracy goal. + /// @param[in] max_iteration The maximum number of iterations. + /// @return the value of the positive-infinite integral. + /// + /// @attention The integrand must converge rapidly (faster than @f$ 1/x @f$) + /// to zero at infinity, i.e. + /// @f[ \lim_{x\to\infty} \frac{f(x)}{x} = 0 @f]. + template + T + integrate_positive_infinite (const F &f, T accuracy_goal = T (1.0E-6), + natural max_iteration = 100) const + { + using std::log; + + return integrate ( + [&f] (T u) -> T { + return u > T (0.0) ? f (-log (u)) / u : T (0.0); + }, // infinity maps to zero + T (0.0), T (1.0), accuracy_goal, max_iteration); + } + + /// Computes the value of the negative-infinite integral of a function, i.e. + /// @f[ \int_{-\infty}^{0} f(x) dx @f]. + /// + /// @tparam F The integrand type. + /// + /// @param[in] f The integrand. + /// @param[in] accuracy_goal The (absolute) accuracy goal. + /// @param[in] max_iteration The maximum number of iterations. + /// @return the value of the negative-infinite integral. + /// + /// @attention The integrand must converge rapidly (faster than @f$ 1/x @f$) + /// to zero at infinity, i.e. + /// @f[ \lim_{x\to -\infty} \frac{f(x)}{x} = 0 @f]. + template + T + integrate_negative_infinite (const F &f, T accuracy_goal = T (1.0E-6), + natural max_iteration = 100) const + { + return integrate_positive_infinite ([&f] (T x) -> T { return f (-x); }, + accuracy_goal, max_iteration); + } + + /// Computes the value of the infinite integral of a function, i.e. + /// @f[ \int_{-\infty}^{\infty} f(x) dx @f]. + /// + /// @tparam F The integrand type. + /// + /// @param[in] f The integrand. + /// @param[in] accuracy_goal The (absolute) accuracy goal. + /// @param[in] max_iteration The maximum number of iterations. + /// @return the value of the infinite integral. + /// + /// @attention The integrand must converge rapidly (faster than @f$ 1/x @f$) + /// to zero at infinity, i.e. + /// @f[ \lim_{x\to\pm\infty} \frac{f(x)}{x} = 0 @f]. + template + T + integrate_infinite (const F &f, T accuracy_goal = T (1.0E-6), + natural max_iteration = 100) const + { + return integrate_positive_infinite (f, accuracy_goal, max_iteration) + + integrate_negative_infinite (f, accuracy_goal, max_iteration); + } + +private: + /// A part of a numerical integral. + /// + /// @tparam F The integrand type. + template class Part + { + public: + /// The constructor. /// - /// Further reading: + /// @param f The integrand. + /// @param a The lower limit of integration. + /// @param b The upper limit of integration. + /// @param p The formula with less quadrature points. + /// @param q The formula with more quadrature points. + Part (const F &f, T a, T b, Formula p, Formula q) + : f (f), a (a), b (b), p (p), q (q), c (T (0.5) * (a + b)), + h (T (0.5) * (b - a)), yl (21), yu (21) + { + evaluate (); + } + + /// The destructor. + ~Part () = default; + + /// Returns the absolute error of the integration result of this part. /// - /// Favati, P.; Lotti, G.; and Romani, F. (1991). - /// *Interpolary Integration Formulas for Optimal Composition.* - /// ACM Trans. Math. Software 17, 207-217. - /// https://doi.org/10.1145/108556.108571 - /// Favati, P.; Lotti, G.; and Romani, F. (1991). - /// *Algorithm 691: Improving QUADPACK Automatic Integration Routines.* - /// ACM Trans. Math. Software 17, 218-232. - /// https://doi.org/10.1145/108556.108580 + /// @return the absolute error of the integration result. + T + absolute_error () const + { + return err; + } + + /// Returns the integration result of this part. /// - /// @tparam T The number type used for quadrature calculations. The quadrature weight - /// and abscissa values are defined with a precision of 48 decimal digits. - template - class Integrator { - public: - - /// The recursive monotone stable quadrature formulas. - enum Formula { - /// The formula for integration with 13 quadrature points. - Q13, - /// The formula for integration with 19 quadrature points. - Q19, - /// The formula for integration with 27 quadrature points. - Q27, - /// The formula for integration with 41 quadrature points. - Q41 - }; - - /// Constructs a new integrator, based on the formulas supplied as argument. - /// - /// @param[in] p The formula with less quadrature points. - /// @param[in] q The formula with more quadrature points. - explicit Integrator(Formula p = Q19, Formula q = Q27) : p(p), q(q) { - } - - /// The destructor. - ~Integrator() = default; - - /// Computes the integral of a function, with the limits supplied as argument, i.e. - /// @f[ \int_{a}^{b} f(x) dx @f]. - /// - /// @tparam F The integrand type. - /// - /// @param[in] f The integrand. - /// @param[in] a The lower limit of integration. - /// @param[in] b The upper limit of integration. - /// @param[in] accuracy_goal The (absolute) accuracy goal. - /// @param[in] max_iteration The maximum number of iterations. - /// @return the value of the integral. - template - T integrate(const F &f, T a, T b, T accuracy_goal = T(1.0E-6), natural max_iteration = 100) const { - Integrator::Partition> partition(f, a, b, p, q); - - for (natural i = 0; i < max_iteration; ++i) { - if (partition.absolute_error() < accuracy_goal) { - break; - } - partition.refine(); + /// @return the integration result. + T + result () const + { + return res; + } + + /// Creates a new part from the lower half of this part. + /// + /// @return the lower half part. + Part * + new_lower_part () const + { + auto *part = new Part (this, a, c); + + part->yu[0] = yl[2]; + part->yu[1] = yl[7]; + part->yu[2] = yl[1]; + part->yu[4] = f (part->c + Integrator::xi[4] * part->h); + part->yu[5] = f (part->c + Integrator::xi[5] * part->h); + part->yu[6] = yl[0]; + part->yl[0] = yl[2]; + part->yl[1] = yl[8]; + part->yl[2] = yl[3]; + part->yl[3] = yl[4]; + part->yl[4] = yl[5]; + part->yl[5] = yl[9]; + part->yl[6] = yl[6]; + if (nl > 10) + { + part->yu[3] = yl[10]; + part->yl[7] = yl[11]; + part->yl[8] = yl[12]; + part->yl[9] = yl[13]; + if (nl > 14) + { + part->yu[7] = yl[15]; + part->yu[8] = yl[14]; + part->yu[9] = f (part->c + Integrator::xi[9] * part->h); + part->yu[10] = yl[16]; + part->yl[10] = yl[17]; + part->yl[11] = yl[18]; + part->yl[12] = yl[19]; + part->yl[13] = yl[20]; + part->nu = 11; + part->nl = 14; + } + else + { + part->nu = 7; + part->nl = 10; } - - return partition.result(); - } - - /// Computes the value of the positive-infinite integral of a function, i.e. - /// @f[ \int_{0}^{\infty} f(x) dx @f]. - /// - /// Makes the variable transformation @f$ u = \exp(-x) @f$ and computes - /// @f[ \int_{0}^{1} \frac{f(-\log(u))}{u} du @f]. - /// - /// @tparam F The integrand type. - /// - /// @param[in] f The integrand. - /// @param[in] accuracy_goal The (absolute) accuracy goal. - /// @param[in] max_iteration The maximum number of iterations. - /// @return the value of the positive-infinite integral. - /// - /// @attention The integrand must converge rapidly (faster than @f$ 1/x @f$) to zero at infinity, i.e. - /// @f[ \lim_{x\to\infty} \frac{f(x)}{x} = 0 @f]. - template - T integrate_positive_infinite(const F &f, T accuracy_goal = T(1.0E-6), natural max_iteration = 100) const { - using std::log; - - return integrate([&f](T u) -> T { return u > T(0.0) ? f(-log(u)) / u : T(0.0); }, // infinity maps to zero - T(0.0), T(1.0), accuracy_goal, max_iteration); - } - - /// Computes the value of the negative-infinite integral of a function, i.e. - /// @f[ \int_{-\infty}^{0} f(x) dx @f]. - /// - /// @tparam F The integrand type. - /// - /// @param[in] f The integrand. - /// @param[in] accuracy_goal The (absolute) accuracy goal. - /// @param[in] max_iteration The maximum number of iterations. - /// @return the value of the negative-infinite integral. - /// - /// @attention The integrand must converge rapidly (faster than @f$ 1/x @f$) to zero at infinity, i.e. - /// @f[ \lim_{x\to -\infty} \frac{f(x)}{x} = 0 @f]. - template - T integrate_negative_infinite(const F &f, T accuracy_goal = T(1.0E-6), natural max_iteration = 100) const { - return integrate_positive_infinite([&f](T x) -> T { return f(-x); }, accuracy_goal, max_iteration); } - - /// Computes the value of the infinite integral of a function, i.e. - /// @f[ \int_{-\infty}^{\infty} f(x) dx @f]. - /// - /// @tparam F The integrand type. - /// - /// @param[in] f The integrand. - /// @param[in] accuracy_goal The (absolute) accuracy goal. - /// @param[in] max_iteration The maximum number of iterations. - /// @return the value of the infinite integral. - /// - /// @attention The integrand must converge rapidly (faster than @f$ 1/x @f$) to zero at infinity, i.e. - /// @f[ \lim_{x\to\pm\infty} \frac{f(x)}{x} = 0 @f]. - template - T integrate_infinite(const F &f, T accuracy_goal = T(1.0E-6), natural max_iteration = 100) const { - return integrate_positive_infinite(f, accuracy_goal, max_iteration) - + integrate_negative_infinite(f, accuracy_goal, max_iteration); + else + { + part->yu[3] = f (part->c + Integrator::xi[3] * part->h); + part->nu = 7; + part->nl = 7; } - private: - /// A part of a numerical integral. - /// - /// @tparam F The integrand type. - template - class Part { - public: - /// The constructor. - /// - /// @param f The integrand. - /// @param a The lower limit of integration. - /// @param b The upper limit of integration. - /// @param p The formula with less quadrature points. - /// @param q The formula with more quadrature points. - Part(const F &f, T a, T b, Formula p, Formula q) - : f(f), a(a), b(b), p(p), q(q), c(T(0.5) * (a + b)), h(T(0.5) * (b - a)), yl(21), yu(21) { - evaluate(); - } - - /// The destructor. - ~Part() = default; + part->evaluate (); - /// Returns the absolute error of the integration result of this part. - /// - /// @return the absolute error of the integration result. - T absolute_error() const { - return err; - } - - /// Returns the integration result of this part. - /// - /// @return the integration result. - T result() const { - return res; - } + return part; + } - /// Creates a new part from the lower half of this part. - /// - /// @return the lower half part. - Part *new_lower_part() const { - auto *part = new Part(this, a, c); - - part->yu[0] = yl[2]; - part->yu[1] = yl[7]; - part->yu[2] = yl[1]; - part->yu[4] = f(part->c + Integrator::xi[4] * part->h); - part->yu[5] = f(part->c + Integrator::xi[5] * part->h); - part->yu[6] = yl[0]; - part->yl[0] = yl[2]; - part->yl[1] = yl[8]; - part->yl[2] = yl[3]; - part->yl[3] = yl[4]; - part->yl[4] = yl[5]; - part->yl[5] = yl[9]; - part->yl[6] = yl[6]; - if (nl > 10) { - part->yu[3] = yl[10]; - part->yl[7] = yl[11]; - part->yl[8] = yl[12]; - part->yl[9] = yl[13]; - if (nl > 14) { - part->yu[7] = yl[15]; - part->yu[8] = yl[14]; - part->yu[9] = f(part->c + Integrator::xi[9] * part->h); - part->yu[10] = yl[16]; - part->yl[10] = yl[17]; - part->yl[11] = yl[18]; - part->yl[12] = yl[19]; - part->yl[13] = yl[20]; - part->nu = 11; - part->nl = 14; - } else { - part->nu = 7; - part->nl = 10; - } - } else { - part->yu[3] = f(part->c + Integrator::xi[3] * part->h); - part->nu = 7; - part->nl = 7; - } - - part->evaluate(); - - return part; - } - - /// Creates a new part from the upper half of this part. - /// - /// @return the upper half part. - Part *new_upper_part() const { - auto *part = new Part(this, c, b); - - part->yl[0] = yu[2]; - part->yl[1] = yu[7]; - part->yl[2] = yu[1]; - part->yl[4] = f(part->c - Integrator::xi[4] * part->h); - part->yl[5] = f(part->c - Integrator::xi[5] * part->h); - part->yl[6] = yu[0]; - part->yu[0] = yu[2]; - part->yu[1] = yu[8]; - part->yu[2] = yu[3]; - part->yu[3] = yu[4]; - part->yu[4] = yu[5]; - part->yu[5] = yu[9]; - part->yu[6] = yu[6]; - if (nu > 10) { - part->yl[3] = yu[10]; - part->yu[7] = yu[11]; - part->yu[8] = yu[12]; - part->yu[9] = yu[13]; - if (nu > 14) { - part->yl[7] = yu[15]; - part->yl[8] = yu[14]; - part->yl[9] = f(part->c - Integrator::xi[9] * part->h); - part->yl[10] = yu[16]; - part->yu[10] = yu[17]; - part->yu[11] = yu[18]; - part->yu[12] = yu[19]; - part->yu[13] = yu[20]; - part->nl = 11; - part->nu = 14; - } else { - part->nl = 7; - part->nu = 10; - } - } else { - part->yl[3] = f(part->c - Integrator::xi[3] * part->h); - part->nl = 7; - part->nu = 7; - } - - part->evaluate(); - - return part; + /// Creates a new part from the upper half of this part. + /// + /// @return the upper half part. + Part * + new_upper_part () const + { + auto *part = new Part (this, c, b); + + part->yl[0] = yu[2]; + part->yl[1] = yu[7]; + part->yl[2] = yu[1]; + part->yl[4] = f (part->c - Integrator::xi[4] * part->h); + part->yl[5] = f (part->c - Integrator::xi[5] * part->h); + part->yl[6] = yu[0]; + part->yu[0] = yu[2]; + part->yu[1] = yu[8]; + part->yu[2] = yu[3]; + part->yu[3] = yu[4]; + part->yu[4] = yu[5]; + part->yu[5] = yu[9]; + part->yu[6] = yu[6]; + if (nu > 10) + { + part->yl[3] = yu[10]; + part->yu[7] = yu[11]; + part->yu[8] = yu[12]; + part->yu[9] = yu[13]; + if (nu > 14) + { + part->yl[7] = yu[15]; + part->yl[8] = yu[14]; + part->yl[9] = f (part->c - Integrator::xi[9] * part->h); + part->yl[10] = yu[16]; + part->yu[10] = yu[17]; + part->yu[11] = yu[18]; + part->yu[12] = yu[19]; + part->yu[13] = yu[20]; + part->nl = 11; + part->nu = 14; } - - private: - /// Constructs a new part from a parent part. - /// - /// @param parent The parent part. - /// @param a The lower limit of integration. - /// @param b The upper limit of integration. - Part(const Part *parent, T a, T b) - : f(parent->f), a(a), b(b), p(parent->p), q(parent->q), - c(T(0.5) * (a + b)), h(T(0.5) * (b - a)), yl(21), yu(21) { - // do not evaluate + else + { + part->nl = 7; + part->nu = 10; } + } + else + { + part->yl[3] = f (part->c - Integrator::xi[3] * part->h); + part->nl = 7; + part->nu = 7; + } - /// Evaluates the integration result of this part and its absolute error. - void evaluate() { - using std::abs; - - res = evaluate(q); - err = abs(res - evaluate(p)); - } + part->evaluate (); - /// Evaluates the integration result of this part using the quadrature formula supplied as argument. - /// - /// @param q The quadrature formula. - /// @return the result. - T evaluate(Formula q) { - const natural m = Integrator::mw[q]; - const natural n = Integrator::nw[q]; + return part; + } - T result = T(0.0); + private: + /// Constructs a new part from a parent part. + /// + /// @param parent The parent part. + /// @param a The lower limit of integration. + /// @param b The upper limit of integration. + Part (const Part *parent, T a, T b) + : f (parent->f), a (a), b (b), p (parent->p), q (parent->q), + c (T (0.5) * (a + b)), h (T (0.5) * (b - a)), yl (21), yu (21) + { + // do not evaluate + } + + /// Evaluates the integration result of this part and its absolute error. + void + evaluate () + { + using std::abs; + + res = evaluate (q); + err = abs (res - evaluate (p)); + } + + /// Evaluates the integration result of this part using the quadrature + /// formula supplied as argument. + /// + /// @param q The quadrature formula. + /// @return the result. + T + evaluate (Formula q) + { + const natural m = Integrator::mw[q]; + const natural n = Integrator::nw[q]; + + T result = T (0.0); #ifdef _OPENMP -#pragma omp parallel for reduction(+:result) +#pragma omp parallel for reduction(+ : result) #endif - for (natural i = 0; i < n; ++i) { - if (i >= nl) { - yl[i] = f(c - h * Integrator::xi[i]); - } - if (i >= nu) { - yu[i] = f(c + h * Integrator::xi[i]); - } - result += (yl[i] + yu[i]) * Integrator::wi[m + i]; - } - if (nl < n) { - nl = n; - } - if (nu < n) { - nu = n; - } - - return result * h; + for (natural i = 0; i < n; ++i) + { + if (i >= nl) + { + yl[i] = f (c - h * Integrator::xi[i]); } + if (i >= nu) + { + yu[i] = f (c + h * Integrator::xi[i]); + } + result += (yl[i] + yu[i]) * Integrator::wi[m + i]; + } + if (nl < n) + { + nl = n; + } + if (nu < n) + { + nu = n; + } - /// The integrand. - const F &f; - - /// The lower limit of integration. - const T a; - - /// The upper limit of integration. - const T b; - - /// The selected formula with less quadrature points. - const Formula p; - - /// The selected formula with more quadrature points. - const Formula q; + return result * h; + } - /// The center of the interval of integration. - const T c; + /// The integrand. + const F &f; - /// The width of the interval of integration. - const T h; + /// The lower limit of integration. + const T a; - /// The integrand values for the lower half interval of integration. - std::valarray yl; + /// The upper limit of integration. + const T b; - /// The integrand values for the upper half interval of integration. - std::valarray yu; + /// The selected formula with less quadrature points. + const Formula p; - /// The number of evaluated integrand values for the lower half interval. - natural nl = 0; + /// The selected formula with more quadrature points. + const Formula q; - /// The number of evaluated integrand values for the upper half interval. - natural nu = 0; + /// The center of the interval of integration. + const T c; - /// The absolute error of the integration result. - T err = T(0.0); + /// The width of the interval of integration. + const T h; - /// The integration result. - T res = T(0.0); - }; + /// The integrand values for the lower half interval of integration. + std::valarray yl; - /// Compares the absolute error of two parts of a numerical integration. - /// - /// @tparam P The part type. - template - class Part_Compare { - public: - /// Compares the absolute error of two parts of a numerical integration. - /// - /// @param p The first part. - /// @param q The other part. - /// @return @c true, if the absolute error of the first part is less than that of the other part. - bool operator()(const P *p, const P *q) const { - return p->absolute_error() < q->absolute_error(); - } - }; - - /// A partition of a numerical integral into a complete set of disjoint parts. - /// - /// @tparam F The integrand type. - /// @tparam P The part type. - template - class Partition { - public: - /// The constructor. - /// - /// @param f The integrand. - /// @param a The lower limit of integration. - /// @param b The upper limit of integration. - /// @param p The formula with less quadrature points. - /// @param q The formula with more quadrature points. - Partition(const F &f, T a, T b, Formula p, Formula q) : part_compare(Part_Compare

()) { - using std::make_heap; - using std::push_heap; - - auto *part = new P(f, a, b, p, q); - make_heap(parts.begin(), parts.end(), part_compare); - add_part(part); - } + /// The integrand values for the upper half interval of integration. + std::valarray yu; - /// The destructor. - ~Partition() { // NOLINT - for (auto part : parts) { - delete part; - } - } + /// The number of evaluated integrand values for the lower half interval. + natural nl = 0; - /// Returns the absolute error of the integration result for this partition. - /// - /// @return the absolute error of the integration result. - T absolute_error() const { - T err = T(0.0); + /// The number of evaluated integrand values for the upper half interval. + natural nu = 0; - for (auto part : parts) { - err += part->absolute_error(); - } + /// The absolute error of the integration result. + T err = T (0.0); - return err; - } + /// The integration result. + T res = T (0.0); + }; - /// Returns the integration result for this partition. - /// - /// @return the integration result. - T result() const { - T res = T(0.0); + /// Compares the absolute error of two parts of a numerical integration. + /// + /// @tparam P The part type. + template class Part_Compare + { + public: + /// Compares the absolute error of two parts of a numerical integration. + /// + /// @param p The first part. + /// @param q The other part. + /// @return @c true, if the absolute error of the first part is less than + /// that of the other part. + bool + operator() (const P *p, const P *q) const + { + return p->absolute_error () < q->absolute_error (); + } + }; + + /// A partition of a numerical integral into a complete set of disjoint + /// parts. + /// + /// @tparam F The integrand type. + /// @tparam P The part type. + template class Partition + { + public: + /// The constructor. + /// + /// @param f The integrand. + /// @param a The lower limit of integration. + /// @param b The upper limit of integration. + /// @param p The formula with less quadrature points. + /// @param q The formula with more quadrature points. + Partition (const F &f, T a, T b, Formula p, Formula q) + : part_compare (Part_Compare

()) + { + using std::make_heap; + using std::push_heap; + + auto *part = new P (f, a, b, p, q); + make_heap (parts.begin (), parts.end (), part_compare); + add_part (part); + } + + /// The destructor. + ~Partition () + { // NOLINT + for (auto part : parts) + { + delete part; + } + } - for (auto part : parts) { - res += part->result(); - } + /// Returns the absolute error of the integration result for this + /// partition. + /// + /// @return the absolute error of the integration result. + T + absolute_error () const + { + T err = T (0.0); + + for (auto part : parts) + { + err += part->absolute_error (); + } - return res; - } + return err; + } - /// Refines this partition. - void refine() { - P *popped = pop_part(); - add_part(popped->new_lower_part()); - add_part(popped->new_upper_part()); + /// Returns the integration result for this partition. + /// + /// @return the integration result. + T + result () const + { + T res = T (0.0); + + for (auto part : parts) + { + res += part->result (); + } - delete popped; - } + return res; + } - private: - /// Removes the part with the largest absolute error of integration from the partition. - /// - /// @return the part with the largest absolute error. - P *pop_part() { - using std::pop_heap; + /// Refines this partition. + void + refine () + { + P *popped = pop_part (); + add_part (popped->new_lower_part ()); + add_part (popped->new_upper_part ()); - P *popped = parts.front(); - pop_heap(parts.begin(), parts.end(), part_compare); - parts.pop_back(); + delete popped; + } - return popped; - } + private: + /// Removes the part with the largest absolute error of integration from + /// the partition. + /// + /// @return the part with the largest absolute error. + P * + pop_part () + { + using std::pop_heap; - /// Adds a new part to the partition. - /// - /// @param part The part. - void add_part(P *part) { - using std::push_heap; + P *popped = parts.front (); + pop_heap (parts.begin (), parts.end (), part_compare); + parts.pop_back (); - parts.push_back(part); - push_heap(parts.begin(), parts.end(), part_compare); - } + return popped; + } - /// Compares the absolute error of integration of two parts. - const Part_Compare

part_compare; - - /// The parts of this partition. - std::vector

parts{}; - }; - - /// The selected quadrature formula with less points. - const Formula p; - - /// The selected quadrature formula with more points. - const Formula q; - - /// The quadrature abscissa values. - static const T xi[]; - - /// The quadrature weights. - static const T wi[]; - - /// The start indices into the quadrature weights. - static const natural mw[]; - - /// The number of quadrature weights. - static const natural nw[]; - }; - - template - const T Integrator::xi[] = { - // abscissas for Q13 - T(0.0000000L), - T(0.2500000L), - T(0.5000000L), - T(0.7500000L), - T(0.8750000L), - T(0.9375000L), - T(1.0000000L), - // additional abscissas for Q19, Q27 and Q41 - T(0.3750000L), - T(0.6250000L), - T(0.9687500L), - // additional abscissas for Q27 and Q41 - T(0.1250000L), - T(0.6875000L), - T(0.8125000L), - T(0.9843750L), - // additional abscissas for Q41 - T(0.1875000L), - T(0.3125000L), - T(0.4375000L), - T(0.5625000L), - T(0.8437500L), - T(0.9062500L), - T(0.9921875L) - }; - - template - const T Integrator::wi[] = { - // weights for Q13 - T(1.303262173284849021810473057638590518409112513421E-01L), - T(2.390632866847646220320329836544615917290026806242E-01L), - T(2.630626354774670227333506083741355715758124943143E-01L), - T(2.186819313830574175167853094864355208948886875898E-01L), - T(2.757897646642836865859601197607471574336674206700E-02L), - T(1.055750100538458443365034879086669791305550493830E-01L), - T(1.571194260595182254168429283636656908546309467968E-02L), - // weights for Q19 - T(1.298751627936015783241173611320651866834051160074E-01L), - T(2.249996826462523640447834514709508786970828213187E-01L), - T(1.680415725925575286319046726692683040162290325505E-01L), - T(1.415567675701225879892811622832845252125600939627E-01L), - T(1.006482260551160175038684459742336605269707889822E-01L), - T(2.510604860724282479058338820428989444699235030871E-02L), - T(9.402964360009747110031098328922608224934320397592E-03L), - T(5.542699233295875168406783695143646338274805359780E-02L), - T(9.986735247403367525720377847755415293097913496236E-02L), - T(4.507523056810492466415880450799432587809828791196E-02L), - // weights for Q27 - T(6.300942249647773931746170540321811473310938661469E-02L), - T(1.261383225537664703012999637242003647020326905948E-01L), - T(1.273864433581028272878709981850307363453523117880E-01L), - T(8.576500414311820514214087864326799153427368592787E-02L), - T(7.102884842310253397447305465997026228407227220665E-02L), - T(5.026383572857942403759829860675892897279675661654E-02L), - T(4.683670010609093810432609684738393586390722052124E-03L), - T(1.235837891364555000245004813294817451524633100256E-01L), - T(1.148933497158144016800199601785309838604146040215E-01L), - T(1.252575774226122633391477702593585307254527198070E-02L), - T(1.239572396231834242194189674243818619042280816640E-01L), - T(2.501306413750310579525950767549691151739047969345E-02L), - T(4.915957918146130094258849161350510503556792927578E-02L), - T(2.259167374956474713302030584548274729936249753832E-02L), - // weights for Q41 - T(6.362762978782724559269342300509058175967124446839E-02L), - T(9.950065827346794643193261975720606296171462239514E-02L), - T(7.048220002718565366098742295389607994441704889441E-02L), - T(6.512297339398335645872697307762912795346716454337E-02L), - T(3.998229150313659724790527138690215186863915308702E-02L), - T(3.456512257080287509832054272964315588028252136044E-02L), - T(2.212167975884114432760321569298651047876071264944E-03L), - T(8.140326425945938045967829319725797511040878579808E-02L), - T(6.583213447600552906273539578430361199084485578379E-02L), - T(2.592913726450792546064232192976262988065252032902E-02L), - T(1.187141856692283347609436153545356484256869129472E-01L), - T(5.999947605385971985589674757013565610751028128731E-02L), - T(5.500937980198041736910257988346101839062581489820E-02L), - T(5.264422421764655969760271538981443718440340270116E-03L), - T(1.533126874056586959338368742803997744815413565014E-02L), - T(3.527159369750123100455704702965541866345781113903E-02L), - T(5.000556431653955124212795201196389006184693561679E-02L), - T(5.744164831179720106340717579281831675999717767532E-02L), - T(1.598823797283813438301248206397233634639162043386E-02L), - T(2.635660410220884993472478832884065450876913559421E-02L), - T(1.196003937945541091670106760660561117114584656319E-02L) - }; - - template - const natural Integrator::mw[] = { - 0, 7, 17, 31 - }; - - template - const natural Integrator::nw[] = { - 7, 10, 14, 21 - }; + /// Adds a new part to the partition. + /// + /// @param part The part. + void + add_part (P *part) + { + using std::push_heap; + + parts.push_back (part); + push_heap (parts.begin (), parts.end (), part_compare); + } + + /// Compares the absolute error of integration of two parts. + const Part_Compare

part_compare; + + /// The parts of this partition. + std::vector

parts{}; + }; + + /// The selected quadrature formula with less points. + const Formula p; + + /// The selected quadrature formula with more points. + const Formula q; + + /// The quadrature abscissa values. + static const T xi[]; + + /// The quadrature weights. + static const T wi[]; + + /// The start indices into the quadrature weights. + static const natural mw[]; + + /// The number of quadrature weights. + static const natural nw[]; +}; + +template +const T Integrator::xi[] = { + // abscissas for Q13 + T (0.0000000L), T (0.2500000L), T (0.5000000L), T (0.7500000L), + T (0.8750000L), T (0.9375000L), T (1.0000000L), + // additional abscissas for Q19, Q27 and Q41 + T (0.3750000L), T (0.6250000L), T (0.9687500L), + // additional abscissas for Q27 and Q41 + T (0.1250000L), T (0.6875000L), T (0.8125000L), T (0.9843750L), + // additional abscissas for Q41 + T (0.1875000L), T (0.3125000L), T (0.4375000L), T (0.5625000L), + T (0.8437500L), T (0.9062500L), T (0.9921875L) +}; + +template +const T Integrator::wi[] = { + // weights for Q13 + T (1.303262173284849021810473057638590518409112513421E-01L), + T (2.390632866847646220320329836544615917290026806242E-01L), + T (2.630626354774670227333506083741355715758124943143E-01L), + T (2.186819313830574175167853094864355208948886875898E-01L), + T (2.757897646642836865859601197607471574336674206700E-02L), + T (1.055750100538458443365034879086669791305550493830E-01L), + T (1.571194260595182254168429283636656908546309467968E-02L), + // weights for Q19 + T (1.298751627936015783241173611320651866834051160074E-01L), + T (2.249996826462523640447834514709508786970828213187E-01L), + T (1.680415725925575286319046726692683040162290325505E-01L), + T (1.415567675701225879892811622832845252125600939627E-01L), + T (1.006482260551160175038684459742336605269707889822E-01L), + T (2.510604860724282479058338820428989444699235030871E-02L), + T (9.402964360009747110031098328922608224934320397592E-03L), + T (5.542699233295875168406783695143646338274805359780E-02L), + T (9.986735247403367525720377847755415293097913496236E-02L), + T (4.507523056810492466415880450799432587809828791196E-02L), + // weights for Q27 + T (6.300942249647773931746170540321811473310938661469E-02L), + T (1.261383225537664703012999637242003647020326905948E-01L), + T (1.273864433581028272878709981850307363453523117880E-01L), + T (8.576500414311820514214087864326799153427368592787E-02L), + T (7.102884842310253397447305465997026228407227220665E-02L), + T (5.026383572857942403759829860675892897279675661654E-02L), + T (4.683670010609093810432609684738393586390722052124E-03L), + T (1.235837891364555000245004813294817451524633100256E-01L), + T (1.148933497158144016800199601785309838604146040215E-01L), + T (1.252575774226122633391477702593585307254527198070E-02L), + T (1.239572396231834242194189674243818619042280816640E-01L), + T (2.501306413750310579525950767549691151739047969345E-02L), + T (4.915957918146130094258849161350510503556792927578E-02L), + T (2.259167374956474713302030584548274729936249753832E-02L), + // weights for Q41 + T (6.362762978782724559269342300509058175967124446839E-02L), + T (9.950065827346794643193261975720606296171462239514E-02L), + T (7.048220002718565366098742295389607994441704889441E-02L), + T (6.512297339398335645872697307762912795346716454337E-02L), + T (3.998229150313659724790527138690215186863915308702E-02L), + T (3.456512257080287509832054272964315588028252136044E-02L), + T (2.212167975884114432760321569298651047876071264944E-03L), + T (8.140326425945938045967829319725797511040878579808E-02L), + T (6.583213447600552906273539578430361199084485578379E-02L), + T (2.592913726450792546064232192976262988065252032902E-02L), + T (1.187141856692283347609436153545356484256869129472E-01L), + T (5.999947605385971985589674757013565610751028128731E-02L), + T (5.500937980198041736910257988346101839062581489820E-02L), + T (5.264422421764655969760271538981443718440340270116E-03L), + T (1.533126874056586959338368742803997744815413565014E-02L), + T (3.527159369750123100455704702965541866345781113903E-02L), + T (5.000556431653955124212795201196389006184693561679E-02L), + T (5.744164831179720106340717579281831675999717767532E-02L), + T (1.598823797283813438301248206397233634639162043386E-02L), + T (2.635660410220884993472478832884065450876913559421E-02L), + T (1.196003937945541091670106760660561117114584656319E-02L) +}; + +template const natural Integrator::mw[] = { 0, 7, 17, 31 }; + +template const natural Integrator::nw[] = { 7, 10, 14, 21 }; } #endif // INTEGRATOR_H diff --git a/src/main/cxx/core/model.h b/src/main/cxx/core/model.h index c8edc99..dd27114 100644 --- a/src/main/cxx/core/model.h +++ b/src/main/cxx/core/model.h @@ -23,623 +23,731 @@ #include "readline.h" #include "section.h" -namespace especia { - +namespace especia +{ + +/// +/// @todo - the whole class needs a cleanup. +template class Model +{ +public: + /// A bounded constraint. + /// + /// @tparam T The number type. + template class Bounded_Constraint + { + public: + /// Constructs a new strict-bound prior constraint. /// - /// @todo - the whole class needs a cleanup. - template - class Model { - public: - - /// A bounded constraint. - /// - /// @tparam T The number type. - template - class Bounded_Constraint { - public: - /// Constructs a new strict-bound prior constraint. - /// - /// @param[in] lower_bounds The lower bounds. - /// @param[in] upper_bounds The upper bounds. - /// @param[in] n The number of bounds. - Bounded_Constraint(const T lower_bounds[], const T upper_bounds[], natural n) - : a(lower_bounds, n), b(upper_bounds, n) { - } - - /// The destructor. - ~Bounded_Constraint() = default; - - /// Tests if a given parameter vector violates the constraint. - /// - /// @param[in] x The parameter vector. - /// @param[in] n The number of parameters to test. - /// @return @c true, if the parameter vector violates the constraint. - bool is_violated(const T x[], natural n) const { - for (natural i = 0; i < n; ++i) { - if (x[i] < a[i] || x[i] > b[i]) { - return true; - } - } - return false; - } - - /// Computes the cost associated with the constraint. - /// - /// @param[in] x The parameter vector. - /// @param[in] n The number of parameters to take account of. - /// @return always zero. - T cost(const T x[], natural n) const { - return T(0); - } - - private: - const std::valarray a; - const std::valarray b; - }; - - std::istream &get(std::istream &is, - std::ostream &os, - char comment_mark = '%', - char begin_of_section = '{', - char end_of_section = '}') { - using namespace std; - - typedef map::const_iterator id_index_map_ci; - - const char errmsg[] = "especia::Model<>::get(): Error: "; - const char dlimsg[] = "duplicate line identifier"; - const char dsimsg[] = "duplicate section identifier"; - const char fnfmsg[] = "file not found"; - const char infmsg[] = "input failed"; - const char srfmsg[] = "self reference"; - const char synmsg[] = "syntax error"; - const char rnfmsg[] = "reference not found"; - - vector sections; - - vector isc; - vector nle; - vector nli; - - vector val; - vector lo; - vector up; - - vector msk; - vector ind; - - map profile_name_map; - map section_name_map; - - vector ref; - - stringstream ss; - stringstream st; - string line; - - os << "\n"; - os << "\n"; - os << "\n"; - os << "\n"; - - // Now strip empty lines and comments - while (readline(ss, line, comment_mark)) { - st << line << '\n'; + /// @param[in] lower_bounds The lower bounds. + /// @param[in] upper_bounds The upper bounds. + /// @param[in] n The number of bounds. + Bounded_Constraint (const T lower_bounds[], const T upper_bounds[], + natural n) + : a (lower_bounds, n), b (upper_bounds, n) + { + } + + /// The destructor. + ~Bounded_Constraint () = default; + + /// Tests if a given parameter vector violates the constraint. + /// + /// @param[in] x The parameter vector. + /// @param[in] n The number of parameters to test. + /// @return @c true, if the parameter vector violates the constraint. + bool + is_violated (const T x[], natural n) const + { + for (natural i = 0; i < n; ++i) + { + if (x[i] < a[i] || x[i] > b[i]) + { + return true; } + } + return false; + } - natural i = 0; - size_t j = 0; - - while (getline(st, line, end_of_section)) - if (!st.eof()) { - j = line.find(begin_of_section); - - if (j != string::npos) { - istringstream ist(line.substr(j + 1)); - - string sid, pid, fn, s2; - real a, b; - natural p; - - // Parse section head - if (ist >> sid >> fn >> a >> b >> p and getline(ist, s2)) { - if (section_name_map.find(sid) == section_name_map.end()) { - section_name_map[sid] = sections.size(); - - ifstream ifs(fn.c_str()); - - if (ifs) { - especia::Section s; - - if (s.get(ifs, a, b)) { - istringstream is2(s2); - while (is2 >> a >> b) - s.mask(a, b); - - sections.push_back(s); - isc.push_back(i); - nle.push_back(p); - } else { - is.setstate(ios_base::badbit | ios_base::failbit); - cerr << errmsg << fn << ": " << infmsg << endl; - - return is; - } - } else { - is.setstate(ios_base::badbit | ios_base::failbit); - cerr << errmsg << fn << ": " << fnfmsg << endl; - - return is; - } - } else { - is.setstate(ios_base::badbit | ios_base::failbit); - cerr << errmsg << sid << ": " << dsimsg << endl; - - return is; + /// Computes the cost associated with the constraint. + /// + /// @param[in] x The parameter vector. + /// @param[in] n The number of parameters to take account of. + /// @return always zero. + T + cost (const T x[], natural n) const + { + return T (0); + } + + private: + const std::valarray a; + const std::valarray b; + }; + + std::istream & + get (std::istream &is, std::ostream &os, char comment_mark = '%', + char begin_of_section = '{', char end_of_section = '}') + { + using namespace std; + + typedef map::const_iterator id_index_map_ci; + + const char errmsg[] = "especia::Model<>::get(): Error: "; + const char dlimsg[] = "duplicate line identifier"; + const char dsimsg[] = "duplicate section identifier"; + const char fnfmsg[] = "file not found"; + const char infmsg[] = "input failed"; + const char srfmsg[] = "self reference"; + const char synmsg[] = "syntax error"; + const char rnfmsg[] = "reference not found"; + + vector sections; + + vector isc; + vector nle; + vector nli; + + vector val; + vector lo; + vector up; + + vector msk; + vector ind; + + map profile_name_map; + map section_name_map; + + vector ref; + + stringstream ss; + stringstream st; + string line; + + os << "\n"; + os << "\n"; + os << "\n"; + os << "\n"; + + // Now strip empty lines and comments + while (readline (ss, line, comment_mark)) + { + st << line << '\n'; + } + + natural i = 0; + size_t j = 0; + + while (getline (st, line, end_of_section)) + if (!st.eof ()) + { + j = line.find (begin_of_section); + + if (j != string::npos) + { + istringstream ist (line.substr (j + 1)); + + string sid, pid, fn, s2; + real a, b; + natural p; + + // Parse section head + if (ist >> sid >> fn >> a >> b >> p and getline (ist, s2)) + { + if (section_name_map.find (sid) == section_name_map.end ()) + { + section_name_map[sid] = sections.size (); + + ifstream ifs (fn.c_str ()); + + if (ifs) + { + especia::Section s; + + if (s.get (ifs, a, b)) + { + istringstream is2 (s2); + while (is2 >> a >> b) + s.mask (a, b); + + sections.push_back (s); + isc.push_back (i); + nle.push_back (p); } - } else { - is.setstate(ios_base::badbit | ios_base::failbit); - cerr << errmsg << infmsg << endl; + else + { + is.setstate (ios_base::badbit + | ios_base::failbit); + cerr << errmsg << fn << ": " << infmsg << endl; - return is; + return is; + } } + else + { + is.setstate (ios_base::badbit | ios_base::failbit); + cerr << errmsg << fn << ": " << fnfmsg << endl; - // Read resolution parameter specification - if (read(ist, val, lo, up, msk, ref, 1, '\n', true)) - ++i; - else { - is.setstate(ios_base::badbit | ios_base::failbit); - cerr << errmsg << infmsg << endl; - - return is; + return is; } - - natural k = 0; - - // Read profile function parameter specification - while (ist >> pid) - if (profile_name_map.find(pid) == profile_name_map.end()) { - profile_name_map[pid] = i; - - if (read(ist, val, lo, up, msk, ref, Function::parameter_count(), '\n', true)) { - i += Function::parameter_count(); - k += 1; - } else { - is.setstate(ios_base::badbit | ios_base::failbit); - cerr << errmsg << infmsg << endl; - - return is; - } - } else { - is.setstate(ios_base::badbit | ios_base::failbit); - cerr << errmsg << pid << ": " << dlimsg << endl; - - return is; - } - - nli.push_back(k); - } else { - is.setstate(ios_base::badbit | ios_base::failbit); - cerr << errmsg << synmsg << endl; - - return is; } - } - - if (!st.bad() and st.eof()) { - // Index independent parameters - for (natural i = 0, k = 0; i < msk.size(); ++i) - if (msk[i] and ref[i].empty()) { - if (lo[i] > up[i]) - swap(lo[i], up[i]); - - ind.push_back(k++); - } else { - lo[i] = 0.0; - up[i] = 0.0; - - ind.push_back(0); - } - - // Dereference resolution parameter references - for (id_index_map_ci i = section_name_map.begin(); i != section_name_map.end(); ++i) { - const natural j = isc[i->second]; - - while (!ref[j].empty()) { - if (section_name_map.find(ref[j]) != section_name_map.end()) { - const natural k = isc[section_name_map[ref[j]]]; - - if (j != k) { - if (ref[k].empty()) { - val[j] = val[k]; + else + { + is.setstate (ios_base::badbit | ios_base::failbit); + cerr << errmsg << sid << ": " << dsimsg << endl; - lo[j] = lo[k]; - up[j] = up[k]; - - msk[j] = msk[k]; - ind[j] = ind[k]; - } - ref[j] = ref[k]; - } else { - is.setstate(ios_base::failbit); - cerr << errmsg << ref[j] << srfmsg << endl; - - return is; - } - } else { - is.setstate(ios_base::failbit); - cerr << errmsg << ref[j] << rnfmsg << endl; - - return is; - } + return is; } } + else + { + is.setstate (ios_base::badbit | ios_base::failbit); + cerr << errmsg << infmsg << endl; - // Dereference line parameter references - for (id_index_map_ci i = profile_name_map.begin(); i != profile_name_map.end(); ++i) - for (natural j = 0; j < Function::parameter_count(); ++j) { - const natural k = i->second + j; - - while (!ref[k].empty()) { - if (profile_name_map.find(ref[k]) != profile_name_map.end()) { - const natural l = profile_name_map[ref[k]] + j; - - if (k != l) { - if (ref[l].empty()) { - val[k] = val[l]; - - lo[k] = lo[l]; - up[k] = up[l]; - - msk[k] = msk[l]; - ind[k] = ind[l]; - } - ref[k] = ref[l]; - } else { - is.setstate(ios_base::failbit); - cerr << errmsg << ref[j] << srfmsg << endl; - - return is; - } - } else { - is.setstate(ios_base::failbit); - cerr << errmsg << ref[j] << rnfmsg << endl; - - return is; - } - } - } - - const natural m = sections.size(); - const natural n = msk.size(); - - this->sections = sections; - - this->isc.resize(m); - this->nle.resize(m); - this->nli.resize(m); - - copy(isc.begin(), isc.end(), &(this->isc[0])); - copy(nle.begin(), nle.end(), &(this->nle[0])); - copy(nli.begin(), nli.end(), &(this->nli[0])); - - this->val.resize(n); - this->err.resize(n); - - copy(val.begin(), val.end(), &(this->val[0])); - - this->lo.resize(n); - this->up.resize(n); - - copy(lo.begin(), lo.end(), &(this->lo[0])); - copy(up.begin(), up.end(), &(this->up[0])); - - this->msk.resize(n); - this->ind.resize(n); - - copy(msk.begin(), msk.end(), &(this->msk[0])); - copy(ind.begin(), ind.end(), &(this->ind[0])); - - this->section_name_map = section_name_map; - this->profile_name_map = profile_name_map; - - is.clear(is.rdstate() & ~ios_base::failbit); - } else - is.setstate(ios_base::badbit | ios_base::failbit); - - return is; - } - - std::ostream &put(std::ostream &os) const { - using namespace std; - - typedef map::const_iterator id_index_map_ci; - - const ios_base::fmtflags fmt = os.flags(); - - os.setf(ios_base::fmtflags()); - os.setf(ios_base::fixed, ios_base::floatfield); - os.setf(ios_base::left, ios_base::adjustfield); - - os << "\n"; - os << "\n"; - os << "\n"; - - os << "\n"; - os << " Parameter Table\n"; - os << "\n"; - os << "\n"; - os << "\n"; - os << " \n"; - os << " \n"; - os << " \n"; - os << " \n"; - os << " \n"; - os << " \n"; - os << " \n"; - os << " \n"; - os << " \n"; - os << " \n"; - os << " \n"; - os << " \n"; - os << " \n"; - - for (auto i = section_name_map.begin(); i != section_name_map.end(); ++i) { - const natural j = i->second; - - const string id = i->first; - const size_t px = sections[j].valid_data_count(); - const real st = sections[j].cost(); - - os.precision(2); - - os << " \n"; - os << " \n"; - os << " \n"; - os << " \n"; - os << " \n"; - os << " \n"; - os << " \n"; - os << " \n"; - os << " \n"; - os << " \n"; - } - - os << " \n"; - os << "
SectionStart
Wavelength
(Å)
End
Wavelength
(Å)
Legendre Basis
Polynomials
Resolution
(103)
Data PointsCostCost per
Data Point
" << id << "" << sections[j].lower_bound() << "" << sections[j].upper_bound() << "" << nle[j] << ""; - put_parameter(os, ios_base::fixed, 2, isc[j]); - os << "" << px << "" << st << "" << st / px << "
\n"; - os << "
\n"; - os << "\n"; - os << " \n"; - os << " \n"; - os << " \n"; - os << " \n"; - os << " \n"; - os << " \n"; - os << " \n"; - os << " \n"; - os << " \n"; - os << " \n"; - os << " \n"; - if (Function::parameter_count() == 8) { - os << " \n"; - } - os << " \n"; - os << " \n"; - os << " \n"; - - const Equivalent_Width_Calculator> calculator; - - for (auto i = profile_name_map.begin(); i != profile_name_map.end(); ++i) { - const natural j = i->second; - const string id = i->first; - - const real c = 1.0E-3 * speed_of_light; - const real x = val[j]; - const real z = val[j + 2]; - const real v = val[j + 3]; - const real w = x * (1.0 + z) * (1.0 + v / c); - const real dx = err[j]; - const real dz = err[j + 2]; - const real dv = err[j + 3]; - const real dw = dx + x * sqrt(sq((1.0 + v / c) * dz) + sq((1.0 + z) * dv / c)); - const real ew = calculator.calculate(Function(&val[j]), milli); - - os.precision(4); - - os << " \n"; - os << " \n"; - os << " \n"; - os << " \n"; - os << " \n"; - os << " \n"; - os << " \n"; - os << " \n"; - os << " \n"; - os << " \n"; - if (Function::parameter_count() == 8) { - os << " \n"; + return is; } - os << " \n"; - } - os << " \n"; - os << "
LineObserved
Wavelength
(Å)
Rest
Wavelength
(Å)
Oscillator
Strength
RedshiftRadial
Velocity
(km s-1)
Broadening
Velocity
(km s-1)
Log. Column
Density
(cm-2)
Equivalent
Width
(mÅ)
Δα/α
(10-6)
" << id << "" << w << " ± " << dw << ""; - put_parameter(os, ios_base::fixed, 4, j); - os << ""; - put_parameter(os, ios_base::scientific, 3, j + 1); - os << ""; - put_parameter(os, ios_base::fixed, 7, j + 2); - os << ""; - put_parameter(os, ios_base::fixed, 3, j + 3); - os << ""; - put_parameter(os, ios_base::fixed, 3, j + 4); - os << ""; - put_parameter(os, ios_base::fixed, 3, j + 5); - os << ""; - put_parameter(os, ios_base::fixed, 3, ew); - os << ""; - put_parameter(os, ios_base::fixed, 3, j + 7); - os << "
\n"; + // Read resolution parameter specification + if (read (ist, val, lo, up, msk, ref, 1, '\n', true)) + ++i; + else + { + is.setstate (ios_base::badbit | ios_base::failbit); + cerr << errmsg << infmsg << endl; - os << "

" << endl; - os << " Created by " << project_title << ". " << project_doi_html << "
" << endl; - os << " " << project_long_name << "
" << endl; - os << " " << system_name << "
" << endl; - os << " " << cxx_compiler << " " << cxx_compiler_version << "
" << endl; - os << "
" << endl; - - os << "\n"; - os << "\n"; + return is; + } - os.flush(); - os.flags(fmt); + natural k = 0; + + // Read profile function parameter specification + while (ist >> pid) + if (profile_name_map.find (pid) == profile_name_map.end ()) + { + profile_name_map[pid] = i; + + if (read (ist, val, lo, up, msk, ref, + Function::parameter_count (), '\n', true)) + { + i += Function::parameter_count (); + k += 1; + } + else + { + is.setstate (ios_base::badbit | ios_base::failbit); + cerr << errmsg << infmsg << endl; - return os; - } + return is; + } + } + else + { + is.setstate (ios_base::badbit | ios_base::failbit); + cerr << errmsg << pid << ": " << dlimsg << endl; - real operator()(const real x[], natural n) const { - return cost(x, n); - } + return is; + } - void set(const real x[], const real z[]) { - for (natural i = 0; i < val.size(); ++i) { - if (msk[i]) { - val[i] = x[ind[i]]; - err[i] = z[ind[i]]; - } else { - err[i] = 0.0; - } + nli.push_back (k); } - for (natural i = 0; i < sections.size(); ++i) { - sections[i].apply(nle[i], val[isc[i]], Superposition(nli[i], &val[isc[i] + 1])); - } - } + else + { + is.setstate (ios_base::badbit | ios_base::failbit); + cerr << errmsg << synmsg << endl; - real cost(const real x[], natural n) const { - using std::valarray; - - valarray y = val; - for (natural i = 0; i < y.size(); ++i) { - if (msk[i]) { - y[i] = x[ind[i]]; - } + return is; } - real d = 0.0; - for (natural i = 0; i < sections.size(); ++i) { - d += sections[i].cost(Superposition(nli[i], &y[isc[i] + 1]), y[isc[i]], nle[i]); - } - return d; - } - - natural get_parameter_count() const { - return ind.max() + 1; } - std::valarray get_initial_parameter_values() const { - std::valarray x(get_parameter_count()); + if (!st.bad () and st.eof ()) + { + // Index independent parameters + for (natural i = 0, k = 0; i < msk.size (); ++i) + if (msk[i] and ref[i].empty ()) + { + if (lo[i] > up[i]) + swap (lo[i], up[i]); - for (natural i = 0, j = 0; i < msk.size(); ++i) { - if (msk[i] and ind[i] == j) { - x[j++] = 0.5 * (lo[i] + up[i]); - } + ind.push_back (k++); } + else + { + lo[i] = 0.0; + up[i] = 0.0; - return x; - } - - std::valarray get_initial_local_step_sizes() const { - std::valarray z(get_parameter_count()); - - for (natural i = 0, j = 0; i < msk.size(); ++i) { - if (msk[i] and ind[i] == j) { - z[j++] = 0.5 * (up[i] - lo[i]); - } + ind.push_back (0); } - return z; - } + // Dereference resolution parameter references + for (id_index_map_ci i = section_name_map.begin (); + i != section_name_map.end (); ++i) + { + const natural j = isc[i->second]; + + while (!ref[j].empty ()) + { + if (section_name_map.find (ref[j]) != section_name_map.end ()) + { + const natural k = isc[section_name_map[ref[j]]]; + + if (j != k) + { + if (ref[k].empty ()) + { + val[j] = val[k]; + + lo[j] = lo[k]; + up[j] = up[k]; + + msk[j] = msk[k]; + ind[j] = ind[k]; + } + ref[j] = ref[k]; + } + else + { + is.setstate (ios_base::failbit); + cerr << errmsg << ref[j] << srfmsg << endl; + + return is; + } + } + else + { + is.setstate (ios_base::failbit); + cerr << errmsg << ref[j] << rnfmsg << endl; + + return is; + } + } + } + + // Dereference line parameter references + for (id_index_map_ci i = profile_name_map.begin (); + i != profile_name_map.end (); ++i) + for (natural j = 0; j < Function::parameter_count (); ++j) + { + const natural k = i->second + j; + + while (!ref[k].empty ()) + { + if (profile_name_map.find (ref[k]) + != profile_name_map.end ()) + { + const natural l = profile_name_map[ref[k]] + j; + + if (k != l) + { + if (ref[l].empty ()) + { + val[k] = val[l]; + + lo[k] = lo[l]; + up[k] = up[l]; + + msk[k] = msk[l]; + ind[k] = ind[l]; + } + ref[k] = ref[l]; + } + else + { + is.setstate (ios_base::failbit); + cerr << errmsg << ref[j] << srfmsg << endl; - Bounded_Constraint get_constraint() const { - std::valarray a(get_parameter_count()); - std::valarray b(get_parameter_count()); + return is; + } + } + else + { + is.setstate (ios_base::failbit); + cerr << errmsg << ref[j] << rnfmsg << endl; - for (natural i = 0, j = 0; i < msk.size(); ++i) { - if (msk[i] and ind[i] == j) { - a[j] = lo[i]; - b[j] = up[i]; - ++j; + return is; + } } } - return Bounded_Constraint(&a[0], &b[0], get_parameter_count()); - } - - private: - std::ostream &put_parameter(std::ostream &os, std::ios_base::fmtflags f, natural p, real parameter) const { - using namespace std; - - const ios_base::fmtflags fmt = os.flags(); - - os.setf(f, ios_base::floatfield); - os.precision(p); - - os << parameter; - - os.flags(fmt); - - return os; - } - - std::ostream &put_parameter(std::ostream &os, std::ios_base::fmtflags f, natural p, natural parameter_index) const { - using namespace std; - - const ios_base::fmtflags fmt = os.flags(); - - os.setf(f, ios_base::floatfield); - os.precision(p); - - os << val[parameter_index]; - if (msk[parameter_index]) - os << " ± " << err[parameter_index]; - - os.flags(fmt); - - return os; - } - - std::vector sections; - - std::valarray isc; - std::valarray nle; - std::valarray nli; - - std::valarray val; - std::valarray err; - std::valarray lo; - std::valarray up; - - std::valarray msk; - std::valarray ind; - - std::map section_name_map; - std::map profile_name_map; - }; + const natural m = sections.size (); + const natural n = msk.size (); + + this->sections = sections; + + this->isc.resize (m); + this->nle.resize (m); + this->nli.resize (m); + + copy (isc.begin (), isc.end (), &(this->isc[0])); + copy (nle.begin (), nle.end (), &(this->nle[0])); + copy (nli.begin (), nli.end (), &(this->nli[0])); + + this->val.resize (n); + this->err.resize (n); + + copy (val.begin (), val.end (), &(this->val[0])); + + this->lo.resize (n); + this->up.resize (n); + + copy (lo.begin (), lo.end (), &(this->lo[0])); + copy (up.begin (), up.end (), &(this->up[0])); + + this->msk.resize (n); + this->ind.resize (n); + + copy (msk.begin (), msk.end (), &(this->msk[0])); + copy (ind.begin (), ind.end (), &(this->ind[0])); + + this->section_name_map = section_name_map; + this->profile_name_map = profile_name_map; + + is.clear (is.rdstate () & ~ios_base::failbit); + } + else + is.setstate (ios_base::badbit | ios_base::failbit); + + return is; + } + + std::ostream & + put (std::ostream &os) const + { + using namespace std; + + typedef map::const_iterator id_index_map_ci; + + const ios_base::fmtflags fmt = os.flags (); + + os.setf (ios_base::fmtflags ()); + os.setf (ios_base::fixed, ios_base::floatfield); + os.setf (ios_base::left, ios_base::adjustfield); + + os << "\n"; + os << "\n"; + os << "\n"; + + os << "\n"; + os << " Parameter Table\n"; + os << "\n"; + os << "\n"; + os << "\n"; + os << " \n"; + os << " \n"; + os << " \n"; + os << " \n"; + os << " \n"; + os << " \n"; + os << " \n"; + os << " \n"; + os << " \n"; + os << " \n"; + os << " \n"; + os << " \n"; + os << " \n"; + + for (auto i = section_name_map.begin (); i != section_name_map.end (); ++i) + { + const natural j = i->second; + + const string id = i->first; + const size_t px = sections[j].valid_data_count (); + const real st = sections[j].cost (); + + os.precision (2); + + os << " \n"; + os << " \n"; + os << " \n"; + os << " \n"; + os << " \n"; + os << " \n"; + os << " \n"; + os << " \n"; + os << " \n"; + os << " \n"; + } + + os << " \n"; + os << "
SectionStart
Wavelength
(Å)
End
Wavelength
(Å)
Legendre Basis
Polynomials
Resolution
(103)
Data PointsCostCost per
Data Point
" << id << "" << sections[j].lower_bound () << "" << sections[j].upper_bound () << "" << nle[j] << ""; + put_parameter (os, ios_base::fixed, 2, isc[j]); + os << "" << px << "" << st << "" << st / px << "
\n"; + os << "
\n"; + os << "\n"; + os << " \n"; + os << " \n"; + os << " \n"; + os << " \n"; + os << " \n"; + os << " \n"; + os << " \n"; + os << " \n"; + os << " \n"; + os << " \n"; + os << " \n"; + if (Function::parameter_count () == 8) + { + os << " \n"; + } + os << " \n"; + os << " \n"; + os << " \n"; + + const Equivalent_Width_Calculator > calculator; + + for (auto i = profile_name_map.begin (); i != profile_name_map.end (); ++i) + { + const natural j = i->second; + const string id = i->first; + + const real c = 1.0E-3 * speed_of_light; + const real x = val[j]; + const real z = val[j + 2]; + const real v = val[j + 3]; + const real w = x * (1.0 + z) * (1.0 + v / c); + const real dx = err[j]; + const real dz = err[j + 2]; + const real dv = err[j + 3]; + const real dw + = dx + + x * sqrt (sq ((1.0 + v / c) * dz) + sq ((1.0 + z) * dv / c)); + const real ew = calculator.calculate (Function (&val[j]), milli); + + os.precision (4); + + os << " \n"; + os << " \n"; + os << " \n"; + os << " \n"; + os << " \n"; + os << " \n"; + os << " \n"; + os << " \n"; + os << " \n"; + os << " \n"; + if (Function::parameter_count () == 8) + { + os << " \n"; + } + os << " \n"; + } + + os << " \n"; + os << "
LineObserved
Wavelength
(Å)
Rest
Wavelength
(Å)
Oscillator
Strength
RedshiftRadial
Velocity
(km s-1)
Broadening
Velocity
(km s-1)
Log. Column
Density
(cm-2)
Equivalent
Width
(mÅ)
Δα/α
(10-6)
" << id << "" << w << " ± " << dw << ""; + put_parameter (os, ios_base::fixed, 4, j); + os << ""; + put_parameter (os, ios_base::scientific, 3, j + 1); + os << ""; + put_parameter (os, ios_base::fixed, 7, j + 2); + os << ""; + put_parameter (os, ios_base::fixed, 3, j + 3); + os << ""; + put_parameter (os, ios_base::fixed, 3, j + 4); + os << ""; + put_parameter (os, ios_base::fixed, 3, j + 5); + os << ""; + put_parameter (os, ios_base::fixed, 3, ew); + os << ""; + put_parameter (os, ios_base::fixed, 3, j + 7); + os << "
\n"; + + os << "
" << endl; + os << " Created by " << project_title << ". " + << project_doi_html << "
" << endl; + os << " " << project_long_name << "
" << endl; + os << " " << system_name << "
" << endl; + os << " " << cxx_compiler << " " << cxx_compiler_version << "
" << endl; + os << "
" << endl; + + os << "\n"; + os << "\n"; + + os.flush (); + os.flags (fmt); + + return os; + } + + real + operator() (const real x[], natural n) const + { + return cost (x, n); + } + + void + set (const real x[], const real z[]) + { + for (natural i = 0; i < val.size (); ++i) + { + if (msk[i]) + { + val[i] = x[ind[i]]; + err[i] = z[ind[i]]; + } + else + { + err[i] = 0.0; + } + } + for (natural i = 0; i < sections.size (); ++i) + { + sections[i].apply (nle[i], val[isc[i]], + Superposition (nli[i], &val[isc[i] + 1])); + } + } + + real + cost (const real x[], natural n) const + { + using std::valarray; + + valarray y = val; + for (natural i = 0; i < y.size (); ++i) + { + if (msk[i]) + { + y[i] = x[ind[i]]; + } + } + real d = 0.0; + for (natural i = 0; i < sections.size (); ++i) + { + d += sections[i].cost ( + Superposition (nli[i], &y[isc[i] + 1]), y[isc[i]], + nle[i]); + } + return d; + } + + natural + get_parameter_count () const + { + return ind.max () + 1; + } + + std::valarray + get_initial_parameter_values () const + { + std::valarray x (get_parameter_count ()); + + for (natural i = 0, j = 0; i < msk.size (); ++i) + { + if (msk[i] and ind[i] == j) + { + x[j++] = 0.5 * (lo[i] + up[i]); + } + } + + return x; + } + + std::valarray + get_initial_local_step_sizes () const + { + std::valarray z (get_parameter_count ()); + + for (natural i = 0, j = 0; i < msk.size (); ++i) + { + if (msk[i] and ind[i] == j) + { + z[j++] = 0.5 * (up[i] - lo[i]); + } + } + + return z; + } + + Bounded_Constraint + get_constraint () const + { + std::valarray a (get_parameter_count ()); + std::valarray b (get_parameter_count ()); + + for (natural i = 0, j = 0; i < msk.size (); ++i) + { + if (msk[i] and ind[i] == j) + { + a[j] = lo[i]; + b[j] = up[i]; + ++j; + } + } + + return Bounded_Constraint (&a[0], &b[0], get_parameter_count ()); + } + +private: + std::ostream & + put_parameter (std::ostream &os, std::ios_base::fmtflags f, natural p, + real parameter) const + { + using namespace std; + + const ios_base::fmtflags fmt = os.flags (); + + os.setf (f, ios_base::floatfield); + os.precision (p); + + os << parameter; + + os.flags (fmt); + + return os; + } + + std::ostream & + put_parameter (std::ostream &os, std::ios_base::fmtflags f, natural p, + natural parameter_index) const + { + using namespace std; + + const ios_base::fmtflags fmt = os.flags (); + + os.setf (f, ios_base::floatfield); + os.precision (p); + + os << val[parameter_index]; + if (msk[parameter_index]) + os << " ± " << err[parameter_index]; + + os.flags (fmt); + + return os; + } + + std::vector sections; + + std::valarray isc; + std::valarray nle; + std::valarray nli; + + std::valarray val; + std::valarray err; + std::valarray lo; + std::valarray up; + + std::valarray msk; + std::valarray ind; + + std::map section_name_map; + std::map profile_name_map; +}; } diff --git a/src/main/cxx/core/optimize.h b/src/main/cxx/core/optimize.h index affa79f..6b88360 100644 --- a/src/main/cxx/core/optimize.h +++ b/src/main/cxx/core/optimize.h @@ -15,347 +15,389 @@ #include "base.h" -namespace especia { +namespace especia +{ - /// An indirect comparing of indexes. - /// - /// @tparam T The base value type. - /// @tparam Compare The strategy to compare base values directly. - template - class Index_Compare { - public: - /// Constructs a new index comparing. - /// - /// @param[in] v The base values. - /// @param[in] c The direct base value comparing. - Index_Compare(const std::valarray &v, const Compare &c) - : values(v), compare(c) { - } +/// An indirect comparing of indexes. +/// +/// @tparam T The base value type. +/// @tparam Compare The strategy to compare base values directly. +template class Index_Compare +{ +public: + /// Constructs a new index comparing. + /// + /// @param[in] v The base values. + /// @param[in] c The direct base value comparing. + Index_Compare (const std::valarray &v, const Compare &c) + : values (v), compare (c) + { + } - /// The destructor. - ~Index_Compare() = default; + /// The destructor. + ~Index_Compare () = default; - /// The index comparing operator. - /// - /// @param[in] i An index into the set of base values. - /// @param[in] j An index into the set of base values. - /// @return the result of comparing the indexed base values directly. - bool operator()(const natural &i, const natural &j) const { - return compare(values[i], values[j]); - } + /// The index comparing operator. + /// + /// @param[in] i An index into the set of base values. + /// @param[in] j An index into the set of base values. + /// @return the result of comparing the indexed base values directly. + bool + operator() (const natural &i, const natural &j) const + { + return compare (values[i], values[j]); + } - private: - const std::valarray &values; - const Compare &compare; - }; +private: + const std::valarray &values; + const Compare &compare; +}; - /// Evolution strategy with covariance matrix adaption (CMA-ES) for nonlinear function optimization. - /// Based on Hansen (2014, http://cma.gforge.inria.fr/purecmaes.m). - /// - /// Further reading: - /// - /// N. Hansen, S. D. Müller, P. Koumoutsakos (2003). - /// *Reducing the Increasing the Time Complexity of the Derandomized Evolution - /// Strategy with Covariance Matrix Adaption (CMA-ES).* - /// Evolutionary Computation, 11, 1, ISSN 1063-6560. - /// - /// N. Hansen, A. Ostermeier (2001). - /// *Completely Derandomized Self-Adaption in Evolution Strategies.* - /// Evolutionary Computation, 9, 159, ISSN 1063-6560. - /// - /// @tparam F The function type. - /// @tparam Constraint The constraint type. - /// @tparam Deviate The strategy to generate random normal deviates. - /// @tparam Decompose The strategy to perform the symmetric eigenvalue decomposition. - /// @tparam Compare The strategy to compare fitness. - /// @tparam Tracing The tracer type. - /// - /// @param[in] f The model function. - /// @param[in] constraint The prior constraint on the parameter values. - /// @param[in] n The number of parameters. - /// @param[in] parent_number The number of parents per generation. - /// @param[in] population_size The number of individuals per generation. Twice the parent number, at least - /// @param[in] w The recombination weights. - /// @param[in] step_size_damping The step size damping. - /// @param[in] cs The step size cumulation rate. - /// @param[in] cc The distribution cumulation rate. - /// @param[in] ccov The rank-1 covariance matrix adaption rate. - /// @param[in] acov The rank-µ covariance matrix adaption rate. - /// @param[in] update_modulus The covariance matrix update modulus. - /// @param[in] accuracy_goal The accuracy goal. - /// @param[in] stop_generation The stop generation. - /// @param[in,out] g The generation number. - /// @param[in,out] xw The parameter values. - /// @param[in,out] step_size The global step size. - /// @param[in,out] d The local step sizes. - /// @param[in,out] B The rotation matrix (in column-major layout). - /// @param[in,out] C The covariance matrix (upper triangular part only, in column-major layout). - /// @param[in,out] ps The step size cumulation path. - /// @param[in,out] pc The distribution cumulation path. - /// @param[out] yw The fitness at @c xw. - /// @param[out] optimized Set to @c true when the optimization has converged. - /// @param[out] underflow Set to @c true when the mutation variance is too small. - /// @param[in] deviate The random number generator. - /// @param[in] decompose The eigenvalue decomposition. - /// @param[in] compare The comparator to compare fitness. - /// @param[in] tracer The tracer. - template - void optimize(const F &f, - const Constraint &constraint, - natural n, - natural parent_number, - natural population_size, - const real w[], - real step_size_damping, - real cs, - real cc, - real ccov, - real acov, - natural update_modulus, - real accuracy_goal, - natural stop_generation, - natural &g, - real xw[], - real &step_size, - real d[], - real B[], - real C[], - real ps[], - real pc[], - real &yw, - bool &optimized, - bool &underflow, - const Deviate &deviate, const Decompose &decompose, const Compare &compare, const Tracing &tracer) { - using std::accumulate; - using std::exp; - using std::numeric_limits; - using std::partial_sort; - using std::sqrt; - using std::thread; - using std::valarray; - using std::vector; +/// Evolution strategy with covariance matrix adaption (CMA-ES) for nonlinear +/// function optimization. Based on Hansen (2014, +/// http://cma.gforge.inria.fr/purecmaes.m). +/// +/// Further reading: +/// +/// N. Hansen, S. D. Müller, P. Koumoutsakos (2003). +/// *Reducing the Increasing the Time Complexity of the Derandomized Evolution +/// Strategy with Covariance Matrix Adaption (CMA-ES).* +/// Evolutionary Computation, 11, 1, ISSN 1063-6560. +/// +/// N. Hansen, A. Ostermeier (2001). +/// *Completely Derandomized Self-Adaption in Evolution Strategies.* +/// Evolutionary Computation, 9, 159, ISSN 1063-6560. +/// +/// @tparam F The function type. +/// @tparam Constraint The constraint type. +/// @tparam Deviate The strategy to generate random normal deviates. +/// @tparam Decompose The strategy to perform the symmetric eigenvalue +/// decomposition. +/// @tparam Compare The strategy to compare fitness. +/// @tparam Tracing The tracer type. +/// +/// @param[in] f The model function. +/// @param[in] constraint The prior constraint on the parameter values. +/// @param[in] n The number of parameters. +/// @param[in] parent_number The number of parents per generation. +/// @param[in] population_size The number of individuals per generation. Twice +/// the parent number, at least +/// @param[in] w The recombination weights. +/// @param[in] step_size_damping The step size damping. +/// @param[in] cs The step size cumulation rate. +/// @param[in] cc The distribution cumulation rate. +/// @param[in] ccov The rank-1 covariance matrix adaption rate. +/// @param[in] acov The rank-µ covariance matrix adaption rate. +/// @param[in] update_modulus The covariance matrix update modulus. +/// @param[in] accuracy_goal The accuracy goal. +/// @param[in] stop_generation The stop generation. +/// @param[in,out] g The generation number. +/// @param[in,out] xw The parameter values. +/// @param[in,out] step_size The global step size. +/// @param[in,out] d The local step sizes. +/// @param[in,out] B The rotation matrix (in column-major layout). +/// @param[in,out] C The covariance matrix (upper triangular part only, in +/// column-major layout). +/// @param[in,out] ps The step size cumulation path. +/// @param[in,out] pc The distribution cumulation path. +/// @param[out] yw The fitness at @c xw. +/// @param[out] optimized Set to @c true when the optimization has converged. +/// @param[out] underflow Set to @c true when the mutation variance is too +/// small. +/// @param[in] deviate The random number generator. +/// @param[in] decompose The eigenvalue decomposition. +/// @param[in] compare The comparator to compare fitness. +/// @param[in] tracer The tracer. +template +void +optimize (const F &f, const Constraint &constraint, natural n, + natural parent_number, natural population_size, const real w[], + real step_size_damping, real cs, real cc, real ccov, real acov, + natural update_modulus, real accuracy_goal, natural stop_generation, + natural &g, real xw[], real &step_size, real d[], real B[], real C[], + real ps[], real pc[], real &yw, bool &optimized, bool &underflow, + const Deviate &deviate, const Decompose &decompose, + const Compare &compare, const Tracing &tracer) +{ + using std::accumulate; + using std::exp; + using std::numeric_limits; + using std::partial_sort; + using std::sqrt; + using std::thread; + using std::valarray; + using std::vector; - const real expected_norm = (n - 0.25 + 1.0 / (21 * n)) / sqrt(real(n)); - const real max_covariance_matrix_condition = 0.01 / numeric_limits::epsilon(); - const real csu = sqrt(cs * (2.0 - cs)); - const real ccu = sqrt(cc * (2.0 - cc)); - const real ws = accumulate(w, w + parent_number, 0.0); - const real cw = ws / norm(parent_number, w); + const real expected_norm = (n - 0.25 + 1.0 / (21 * n)) / sqrt (real (n)); + const real max_covariance_matrix_condition + = 0.01 / numeric_limits::epsilon (); + const real csu = sqrt (cs * (2.0 - cs)); + const real ccu = sqrt (cc * (2.0 - cc)); + const real ws = accumulate (w, w + parent_number, 0.0); + const real cw = ws / norm (parent_number, w); - valarray uw(n); - valarray vw(n); - valarray> u(uw, population_size); - valarray> v = u; - valarray> x = u; + valarray uw (n); + valarray vw (n); + valarray > u (uw, population_size); + valarray > v = u; + valarray > x = u; - valarray y(population_size); - valarray indexes(population_size); + valarray y (population_size); + valarray indexes (population_size); - while (g < stop_generation) { - // Generate a new population of object parameter vectors, - // sorted indirectly by fitness - for (natural k = 0; k < population_size; ++k) { - uw = 0.0; - vw = 0.0; - for (natural j = 0, nj = 0; j < n; ++j, nj += n) { - do { - const real z = deviate(); + while (g < stop_generation) + { + // Generate a new population of object parameter vectors, + // sorted indirectly by fitness + for (natural k = 0; k < population_size; ++k) + { + uw = 0.0; + vw = 0.0; + for (natural j = 0, nj = 0; j < n; ++j, nj += n) + { + do + { + const real z = deviate (); - for (natural i = 0, ij = nj; i < n; ++i, ++ij) { - u[k][i] = uw[i] + z * (B[ij] * d[j]); - v[k][i] = vw[i] + z * B[ij]; - x[k][i] = xw[i] + u[k][i] * step_size; // Hansen & Ostermeier (2001, Eq. 13) - } - } while (constraint.is_violated(&x[k][0], n)); - uw = u[k]; - vw = v[k]; + for (natural i = 0, ij = nj; i < n; ++i, ++ij) + { + u[k][i] = uw[i] + z * (B[ij] * d[j]); + v[k][i] = vw[i] + z * B[ij]; + x[k][i] + = xw[i] + u[k][i] * step_size; // Hansen & Ostermeier + // (2001, Eq. 13) + } } + while (constraint.is_violated (&x[k][0], n)); + uw = u[k]; + vw = v[k]; } + } #ifdef _OPENMP #pragma omp parallel for - for (natural k = 0; k < population_size; ++k) { - y[k] = f(&x[k][0], n) + constraint.cost(&x[k][0], n); - indexes[k] = k; - } + for (natural k = 0; k < population_size; ++k) + { + y[k] = f (&x[k][0], n) + constraint.cost (&x[k][0], n); + indexes[k] = k; + } #else // C++-11 - vector threads; threads.reserve(population_size); - for (natural k = 0; k < population_size; ++k) { - threads.push_back( - thread([k, &f, &constraint, &x, n, &y]() { - y[k] = f(&x[k][0], n) + constraint.cost(&x[k][0], n); - }) - ); - } - for (natural k = 0; k < population_size; ++k) { - threads[k].join(); - indexes[k] = k; - } + vector threads; + threads.reserve (population_size); + for (natural k = 0; k < population_size; ++k) + { + threads.push_back (thread ([k, &f, &constraint, &x, n, &y] () { + y[k] = f (&x[k][0], n) + constraint.cost (&x[k][0], n); + })); + } + for (natural k = 0; k < population_size; ++k) + { + threads[k].join (); + indexes[k] = k; + } #endif - partial_sort(&indexes[0], &indexes[parent_number], &indexes[population_size], - Index_Compare(y, compare)); - ++g; + partial_sort (&indexes[0], &indexes[parent_number], + &indexes[population_size], + Index_Compare (y, compare)); + ++g; - // Check the mutation variance - underflow = (y[indexes[0]] == y[indexes[parent_number]]); - if (underflow) { - break; - } + // Check the mutation variance + underflow = (y[indexes[0]] == y[indexes[parent_number]]); + if (underflow) + { + break; + } - // Recombine the best individuals - for (natural i = 0; i < n; ++i) { - uw[i] = vw[i] = xw[i] = 0.0; - for (natural k = 0; k < parent_number; ++k) { - uw[i] += w[k] * u[indexes[k]][i]; - vw[i] += w[k] * v[indexes[k]][i]; - xw[i] += w[k] * x[indexes[k]][i]; - } - uw[i] /= ws; - vw[i] /= ws; - xw[i] /= ws; + // Recombine the best individuals + for (natural i = 0; i < n; ++i) + { + uw[i] = vw[i] = xw[i] = 0.0; + for (natural k = 0; k < parent_number; ++k) + { + uw[i] += w[k] * u[indexes[k]][i]; + vw[i] += w[k] * v[indexes[k]][i]; + xw[i] += w[k] * x[indexes[k]][i]; } + uw[i] /= ws; + vw[i] /= ws; + xw[i] /= ws; + } - // Adapt the covariance matrix and the step size according to Hansen & Ostermeier (2001) - // and Hansen (2014) - if (acov > 0.0 or ccov > 0.0) { - for (natural j = 0, nj = 0; j < n; ++j, nj += n) { - pc[j] = (1.0 - cc) * pc[j] + (ccu * cw) * uw[j]; // Hansen & Ostermeier (2001, Eq. 14) - for (natural i = 0, ij = nj; i <= j; ++i, ++ij) { - real z = 0.0; - for (natural k = 0; k < parent_number; ++k) { - z += w[k] * (u[indexes[k]][i] * u[indexes[k]][j]); - } - // Hansen (2014, http://www.lri.fr/~hansen/purecmaes.m) - C[ij] = (C[ij] + acov * (pc[i] * pc[j] - C[ij])) + ccov * (z / ws - C[ij]); + // Adapt the covariance matrix and the step size according to Hansen & + // Ostermeier (2001) and Hansen (2014) + if (acov > 0.0 or ccov > 0.0) + { + for (natural j = 0, nj = 0; j < n; ++j, nj += n) + { + pc[j] + = (1.0 - cc) * pc[j] + + (ccu * cw) * uw[j]; // Hansen & Ostermeier (2001, Eq. 14) + for (natural i = 0, ij = nj; i <= j; ++i, ++ij) + { + real z = 0.0; + for (natural k = 0; k < parent_number; ++k) + { + z += w[k] * (u[indexes[k]][i] * u[indexes[k]][j]); } + // Hansen (2014, http://www.lri.fr/~hansen/purecmaes.m) + C[ij] = (C[ij] + acov * (pc[i] * pc[j] - C[ij])) + + ccov * (z / ws - C[ij]); } - if (g % update_modulus == 0) { - decompose(C, B, d); + } + if (g % update_modulus == 0) + { + decompose (C, B, d); - const real t = d[n - 1] / max_covariance_matrix_condition - d[0]; - if (t > 0.0) { - for (natural i = 0, ii = 0; i < n; ++i, ii += n + 1) { - C[ii] += t; - d[i] += t; - } - } - for (natural i = 0; i < n; ++i) { - d[i] = sqrt(d[i]); + const real t = d[n - 1] / max_covariance_matrix_condition - d[0]; + if (t > 0.0) + { + for (natural i = 0, ii = 0; i < n; ++i, ii += n + 1) + { + C[ii] += t; + d[i] += t; } } - } - for (natural i = 0; i < n; ++i) { - ps[i] = (1.0 - cs) * ps[i] + (csu * cw) * vw[i]; // Hansen & Ostermeier (2001, Eq. 16) - } - // Hansen & Ostermeier (2001, Eq. 17) - step_size *= exp((cs / step_size_damping) * (norm(n, ps) / expected_norm - 1.0)); - - // Check if the optimization is completed - for (natural i = 0, ii = 0; i < n; ++i, ii += n + 1) { - optimized = (sq(step_size) * C[ii] < sq(accuracy_goal * xw[i]) + 1.0 / max_covariance_matrix_condition); - if (!optimized) { - break; + for (natural i = 0; i < n; ++i) + { + d[i] = sqrt (d[i]); } } - if (optimized or tracer.is_tracing(g)) { - tracer.trace(g, f(xw, n) + constraint.cost(xw, n), step_size * d[0], step_size * d[n - 1]); - } - if (optimized) { - break; - } } + for (natural i = 0; i < n; ++i) + { + ps[i] = (1.0 - cs) * ps[i] + + (csu * cw) * vw[i]; // Hansen & Ostermeier (2001, Eq. 16) + } + // Hansen & Ostermeier (2001, Eq. 17) + step_size *= exp ((cs / step_size_damping) + * (norm (n, ps) / expected_norm - 1.0)); - yw = f(xw, n) + constraint.cost(xw, n); + // Check if the optimization is completed + for (natural i = 0, ii = 0; i < n; ++i, ii += n + 1) + { + optimized = (sq (step_size) * C[ii] + < sq (accuracy_goal * xw[i]) + + 1.0 / max_covariance_matrix_condition); + if (!optimized) + { + break; + } + } + if (optimized or tracer.is_tracing (g)) + { + tracer.trace (g, f (xw, n) + constraint.cost (xw, n), + step_size * d[0], step_size * d[n - 1]); + } + if (optimized) + { + break; + } } - /// Yields the paramater standard uncertainties. - /// - /// Computes the standard variance along ellipsoid principal axes from the curvature of a - /// parabola through three points around the minimum. The global step size is rescaled to - /// approximate the standard covariance matrix. - /// - /// @tparam F The function type. - /// @tparam Constraint The constraint type. - /// - /// @param[in] f The objective function. - /// @param[in] constraint The constraint on parameter values. - /// @param[in] n The number of parameter values. - /// @param[in] x The parameter values. - /// @param[in] d The local step sizes - /// @param[in] B The rotation matrix (in column-major layout). - /// @param[in] C The covariance matrix (upper triangular part only, in column-major layout). - /// @param[in] s The global step size. - /// @param[out] z The parameter uncertainties. - template - void postopti(const F &f, const Constraint &constraint, natural n, - const real x[], - const real d[], - const real B[], - const real C[], - const real s, - real z[]) { - using std::abs; - using std::exp; - using std::log; - using std::sqrt; - using std::valarray; - using std::thread; + yw = f (xw, n) + constraint.cost (xw, n); +} - const real zx = f(&x[0], n) + constraint.cost(&x[0], n); - // The rescaled global step sizes - valarray g(s, n); +/// Yields the paramater standard uncertainties. +/// +/// Computes the standard variance along ellipsoid principal axes from the +/// curvature of a parabola through three points around the minimum. The global +/// step size is rescaled to approximate the standard covariance matrix. +/// +/// @tparam F The function type. +/// @tparam Constraint The constraint type. +/// +/// @param[in] f The objective function. +/// @param[in] constraint The constraint on parameter values. +/// @param[in] n The number of parameter values. +/// @param[in] x The parameter values. +/// @param[in] d The local step sizes +/// @param[in] B The rotation matrix (in column-major layout). +/// @param[in] C The covariance matrix (upper triangular part only, in +/// column-major layout). +/// @param[in] s The global step size. +/// @param[out] z The parameter uncertainties. +template +void +postopti (const F &f, const Constraint &constraint, natural n, const real x[], + const real d[], const real B[], const real C[], const real s, + real z[]) +{ + using std::abs; + using std::exp; + using std::log; + using std::sqrt; + using std::thread; + using std::valarray; - for (natural j = 0; j < n; ++j) { - real a = 0.0; - real b = 0.0; - real c = g[j]; + const real zx = f (&x[0], n) + constraint.cost (&x[0], n); + // The rescaled global step sizes + valarray g (s, n); - do { - // Compute two steps along a principal axis in opposite directions - valarray p(x, n); - valarray q(x, n); - for (natural i = 0, ij = j * n; i < n; ++i, ++ij) { - p[i] += c * B[ij] * d[j]; - q[i] -= c * B[ij] * d[j]; - } - real zp; - real zq; + for (natural j = 0; j < n; ++j) + { + real a = 0.0; + real b = 0.0; + real c = g[j]; + + do + { + // Compute two steps along a principal axis in opposite directions + valarray p (x, n); + valarray q (x, n); + for (natural i = 0, ij = j * n; i < n; ++i, ++ij) + { + p[i] += c * B[ij] * d[j]; + q[i] -= c * B[ij] * d[j]; + } + real zp; + real zq; #ifdef _OPENMP #pragma omp parallel - { + { #pragma omp sections - { + { #pragma omp section - zp = f(&p[0], n) + constraint.cost(&p[0], n); + zp = f (&p[0], n) + constraint.cost (&p[0], n); #pragma omp section - zq = f(&q[0], n) + constraint.cost(&q[0], n); - } - } + zq = f (&q[0], n) + constraint.cost (&q[0], n); + } + } #else // C++-11 - thread tp([&f, &constraint, &p, n, &zp]() { zp = f(&p[0], n) + constraint.cost(&p[0], n); }); - thread tq([&f, &constraint, &q, n, &zq]() { zq = f(&q[0], n) + constraint.cost(&q[0], n); }); - tp.join(); - tq.join(); + thread tp ([&f, &constraint, &p, n, &zp] () { + zp = f (&p[0], n) + constraint.cost (&p[0], n); + }); + thread tq ([&f, &constraint, &q, n, &zq] () { + zq = f (&q[0], n) + constraint.cost (&q[0], n); + }); + tp.join (); + tq.join (); #endif - // Compute the rescaled global step size - g[j] = c / sqrt(abs((zp + zq) - (zx + zx))); + // Compute the rescaled global step size + g[j] = c / sqrt (abs ((zp + zq) - (zx + zx))); - // Make a smaller or larger computation step in the next iteration - if (abs(0.5 * (zp + zq) - zx) < 0.5) { - a = c; - c = c * 1.618; - } else { - b = c; - c = c * 0.618; - } - } while (a == 0.0 or b == 0.0); // the computation step is too small or too large + // Make a smaller or larger computation step in the next iteration + if (abs (0.5 * (zp + zq) - zx) < 0.5) + { + a = c; + c = c * 1.618; + } + else + { + b = c; + c = c * 0.618; + } } - // Take the geometric mean to rescale the covariance matrix - const real h = exp(g.apply(log).sum() / real(n)); + while (a == 0.0 + or b == 0.0); // the computation step is too small or too large + } + // Take the geometric mean to rescale the covariance matrix + const real h = exp (g.apply (log).sum () / real (n)); - for (natural i = 0, ii = 0; i < n; ++i, ii += n + 1) { - z[i] = h * sqrt(C[ii]); - } + for (natural i = 0, ii = 0; i < n; ++i, ii += n + 1) + { + z[i] = h * sqrt (C[ii]); } +} } diff --git a/src/main/cxx/core/optimizer.cxx b/src/main/cxx/core/optimizer.cxx index 6ef6857..b5a8bcc 100644 --- a/src/main/cxx/core/optimizer.cxx +++ b/src/main/cxx/core/optimizer.cxx @@ -7,116 +7,146 @@ #include "optimizer.h" -especia::Optimizer::Builder::Builder() : weights(parent_number) { - with_strategy_parameters(); +especia::Optimizer::Builder::Builder () : weights (parent_number) +{ + with_strategy_parameters (); } -especia::Optimizer::Builder::~Builder() = default; - -especia::Optimizer::Builder &especia::Optimizer::Builder::with_defaults() { - return with_problem_dimension(). - with_parent_number(). - with_covariance_update_modulus(). - with_accuracy_goal(). - with_stop_generation(). - with_random_seed(); +especia::Optimizer::Builder::~Builder () = default; + +especia::Optimizer::Builder & +especia::Optimizer::Builder::with_defaults () +{ + return with_problem_dimension () + .with_parent_number () + .with_covariance_update_modulus () + .with_accuracy_goal () + .with_stop_generation () + .with_random_seed (); } -especia::Optimizer::Builder &especia::Optimizer::Builder::with_problem_dimension(natural n) { - if (n != this->n) { - this->n = n; - with_strategy_parameters(); +especia::Optimizer::Builder & +especia::Optimizer::Builder::with_problem_dimension (natural n) +{ + if (n != this->n) + { + this->n = n; + with_strategy_parameters (); } - return *this; + return *this; } -especia::Optimizer::Builder &especia::Optimizer::Builder::with_parent_number(natural parent_number) { - const natural population_size = 2 * parent_number; +especia::Optimizer::Builder & +especia::Optimizer::Builder::with_parent_number (natural parent_number) +{ + const natural population_size = 2 * parent_number; - if (this->parent_number != parent_number) { - this->parent_number = parent_number; - with_strategy_parameters(); + if (this->parent_number != parent_number) + { + this->parent_number = parent_number; + with_strategy_parameters (); } - if (this->population_size != population_size) { - with_population_size(population_size); + if (this->population_size != population_size) + { + with_population_size (population_size); } - return *this; + return *this; } -especia::Optimizer::Builder &especia::Optimizer::Builder::with_population_size(natural population_size) { - this->population_size = population_size; - return *this; +especia::Optimizer::Builder & +especia::Optimizer::Builder::with_population_size (natural population_size) +{ + this->population_size = population_size; + return *this; } -especia::Optimizer::Builder &especia::Optimizer::Builder::with_covariance_update_modulus(natural update_modulus) { - this->update_modulus = update_modulus; - return *this; +especia::Optimizer::Builder & +especia::Optimizer::Builder::with_covariance_update_modulus ( + natural update_modulus) +{ + this->update_modulus = update_modulus; + return *this; } -especia::Optimizer::Builder &especia::Optimizer::Builder::with_accuracy_goal(real accuracy_goal) { - this->accuracy_goal = accuracy_goal; - return *this; +especia::Optimizer::Builder & +especia::Optimizer::Builder::with_accuracy_goal (real accuracy_goal) +{ + this->accuracy_goal = accuracy_goal; + return *this; } -especia::Optimizer::Builder &especia::Optimizer::Builder::with_random_seed(word64 seed) { - this->random_seed = seed; - return *this; +especia::Optimizer::Builder & +especia::Optimizer::Builder::with_random_seed (word64 seed) +{ + this->random_seed = seed; + return *this; } -especia::Optimizer::Builder &especia::Optimizer::Builder::with_stop_generation(natural stop_generation) { - this->stop_generation = stop_generation; - return *this; +especia::Optimizer::Builder & +especia::Optimizer::Builder::with_stop_generation (natural stop_generation) +{ + this->stop_generation = stop_generation; + return *this; } -especia::Optimizer especia::Optimizer::Builder::build() { - return Optimizer(*this); +especia::Optimizer +especia::Optimizer::Builder::build () +{ + return Optimizer (*this); } -void especia::Optimizer::Builder::with_strategy_parameters() { - using std::log; - using std::max; - using std::min; - using std::sqrt; +void +especia::Optimizer::Builder::with_strategy_parameters () +{ + using std::log; + using std::max; + using std::min; + using std::sqrt; - weights.resize(parent_number); + weights.resize (parent_number); - for (natural i = 0; i < parent_number; ++i) { - weights[i] = log((parent_number + 0.5) / (i + 1)); + for (natural i = 0; i < parent_number; ++i) + { + weights[i] = log ((parent_number + 0.5) / (i + 1)); } - wv = sq(weights.sum()) / weights.apply(sq).sum(); - cs = (2.0 + wv) / (5.0 + n + wv); - cc = (4.0 + wv / n) / (4.0 + n + 2.0 * wv / n); + wv = sq (weights.sum ()) / weights.apply (sq).sum (); + cs = (2.0 + wv) / (5.0 + n + wv); + cc = (4.0 + wv / n) / (4.0 + n + 2.0 * wv / n); - acov = 2.0 / (sq(n + 1.3) + wv); - ccov = min(1.0 - acov, 2.0 * (wv - 2.0 + 1.0 / wv) / (sq(n + 2.0) + wv)); - step_size_damping = cs + 1.0 + 2.0 * max(0.0, sqrt((wv - 1.0) / (n + 1.0)) - 1.0); + acov = 2.0 / (sq (n + 1.3) + wv); + ccov = min (1.0 - acov, + 2.0 * (wv - 2.0 + 1.0 / wv) / (sq (n + 2.0) + wv)); + step_size_damping + = cs + 1.0 + 2.0 * max (0.0, sqrt ((wv - 1.0) / (n + 1.0)) - 1.0); } -especia::Optimizer::Result::Result(natural n, - const valarray &x_in, - const valarray &d_in, - real s_in) - : x(x_in), d(d_in), s(s_in), z(0.0, n), B(0.0, sq(n)), C(0.0, sq(n)), pc(0.0, n), ps(0.0, n) { - for (natural i = 0, ii = 0; i < n; ++i, ii += n + 1) { - B[ii] = 1.0; - C[ii] = d[i] * d[i]; +especia::Optimizer::Result::Result (natural n, const valarray &x_in, + const valarray &d_in, real s_in) + : x (x_in), d (d_in), s (s_in), z (0.0, n), B (0.0, sq (n)), + C (0.0, sq (n)), pc (0.0, n), ps (0.0, n) +{ + for (natural i = 0, ii = 0; i < n; ++i, ii += n + 1) + { + B[ii] = 1.0; + C[ii] = d[i] * d[i]; } - y = 0.0; + y = 0.0; - optimized = false; - underflow = false; + optimized = false; + underflow = false; - g = 0; + g = 0; } -especia::Optimizer::Result::~Result() = default; - -especia::Optimizer::Optimizer(const especia::Optimizer::Builder &builder) - : config(builder), decompose(builder.get_problem_dimension()), deviate(builder.get_random_seed()) { +especia::Optimizer::Result::~Result () = default; +especia::Optimizer::Optimizer (const especia::Optimizer::Builder &builder) + : config (builder), decompose (builder.get_problem_dimension ()), + deviate (builder.get_random_seed ()) +{ } -especia::Optimizer::~Optimizer() = default; +especia::Optimizer::~Optimizer () = default; diff --git a/src/main/cxx/core/optimizer.h b/src/main/cxx/core/optimizer.h index c76b845..d392bbe 100644 --- a/src/main/cxx/core/optimizer.h +++ b/src/main/cxx/core/optimizer.h @@ -16,672 +16,757 @@ using std::valarray; -namespace especia { - - /// No constraint. - /// - /// @tparam T The number type. - template - class No_Constraint { - public: - /// The constructor. - No_Constraint() = default; - - /// The destructor. - ~No_Constraint() = default; - - /// Tests if a given parameter vector violates the constraint. - /// - /// @param[in] x The parameter vector. - /// @param[in] n The number of parameters to test. - /// @return always @c false. - bool is_violated(const T x[], natural n) const { - return false; - } - - /// Computes the cost associated with the constraint. - /// - /// @param[in] x The parameter vector. - /// @param[in] n The number of parameters to take account of. - /// @return always zero. - T cost(const T x[], natural n) const { - return T(0); - } - }; - - /// No tracing. - /// - /// @tparam T The number type. - template - class No_Tracing { - public: - - /// The constructor. - No_Tracing() = default; - - /// The destructor. - ~No_Tracing() = default; - - /// Tests if tracing is enabled. - /// - /// @param[in] g The generation number. - /// @return always @c false. - bool is_tracing(natural g) const { - return false; - } - - /// Traces state information. - /// - /// @param[in] g The generation number. - /// @param[in] y The value of the objective function. - /// @param[in] min_step The minimum mutation step size. - /// @param[in] max_step The maximum mutation step size. - void trace(natural g, T y, T min_step, T max_step) const { - } - }; - - /// An optimizer based on the CMA-ES developed by Hansen and Ostermeier (2001). - /// - /// Further reading: - /// - /// N. Hansen, S. D. Müller, P. Koumoutsakos (2003). - /// *Reducing the Increasing the Time Complexity of the Derandomized Evolution - /// Strategy with Covariance Matrix Adaption (CMA-ES).* - /// Evolutionary Computation, 11, 1, ISSN 1063-6560. - /// - /// N. Hansen, A. Ostermeier (2001). - /// *Completely Derandomized Self-Adaption in Evolution Strategies.* - /// Evolutionary Computation, 9, 159, ISSN 1063-6560. - class Optimizer { - public: - /// Builds a new optimizer. - class Builder { - public: - /// Default constructor. - Builder(); - - /// The destructor. - ~Builder(); - - /// Builds a new optimizer. - /// - /// @return the optimizer. - Optimizer build(); - - /// Returns the problem dimension. - /// - /// @return the problem dimension. - natural get_problem_dimension() const { - return n; - } - - /// Returns the parent number. - /// - /// @return the parent number. - natural get_parent_number() const { - return parent_number; - } - - /// Returns the population size. - /// - /// @return the population size. - natural get_population_size() const { - return population_size; - } - - /// Returns the covariance matrix update modulus. - /// - /// @return the covariance matrix update modulus. - natural get_covariance_update_modulus() const { - return update_modulus; - } - - /// Returns the accuracy goal. - /// - /// @return the accuracy goal. - real get_accuracy_goal() const { - return accuracy_goal; - } - - /// Returns the random seed. - /// - /// @return the random seed. - word64 get_random_seed() const { - return random_seed; - } - - /// Returns the stop generation. - /// - /// @return the stop generation. - natural get_stop_generation() const { - return stop_generation; - } - - /// Returns the recombination weights. - /// - /// @return the recombination weights. - const std::valarray &get_weights() const { - return weights; - } - - /// Returns the step size cumulation rate. - /// - /// @return the step size cumulation rate. - real get_step_size_cumulation_rate() const { - return cs; - } - - /// Returns the distribution cumulation rate. - /// - /// @return the distribution cumulation rate. - real get_distribution_cumulation_rate() const { - return cc; - } - - /// Returns the rank-1 covariance matrix adaption rate. - /// - /// @return the rank-1 covariance matrix adaption rate. - real get_rank_1_covariance_matrix_adaption_rate() const { - return ccov; - } - - /// Returns the rank-µ covariance matrix adaption rate. - /// - /// @return the rank-µ covariance matrix adaption rate. - real get_rank_m_covariance_matrix_adaption_rate() const { - return acov; - } - - /// Returns the step size damping. - /// - /// @return the step size damping. - real get_step_size_damping() const { - return step_size_damping; - } - - /// Configures default settings. - /// - /// @return the problem dimension. - Builder &with_defaults(); - - /// Configures the problem dimension. - /// - /// @param[in] n The problem dimension. - /// @return this builder. - Builder &with_problem_dimension(natural n = 10); - - /// Configures the parent number and sets the population size to twice the parent number. - /// - /// @param[in] parent_number The parent number. - /// @return this builder. - Builder &with_parent_number(natural parent_number = 20); - - /// Configures the population size. - /// - /// @attention The population size must be greater than or equal to twice the parent number. - /// - /// @param[in] population_size The population size. - /// @return this builder. - Builder &with_population_size(natural population_size); - - /// Configures the covariance matrix update modulus. - /// - /// @param[in] update_modulus The update modulus. - /// @return this builder. - Builder &with_covariance_update_modulus(natural update_modulus = 1); - - /// Configures the accuracy goal. - /// - /// @param[in] accuracy_goal The accuracy goal. - /// @return this builder. - Builder &with_accuracy_goal(real accuracy_goal = 1.0E-06); - - /// Configures the random seed. - /// - /// @param[in] seed The random seed. - /// @return this builder. - Builder &with_random_seed(word64 seed = 9600629759793949339ULL); - - /// Configures the stop generation. - /// - /// @param[in] stop_generation The stop generation. - /// @return this builder. - Builder &with_stop_generation(natural stop_generation = 1000); - - private: - /// Returns a pointer to the recombination weights. - /// - /// @return a pointer to the recombination weights. - const real *get_weights_pointer() const { - return &weights[0]; - } - - /// Configures strategy parameters like recombination weights, cumulation and adaption rates - /// according to Hansen (2014, http://cma.gforge.inria.fr/purecmaes.m). - void with_strategy_parameters(); - - /// The problem dimension. - natural n = 10; - - /// The parent number. - natural parent_number = 20; - - /// The population size. - natural population_size = 40; - - /// The covariance matrix update modulus. - natural update_modulus = 1; - - /// The accuracy goal. - real accuracy_goal = 1.0E-6; - - /// The random seed. - word64 random_seed = 271828; - - /// The stop generation. - natural stop_generation = 1000; - - /// The recombination weights. - std::valarray weights; - - /// The variance of the recombination weights. - real wv; - - /// The step size cumulation rate. - real cs; - - /// The distribution cumulation rate. - real cc; - - /// The rank-µ covariance matrix adaption rate. - real acov; - - /// The rank-1 covariance matrix adaption rate. - real ccov; - - /// The step size damping. - real step_size_damping; - - friend class Optimizer; - }; - - /// The optimization result. - class Result { - public: - /// The destructor. - ~Result(); - - /// Returns the covariance matrix (upper triangular part only, in column-major layout). - /// - /// @return the covariance matrix. - const std::valarray &get_covariance_matrix() const { - return C; - } - - /// Returns the distribution cumulation path. - /// - /// @return the distribution cumulation path. - const std::valarray &get_distribution_cumulation_path() const { - return pc; - } - - /// Returns the optimized fitness. - /// - /// @return the optimized fitness. - real get_fitness() const { - return y; - } - - /// Returns the final generation number. - /// - /// @return the final generation number. - natural get_generation_number() const { - return g; - } - - /// Returns the final global step size. - /// - /// @return the final global step size. - real get_global_step_size() const { - return s; - } - - /// Returns the final local step sizes. - /// - /// @return the final local step sizes. - const std::valarray &get_local_step_sizes() const { - return d; - } - - /// Returns the optimized parameter values. - /// - /// @return the optimized parameter values. - const std::valarray &get_parameter_values() const { - return x; - } - - /// Returns the parameter uncertainties. - /// - /// @return the parameter uncertainties. - const std::valarray &get_parameter_uncertainties() const { - return z; - } - - /// Returns the final rotation matrix (in column-major layout). - /// - /// @return the final rotation matrix. - const std::valarray &get_rotation_matrix() const { - return B; - } - - /// Returns the step size cumulation path. - /// - /// @return the step size cumulation path. - const std::valarray &get_step_size_cumulation_path() const { - return ps; - } - - /// Returns the optimization status flag. - /// - /// @return the optimization status flag. - bool is_optimized() const { - return optimized; - } - - /// Returns the mutation variance underflow status flag. - /// - /// @return the mutation variance underflow status flag. - bool is_underflow() const { - return underflow; - } - - private: - /// The constructor. - /// - /// @param[in] n The problem dimension. - /// @param[in] x The initial parameter values. - /// @param[in] d The initial local step sizes. - /// @param[in] s The initial global step size. - Result(natural n, const std::valarray &x, const std::valarray &d, real s); - - /// Returns a pointer to the covariance matrix. - /// - /// @return a pointer to the covariance matrix. - real *get_covariance_matrix_pointer() { - return &C[0]; - } - - /// Returns a pointer to the distribution cumulation path. - /// - /// @return a pointer to the distribution cumulation path. - real *get_distribution_cumulation_path_pointer() { - return &pc[0]; - } - - /// Returns a reference to the fitness. - /// - /// @return a reference to the fitness. - real &__fitness() { - return y; - } - - /// Returns a reference to the generation number. - /// - /// @return a reference to the generation number. - natural &__generation_number() { - return g; - } - - /// Returns a reference to the global step size. - /// - /// @return a reference to the global step size. - real &__global_step_size() { - return s; - } - - /// Returns a pointer to the local step sizes. - /// - /// @return a pointer to the local step sizes. - real *get_local_step_sizes_pointer() { - return &d[0]; - } - - /// Returns a pointer to the parameter values. - /// - /// @return a pointer to the parameter values. - real *get_parameter_values_pointer() { - return &x[0]; - } - - /// Returns a pointer to the parameter uncertainties. - /// - /// @return a pointer to the parameter uncertainties. - real *get_parameter_uncertainties_pointer() { - return &z[0]; - } - - /// Returns a pointer to the rotation matrix. - /// - /// @return a pointer to the rotation matrix. - real *get_rotation_matrix_pointer() { - return &B[0]; - } - - /// Returns a pointer to the step size cumulation path. - /// - /// @return a pointer to the step size cumulation path. - real *get_step_size_cumulation_path_pointer() { - return &ps[0]; - } - - /// Returns a reference to the optimization status flag. - /// - /// @return a reference to the optimization status flag. - bool &__optimized() { - return optimized; - } - - /// Returns a reference to the mutation variance underflow status flag. - /// - /// @return a reference to the mutation variance underflow status flag. - bool &__underflow() { - return underflow; - } - - /// The optimized parameter values. - std::valarray x; - - /// The final local step sizes. - std::valarray d; - - /// The final global step size. - real s; - - /// The parameter uncertainties. - std::valarray z; - - /// The optimized fitness. - real y; - - /// The final rotation matrix (in column-major layout). - std::valarray B; - - /// The final covariance matrix (upper triangular part only, in column-major layout). - std::valarray C; - - /// The distribution cumulation path. - std::valarray pc; - - /// The step size cumulation path. - std::valarray ps; - - /// The optimization status flag. - bool optimized; - - /// The mutation variance underflow status flag. - bool underflow; - - /// The final generation number. - natural g; - - friend class Optimizer; - }; - - /// The destructor. - ~Optimizer(); - - /// Maximizes an objective function. - /// - /// @tparam F The function type. - /// @tparam Constraint The constraint type. - /// @tparam Tracing The tracer type. - /// - /// @param[in] f The objective function. - /// @param[in] x The initial parameter values. - /// @param[in] d The initial local step sizes. - /// @param[in] s The initial global step size. - /// @param[in] constraint The constraint. - /// @param[in] tracer The tracer. - /// - /// @return the maximization result. - template - Result maximize(const F &f, - const std::valarray &x, - const std::valarray &d, - const real &s, - const Constraint &constraint, - const Tracing &tracer) const { - return optimize(f, x, d, s, constraint, tracer, std::greater()); - } - - /// @overload - template - Result maximize(const F &f, - const std::valarray &x, - const std::valarray &d, - const real &s) const { - return optimize(f, x, d, s, No_Constraint(), No_Tracing(), std::greater()); - } - - /// Minimizes an objective function. - /// - /// @tparam F The function type. - /// @tparam Constraint The constraint type. - /// @tparam Tracing The tracer type. - /// - /// @param[in] f The objective function. - /// @param[in] x The initial parameter values. - /// @param[in] d The initial local step sizes. - /// @param[in] s The initial global step size. - /// @param[in] constraint The constraint. - /// @param[in] tracer The tracer. - /// - /// @return the minimization result. - template - Result minimize(const F &f, - const std::valarray &x, - const std::valarray &d, - const real &s, - const Constraint &constraint, - const Tracing &tracer) const { - return optimize(f, x, d, s, constraint, tracer, std::less()); - } - - /// @overload - template - Result minimize(const F &f, - const std::valarray &x, - const std::valarray &d, - const real &s) const { - return optimize(f, x, d, s, No_Constraint(), No_Tracing(), std::less()); - } - - private: - /// Creates a new instance of this class with the build configuration supplied as argument. - /// - /// @param[in] builder The build configuration. - explicit Optimizer(const Builder &builder); - - /// Optimizes an objective function. - /// - /// @tparam F The function type. - /// @tparam Constraint The constraint type. - /// @tparam Tracing The tracer type. - /// @tparam Compare The fitness comparator type. - /// - /// @param[in] f The objective function. - /// @param[in] x The initial parameter values. - /// @param[in] d The initial local step sizes. - /// @param[in] s The initial global step size. - /// @param[in] constraint The constraint. - /// @param[in] tracer The tracer. - /// @param[in] compare The fitness comparator. - /// - /// @return the optimization result. - template - Result optimize(const F &f, - const std::valarray &x, - const std::valarray &d, - const real &s, - const Constraint &constraint, - const Tracing &tracer, - const Compare &compare) const { - using especia::optimize; - using especia::postopti; - - const natural n = config.get_problem_dimension(); - - Result result(n, x, d, s); - - optimize(f, constraint, n, - config.get_parent_number(), - config.get_population_size(), - config.get_weights_pointer(), - config.get_step_size_damping(), - config.get_step_size_cumulation_rate(), - config.get_distribution_cumulation_rate(), - config.get_rank_1_covariance_matrix_adaption_rate(), - config.get_rank_m_covariance_matrix_adaption_rate(), - config.get_covariance_update_modulus(), - config.get_accuracy_goal(), - config.get_stop_generation(), - result.__generation_number(), - result.get_parameter_values_pointer(), - result.__global_step_size(), - result.get_local_step_sizes_pointer(), - result.get_rotation_matrix_pointer(), - result.get_covariance_matrix_pointer(), - result.get_step_size_cumulation_path_pointer(), - result.get_distribution_cumulation_path_pointer(), - result.__fitness(), - result.__optimized(), - result.__underflow(), - deviate, decompose, compare, tracer - ); - - if (result.__optimized()) { - postopti(f, constraint, n, - result.get_parameter_values_pointer(), - result.get_local_step_sizes_pointer(), - result.get_rotation_matrix_pointer(), - result.get_covariance_matrix_pointer(), - result.get_global_step_size(), - result.get_parameter_uncertainties_pointer() - ); - } - - return result; - } - - /// The build configuration. - const Builder config; - - /// The eigenvalue decomposition strategy. - const Decompose decompose; - - /// The random number generator. - const Normal_Deviate deviate; - }; +namespace especia +{ + +/// No constraint. +/// +/// @tparam T The number type. +template class No_Constraint +{ +public: + /// The constructor. + No_Constraint () = default; + + /// The destructor. + ~No_Constraint () = default; + + /// Tests if a given parameter vector violates the constraint. + /// + /// @param[in] x The parameter vector. + /// @param[in] n The number of parameters to test. + /// @return always @c false. + bool + is_violated (const T x[], natural n) const + { + return false; + } + + /// Computes the cost associated with the constraint. + /// + /// @param[in] x The parameter vector. + /// @param[in] n The number of parameters to take account of. + /// @return always zero. + T + cost (const T x[], natural n) const + { + return T (0); + } +}; + +/// No tracing. +/// +/// @tparam T The number type. +template class No_Tracing +{ +public: + /// The constructor. + No_Tracing () = default; + + /// The destructor. + ~No_Tracing () = default; + + /// Tests if tracing is enabled. + /// + /// @param[in] g The generation number. + /// @return always @c false. + bool + is_tracing (natural g) const + { + return false; + } + + /// Traces state information. + /// + /// @param[in] g The generation number. + /// @param[in] y The value of the objective function. + /// @param[in] min_step The minimum mutation step size. + /// @param[in] max_step The maximum mutation step size. + void + trace (natural g, T y, T min_step, T max_step) const + { + } +}; + +/// An optimizer based on the CMA-ES developed by Hansen and Ostermeier (2001). +/// +/// Further reading: +/// +/// N. Hansen, S. D. Müller, P. Koumoutsakos (2003). +/// *Reducing the Increasing the Time Complexity of the Derandomized Evolution +/// Strategy with Covariance Matrix Adaption (CMA-ES).* +/// Evolutionary Computation, 11, 1, ISSN 1063-6560. +/// +/// N. Hansen, A. Ostermeier (2001). +/// *Completely Derandomized Self-Adaption in Evolution Strategies.* +/// Evolutionary Computation, 9, 159, ISSN 1063-6560. +class Optimizer +{ +public: + /// Builds a new optimizer. + class Builder + { + public: + /// Default constructor. + Builder (); + + /// The destructor. + ~Builder (); + + /// Builds a new optimizer. + /// + /// @return the optimizer. + Optimizer build (); + + /// Returns the problem dimension. + /// + /// @return the problem dimension. + natural + get_problem_dimension () const + { + return n; + } + + /// Returns the parent number. + /// + /// @return the parent number. + natural + get_parent_number () const + { + return parent_number; + } + + /// Returns the population size. + /// + /// @return the population size. + natural + get_population_size () const + { + return population_size; + } + + /// Returns the covariance matrix update modulus. + /// + /// @return the covariance matrix update modulus. + natural + get_covariance_update_modulus () const + { + return update_modulus; + } + + /// Returns the accuracy goal. + /// + /// @return the accuracy goal. + real + get_accuracy_goal () const + { + return accuracy_goal; + } + + /// Returns the random seed. + /// + /// @return the random seed. + word64 + get_random_seed () const + { + return random_seed; + } + + /// Returns the stop generation. + /// + /// @return the stop generation. + natural + get_stop_generation () const + { + return stop_generation; + } + + /// Returns the recombination weights. + /// + /// @return the recombination weights. + const std::valarray & + get_weights () const + { + return weights; + } + + /// Returns the step size cumulation rate. + /// + /// @return the step size cumulation rate. + real + get_step_size_cumulation_rate () const + { + return cs; + } + + /// Returns the distribution cumulation rate. + /// + /// @return the distribution cumulation rate. + real + get_distribution_cumulation_rate () const + { + return cc; + } + + /// Returns the rank-1 covariance matrix adaption rate. + /// + /// @return the rank-1 covariance matrix adaption rate. + real + get_rank_1_covariance_matrix_adaption_rate () const + { + return ccov; + } + + /// Returns the rank-µ covariance matrix adaption rate. + /// + /// @return the rank-µ covariance matrix adaption rate. + real + get_rank_m_covariance_matrix_adaption_rate () const + { + return acov; + } + + /// Returns the step size damping. + /// + /// @return the step size damping. + real + get_step_size_damping () const + { + return step_size_damping; + } + + /// Configures default settings. + /// + /// @return the problem dimension. + Builder &with_defaults (); + + /// Configures the problem dimension. + /// + /// @param[in] n The problem dimension. + /// @return this builder. + Builder &with_problem_dimension (natural n = 10); + + /// Configures the parent number and sets the population size to twice the + /// parent number. + /// + /// @param[in] parent_number The parent number. + /// @return this builder. + Builder &with_parent_number (natural parent_number = 20); + + /// Configures the population size. + /// + /// @attention The population size must be greater than or equal to twice + /// the parent number. + /// + /// @param[in] population_size The population size. + /// @return this builder. + Builder &with_population_size (natural population_size); + + /// Configures the covariance matrix update modulus. + /// + /// @param[in] update_modulus The update modulus. + /// @return this builder. + Builder &with_covariance_update_modulus (natural update_modulus = 1); + + /// Configures the accuracy goal. + /// + /// @param[in] accuracy_goal The accuracy goal. + /// @return this builder. + Builder &with_accuracy_goal (real accuracy_goal = 1.0E-06); + + /// Configures the random seed. + /// + /// @param[in] seed The random seed. + /// @return this builder. + Builder &with_random_seed (word64 seed = 9600629759793949339ULL); + + /// Configures the stop generation. + /// + /// @param[in] stop_generation The stop generation. + /// @return this builder. + Builder &with_stop_generation (natural stop_generation = 1000); + + private: + /// Returns a pointer to the recombination weights. + /// + /// @return a pointer to the recombination weights. + const real * + get_weights_pointer () const + { + return &weights[0]; + } + + /// Configures strategy parameters like recombination weights, cumulation + /// and adaption rates according to Hansen (2014, + /// http://cma.gforge.inria.fr/purecmaes.m). + void with_strategy_parameters (); + + /// The problem dimension. + natural n = 10; + + /// The parent number. + natural parent_number = 20; + + /// The population size. + natural population_size = 40; + + /// The covariance matrix update modulus. + natural update_modulus = 1; + + /// The accuracy goal. + real accuracy_goal = 1.0E-6; + + /// The random seed. + word64 random_seed = 271828; + + /// The stop generation. + natural stop_generation = 1000; + + /// The recombination weights. + std::valarray weights; + + /// The variance of the recombination weights. + real wv; + + /// The step size cumulation rate. + real cs; + + /// The distribution cumulation rate. + real cc; + + /// The rank-µ covariance matrix adaption rate. + real acov; + + /// The rank-1 covariance matrix adaption rate. + real ccov; + + /// The step size damping. + real step_size_damping; + + friend class Optimizer; + }; + + /// The optimization result. + class Result + { + public: + /// The destructor. + ~Result (); + + /// Returns the covariance matrix (upper triangular part only, in + /// column-major layout). + /// + /// @return the covariance matrix. + const std::valarray & + get_covariance_matrix () const + { + return C; + } + + /// Returns the distribution cumulation path. + /// + /// @return the distribution cumulation path. + const std::valarray & + get_distribution_cumulation_path () const + { + return pc; + } + + /// Returns the optimized fitness. + /// + /// @return the optimized fitness. + real + get_fitness () const + { + return y; + } + + /// Returns the final generation number. + /// + /// @return the final generation number. + natural + get_generation_number () const + { + return g; + } + + /// Returns the final global step size. + /// + /// @return the final global step size. + real + get_global_step_size () const + { + return s; + } + + /// Returns the final local step sizes. + /// + /// @return the final local step sizes. + const std::valarray & + get_local_step_sizes () const + { + return d; + } + + /// Returns the optimized parameter values. + /// + /// @return the optimized parameter values. + const std::valarray & + get_parameter_values () const + { + return x; + } + + /// Returns the parameter uncertainties. + /// + /// @return the parameter uncertainties. + const std::valarray & + get_parameter_uncertainties () const + { + return z; + } + + /// Returns the final rotation matrix (in column-major layout). + /// + /// @return the final rotation matrix. + const std::valarray & + get_rotation_matrix () const + { + return B; + } + + /// Returns the step size cumulation path. + /// + /// @return the step size cumulation path. + const std::valarray & + get_step_size_cumulation_path () const + { + return ps; + } + + /// Returns the optimization status flag. + /// + /// @return the optimization status flag. + bool + is_optimized () const + { + return optimized; + } + + /// Returns the mutation variance underflow status flag. + /// + /// @return the mutation variance underflow status flag. + bool + is_underflow () const + { + return underflow; + } + + private: + /// The constructor. + /// + /// @param[in] n The problem dimension. + /// @param[in] x The initial parameter values. + /// @param[in] d The initial local step sizes. + /// @param[in] s The initial global step size. + Result (natural n, const std::valarray &x, + const std::valarray &d, real s); + + /// Returns a pointer to the covariance matrix. + /// + /// @return a pointer to the covariance matrix. + real * + get_covariance_matrix_pointer () + { + return &C[0]; + } + + /// Returns a pointer to the distribution cumulation path. + /// + /// @return a pointer to the distribution cumulation path. + real * + get_distribution_cumulation_path_pointer () + { + return &pc[0]; + } + + /// Returns a reference to the fitness. + /// + /// @return a reference to the fitness. + real & + __fitness () + { + return y; + } + + /// Returns a reference to the generation number. + /// + /// @return a reference to the generation number. + natural & + __generation_number () + { + return g; + } + + /// Returns a reference to the global step size. + /// + /// @return a reference to the global step size. + real & + __global_step_size () + { + return s; + } + + /// Returns a pointer to the local step sizes. + /// + /// @return a pointer to the local step sizes. + real * + get_local_step_sizes_pointer () + { + return &d[0]; + } + + /// Returns a pointer to the parameter values. + /// + /// @return a pointer to the parameter values. + real * + get_parameter_values_pointer () + { + return &x[0]; + } + + /// Returns a pointer to the parameter uncertainties. + /// + /// @return a pointer to the parameter uncertainties. + real * + get_parameter_uncertainties_pointer () + { + return &z[0]; + } + + /// Returns a pointer to the rotation matrix. + /// + /// @return a pointer to the rotation matrix. + real * + get_rotation_matrix_pointer () + { + return &B[0]; + } + + /// Returns a pointer to the step size cumulation path. + /// + /// @return a pointer to the step size cumulation path. + real * + get_step_size_cumulation_path_pointer () + { + return &ps[0]; + } + + /// Returns a reference to the optimization status flag. + /// + /// @return a reference to the optimization status flag. + bool & + __optimized () + { + return optimized; + } + + /// Returns a reference to the mutation variance underflow status flag. + /// + /// @return a reference to the mutation variance underflow status flag. + bool & + __underflow () + { + return underflow; + } + + /// The optimized parameter values. + std::valarray x; + + /// The final local step sizes. + std::valarray d; + + /// The final global step size. + real s; + + /// The parameter uncertainties. + std::valarray z; + + /// The optimized fitness. + real y; + + /// The final rotation matrix (in column-major layout). + std::valarray B; + + /// The final covariance matrix (upper triangular part only, in + /// column-major layout). + std::valarray C; + + /// The distribution cumulation path. + std::valarray pc; + + /// The step size cumulation path. + std::valarray ps; + + /// The optimization status flag. + bool optimized; + + /// The mutation variance underflow status flag. + bool underflow; + + /// The final generation number. + natural g; + + friend class Optimizer; + }; + + /// The destructor. + ~Optimizer (); + + /// Maximizes an objective function. + /// + /// @tparam F The function type. + /// @tparam Constraint The constraint type. + /// @tparam Tracing The tracer type. + /// + /// @param[in] f The objective function. + /// @param[in] x The initial parameter values. + /// @param[in] d The initial local step sizes. + /// @param[in] s The initial global step size. + /// @param[in] constraint The constraint. + /// @param[in] tracer The tracer. + /// + /// @return the maximization result. + template + Result + maximize (const F &f, const std::valarray &x, + const std::valarray &d, const real &s, + const Constraint &constraint, const Tracing &tracer) const + { + return optimize (f, x, d, s, constraint, tracer, std::greater ()); + } + + /// @overload + template + Result + maximize (const F &f, const std::valarray &x, + const std::valarray &d, const real &s) const + { + return optimize (f, x, d, s, No_Constraint (), No_Tracing (), + std::greater ()); + } + + /// Minimizes an objective function. + /// + /// @tparam F The function type. + /// @tparam Constraint The constraint type. + /// @tparam Tracing The tracer type. + /// + /// @param[in] f The objective function. + /// @param[in] x The initial parameter values. + /// @param[in] d The initial local step sizes. + /// @param[in] s The initial global step size. + /// @param[in] constraint The constraint. + /// @param[in] tracer The tracer. + /// + /// @return the minimization result. + template + Result + minimize (const F &f, const std::valarray &x, + const std::valarray &d, const real &s, + const Constraint &constraint, const Tracing &tracer) const + { + return optimize (f, x, d, s, constraint, tracer, std::less ()); + } + + /// @overload + template + Result + minimize (const F &f, const std::valarray &x, + const std::valarray &d, const real &s) const + { + return optimize (f, x, d, s, No_Constraint (), No_Tracing (), + std::less ()); + } + +private: + /// Creates a new instance of this class with the build configuration + /// supplied as argument. + /// + /// @param[in] builder The build configuration. + explicit Optimizer (const Builder &builder); + + /// Optimizes an objective function. + /// + /// @tparam F The function type. + /// @tparam Constraint The constraint type. + /// @tparam Tracing The tracer type. + /// @tparam Compare The fitness comparator type. + /// + /// @param[in] f The objective function. + /// @param[in] x The initial parameter values. + /// @param[in] d The initial local step sizes. + /// @param[in] s The initial global step size. + /// @param[in] constraint The constraint. + /// @param[in] tracer The tracer. + /// @param[in] compare The fitness comparator. + /// + /// @return the optimization result. + template + Result + optimize (const F &f, const std::valarray &x, + const std::valarray &d, const real &s, + const Constraint &constraint, const Tracing &tracer, + const Compare &compare) const + { + using especia::optimize; + using especia::postopti; + + const natural n = config.get_problem_dimension (); + + Result result (n, x, d, s); + + optimize ( + f, constraint, n, config.get_parent_number (), + config.get_population_size (), config.get_weights_pointer (), + config.get_step_size_damping (), + config.get_step_size_cumulation_rate (), + config.get_distribution_cumulation_rate (), + config.get_rank_1_covariance_matrix_adaption_rate (), + config.get_rank_m_covariance_matrix_adaption_rate (), + config.get_covariance_update_modulus (), config.get_accuracy_goal (), + config.get_stop_generation (), result.__generation_number (), + result.get_parameter_values_pointer (), result.__global_step_size (), + result.get_local_step_sizes_pointer (), + result.get_rotation_matrix_pointer (), + result.get_covariance_matrix_pointer (), + result.get_step_size_cumulation_path_pointer (), + result.get_distribution_cumulation_path_pointer (), + result.__fitness (), result.__optimized (), result.__underflow (), + deviate, decompose, compare, tracer); + + if (result.__optimized ()) + { + postopti (f, constraint, n, result.get_parameter_values_pointer (), + result.get_local_step_sizes_pointer (), + result.get_rotation_matrix_pointer (), + result.get_covariance_matrix_pointer (), + result.get_global_step_size (), + result.get_parameter_uncertainties_pointer ()); + } + + return result; + } + + /// The build configuration. + const Builder config; + + /// The eigenvalue decomposition strategy. + const Decompose decompose; + + /// The random number generator. + const Normal_Deviate deviate; +}; } diff --git a/src/main/cxx/core/profiles.cxx b/src/main/cxx/core/profiles.cxx index 43e59c6..be70a64 100644 --- a/src/main/cxx/core/profiles.cxx +++ b/src/main/cxx/core/profiles.cxx @@ -12,21 +12,22 @@ using std::log; using std::pow; using std::sqrt; -using especia::real; using especia::micro; using especia::pi; +using especia::real; using especia::sq; using especia::sqrt_of_ln_two; using especia::sqrt_of_pi; - /// The Gaussian. /// /// @param[in] x The abscissa value (arbitrary unit). /// @param[in] gamma The width (arbitrary unit). /// @return the value of the Gaussian at @c x. -static real f_g(const real &x, const real &gamma) { - return (1.0 / (sqrt_of_pi * gamma)) * exp(-sq(x / gamma)); +static real +f_g (const real &x, const real &gamma) +{ + return (1.0 / (sqrt_of_pi * gamma)) * exp (-sq (x / gamma)); } /// The Lorentzian. @@ -34,8 +35,10 @@ static real f_g(const real &x, const real &gamma) { /// @param[in] x The abscissa value (arbitrary unit). /// @param[in] gamma The width (arbitrary unit). /// @return the value of the Lorentzian at @c x. -static real f_l(const real &x, const real &gamma) { - return 1.0 / ((pi * gamma) * (1.0 + sq(x / gamma))); +static real +f_l (const real &x, const real &gamma) +{ + return 1.0 / ((pi * gamma) * (1.0 + sq (x / gamma))); } /// The irrational function used in the extended pseudo-Voigt approximation. @@ -43,142 +46,195 @@ static real f_l(const real &x, const real &gamma) { /// @param[in] x The abscissa value (arbitrary unit). /// @param[in] gamma The width (arbitrary unit). /// @return the value of the function at @c x. -static real f_i(const real &x, const real &gamma) { - return 1.0 / ((2.0 * gamma) * pow(1.0 + sq(x / gamma), 1.5)); +static real +f_i (const real &x, const real &gamma) +{ + return 1.0 / ((2.0 * gamma) * pow (1.0 + sq (x / gamma), 1.5)); } -/// The squared hyperbolic secant function used in the extended pseudo-Voigt approximation. +/// The squared hyperbolic secant function used in the extended pseudo-Voigt +/// approximation. /// /// @param[in] x The abscissa value (arbitrary unit). /// @param[in] gamma The width (arbitrary unit). /// @return the value of the function at @c x. -static real f_p(const real &x, const real &gamma) { - return 1.0 / (2.0 * gamma * sq(cosh(x / gamma))); -} - -template -static real truncate(const F &f, const real &x, const real &b, const real &c) { - return abs(x) < c * b ? f(x, b) : real(0.0); -} - -template -static T poly(const T &x, const T &h0, const T &h1, const T &h2, const T &h3, const T &h4, const T &h5, const T &h6) { - return h0 + x * (h1 + x * (h2 + x * (h3 + x * (h4 + x * (h5 + x * h6))))); -} - -static real poly_w_g(const real &r) { - return 1.0 - r * poly(r, 0.66000, 0.15021, -1.24984, 4.74052, -9.48291, 8.48252, -2.95553); -} - -static real poly_w_l(const real &r) { - return 1.0 - (1.0 - r) * poly(r, -0.42179, -1.25693, 10.30003, -23.45651, 29.14158, -16.50453, 3.19974); -} - -static real poly_w_i(const real &r) { - return poly(r, 1.19913, 1.43021, -15.36331, 47.06071, -73.61822, 57.92559, -17.80614); -} - -static real poly_w_p(const real &r) { - return poly(r, 1.10186, -0.47745, -0.68688, 2.76622, -4.55466, 4.05475, -1.26571); -} - -static real poly_eta_l(const real &r) { - return r * (1.0 + (1.0 - r) * poly(r, -0.30165, -1.38927, 9.31550, -24.10743, 34.96491, -21.18862, 3.70290)); -} - -static real poly_eta_i(const real &r) { - return (r * (1.0 - r)) * poly(r, 0.25437, -0.14107, 3.23653, -11.09215, 22.10544, -24.12407, 9.76947); -} - -static real poly_eta_p(const real &r) { - return (r * (1.0 - r)) * poly(r, 1.01579, 1.50429, -9.21815, 23.59717, -39.71134, 32.83023, -10.02142); -} - - -especia::Pseudo_Voigt::Pseudo_Voigt(const real &b, const real &d) - : rho(c_g * b / d), - h(1.0 / pow(1.0 + rho * (0.07842 + rho * (4.47163 + rho * (2.42843 + rho * (rho + 2.69296)))), 0.2)), - // transposed digits in T. Ida, M. Ando, H. Toraya (2000) ^^ - gamma_l(d / h), - gamma_g(gamma_l / c_g), - eta(h * (1.36603 - h * (0.47719 - h * 0.11116))) { -} - -especia::Pseudo_Voigt::~Pseudo_Voigt() = default; - -real especia::Pseudo_Voigt::operator()(const real &x) const { - return (1.0 - eta) * f_g(x, gamma_g) + eta * f_l(x, gamma_l); +static real +f_p (const real &x, const real &gamma) +{ + return 1.0 / (2.0 * gamma * sq (cosh (x / gamma))); +} + +template +static real +truncate (const F &f, const real &x, const real &b, const real &c) +{ + return abs (x) < c * b ? f (x, b) : real (0.0); +} + +template +static T +poly (const T &x, const T &h0, const T &h1, const T &h2, const T &h3, + const T &h4, const T &h5, const T &h6) +{ + return h0 + x * (h1 + x * (h2 + x * (h3 + x * (h4 + x * (h5 + x * h6))))); +} + +static real +poly_w_g (const real &r) +{ + return 1.0 + - r + * poly (r, 0.66000, 0.15021, -1.24984, 4.74052, -9.48291, + 8.48252, -2.95553); +} + +static real +poly_w_l (const real &r) +{ + return 1.0 + - (1.0 - r) + * poly (r, -0.42179, -1.25693, 10.30003, -23.45651, 29.14158, + -16.50453, 3.19974); +} + +static real +poly_w_i (const real &r) +{ + return poly (r, 1.19913, 1.43021, -15.36331, 47.06071, -73.61822, 57.92559, + -17.80614); +} + +static real +poly_w_p (const real &r) +{ + return poly (r, 1.10186, -0.47745, -0.68688, 2.76622, -4.55466, 4.05475, + -1.26571); +} + +static real +poly_eta_l (const real &r) +{ + return r + * (1.0 + + (1.0 - r) + * poly (r, -0.30165, -1.38927, 9.31550, -24.10743, 34.96491, + -21.18862, 3.70290)); +} + +static real +poly_eta_i (const real &r) +{ + return (r * (1.0 - r)) + * poly (r, 0.25437, -0.14107, 3.23653, -11.09215, 22.10544, -24.12407, + 9.76947); +} + +static real +poly_eta_p (const real &r) +{ + return (r * (1.0 - r)) + * poly (r, 1.01579, 1.50429, -9.21815, 23.59717, -39.71134, 32.83023, + -10.02142); +} + +especia::Pseudo_Voigt::Pseudo_Voigt (const real &b, const real &d) + : rho (c_g * b / d), + h (1.0 + / pow ( + 1.0 + + rho + * (0.07842 + + rho + * (4.47163 + + rho * (2.42843 + rho * (rho + 2.69296)))), + 0.2)), + // transposed digits in T. Ida, M. Ando, H. Toraya (2000) ^^ + gamma_l (d / h), gamma_g (gamma_l / c_g), + eta (h * (1.36603 - h * (0.47719 - h * 0.11116))) +{ +} + +especia::Pseudo_Voigt::~Pseudo_Voigt () = default; + +real +especia::Pseudo_Voigt::operator() (const real &x) const +{ + return (1.0 - eta) * f_g (x, gamma_g) + eta * f_l (x, gamma_l); } const real especia::Pseudo_Voigt::c_g = sqrt_of_ln_two; - -especia::Extended_Pseudo_Voigt::Extended_Pseudo_Voigt(const real &b, const real &d) - : g(c_g * b + d), - rho(d / g), - gamma_g(g * poly_w_g(rho) / c_g), - gamma_l(g * poly_w_l(rho)), - gamma_i(g * poly_w_i(rho) / c_i), - gamma_p(g * poly_w_p(rho) / c_p), - eta_l(poly_eta_l(rho)), - eta_i(poly_eta_i(rho)), - eta_p(poly_eta_p(rho)) { +especia::Extended_Pseudo_Voigt::Extended_Pseudo_Voigt (const real &b, + const real &d) + : g (c_g * b + d), rho (d / g), gamma_g (g * poly_w_g (rho) / c_g), + gamma_l (g * poly_w_l (rho)), gamma_i (g * poly_w_i (rho) / c_i), + gamma_p (g * poly_w_p (rho) / c_p), eta_l (poly_eta_l (rho)), + eta_i (poly_eta_i (rho)), eta_p (poly_eta_p (rho)) +{ } -especia::Extended_Pseudo_Voigt::~Extended_Pseudo_Voigt() = default; +especia::Extended_Pseudo_Voigt::~Extended_Pseudo_Voigt () = default; -real especia::Extended_Pseudo_Voigt::operator()(const real &x) const { - return (1.0 - eta_l - eta_i - eta_p) * f_g(x, gamma_g) + - eta_l * f_l(x, gamma_l) + - eta_i * f_i(x, gamma_i) + - eta_p * f_p(x, gamma_p); +real +especia::Extended_Pseudo_Voigt::operator() (const real &x) const +{ + return (1.0 - eta_l - eta_i - eta_p) * f_g (x, gamma_g) + + eta_l * f_l (x, gamma_l) + eta_i * f_i (x, gamma_i) + + eta_p * f_p (x, gamma_p); } const real especia::Extended_Pseudo_Voigt::c_g = sqrt_of_ln_two; -const real especia::Extended_Pseudo_Voigt::c_i = sqrt(pow(2.0, 2.0 / 3.0) - 1.0); // NOLINT -const real especia::Extended_Pseudo_Voigt::c_p = log(sqrt(2.0) + 1.0); // NOLINT - +const real especia::Extended_Pseudo_Voigt::c_i + = sqrt (pow (2.0, 2.0 / 3.0) - 1.0); // NOLINT +const real especia::Extended_Pseudo_Voigt::c_p + = log (sqrt (2.0) + 1.0); // NOLINT -especia::Many_Multiplet::Many_Multiplet() - : u(0.0), z(1.0), c(0.0), b(0.5), a(1.0) { +especia::Many_Multiplet::Many_Multiplet () + : u (0.0), z (1.0), c (0.0), b (0.5), a (1.0) +{ } -especia::Many_Multiplet::Many_Multiplet(const real q[]) - : u(1.0E+08 / (1.0E+08 / q[0] + q[6] * (q[7] * micro) * (q[7] * micro + 2.0))), - z((1.0 + q[2]) * (1.0 + q[3] / c0)), - c(u * z), - b(q[4] * c / c0), - a(c1 * q[1] * pow(10.0, q[5]) * (u * c)) { +especia::Many_Multiplet::Many_Multiplet (const real q[]) + : u (1.0E+08 + / (1.0E+08 / q[0] + q[6] * (q[7] * micro) * (q[7] * micro + 2.0))), + z ((1.0 + q[2]) * (1.0 + q[3] / c0)), c (u * z), b (q[4] * c / c0), + a (c1 * q[1] * pow (10.0, q[5]) * (u * c)) +{ } -especia::Many_Multiplet::~Many_Multiplet() = default; +especia::Many_Multiplet::~Many_Multiplet () = default; -real especia::Many_Multiplet::operator()(const real &x) const { - return a * truncate(f_g, x - c, b, 4.0); +real +especia::Many_Multiplet::operator() (const real &x) const +{ + return a * truncate (f_g, x - c, b, 4.0); } const real especia::Many_Multiplet::c0 = 1.0E-03 * speed_of_light; -const real especia::Many_Multiplet::c1 = 1.0E-06 * sq(elementary_charge) / // NOLINT - (4.0 * electric_constant * electron_mass * sq(speed_of_light)); - +const real especia::Many_Multiplet::c1 + = 1.0E-06 * sq (elementary_charge) / // NOLINT + (4.0 * electric_constant * electron_mass * sq (speed_of_light)); -especia::Intergalactic_Doppler::Intergalactic_Doppler() - : z(1.0), c(0.0), b(0.5), a(1.0) { +especia::Intergalactic_Doppler::Intergalactic_Doppler () + : z (1.0), c (0.0), b (0.5), a (1.0) +{ } -especia::Intergalactic_Doppler::Intergalactic_Doppler(const real q[]) - : z((1.0 + q[2]) * (1.0 + q[3] / c0)), - c(q[0] * z), - b(q[4] * c / c0), - a(c1 * q[1] * pow(10.0, q[5]) * (q[0] * c)) { +especia::Intergalactic_Doppler::Intergalactic_Doppler (const real q[]) + : z ((1.0 + q[2]) * (1.0 + q[3] / c0)), c (q[0] * z), b (q[4] * c / c0), + a (c1 * q[1] * pow (10.0, q[5]) * (q[0] * c)) +{ } -especia::Intergalactic_Doppler::~Intergalactic_Doppler() = default; +especia::Intergalactic_Doppler::~Intergalactic_Doppler () = default; -real especia::Intergalactic_Doppler::operator()(const real &x) const { - return a * truncate(f_g, x - c, b, 4.0); +real +especia::Intergalactic_Doppler::operator() (const real &x) const +{ + return a * truncate (f_g, x - c, b, 4.0); } const real especia::Intergalactic_Doppler::c0 = 1.0E-03 * speed_of_light; -const real especia::Intergalactic_Doppler::c1 = 1.0E-06 * sq(elementary_charge) / // NOLINT - (4.0 * electric_constant * electron_mass * sq(speed_of_light)); +const real especia::Intergalactic_Doppler::c1 + = 1.0E-06 * sq (elementary_charge) / // NOLINT + (4.0 * electric_constant * electron_mass * sq (speed_of_light)); diff --git a/src/main/cxx/core/profiles.h b/src/main/cxx/core/profiles.h index d0a94ee..7a13168 100644 --- a/src/main/cxx/core/profiles.h +++ b/src/main/cxx/core/profiles.h @@ -11,480 +11,526 @@ #include "base.h" -namespace especia { - - /// The pseudo-Voigt approximation to the Voigt function. The Voigt function is - /// defined as the convolution of a Gaussian and a Lorentzian function. - /// - /// Further reading: - /// - /// T. Ida, M. Ando, H. Toraya (2000). - /// *Extended pseudo-Voigt function for approximating the Voigt profile.* - /// J. Appl. Chryst., 33, 1311, ISSN 0021-8898. - /// - /// @remark This class is thread safe. - class Pseudo_Voigt { - public: - /// Creates a new pseudo-Voigt approximation to the Voigt function. - /// - /// @param[in] b The width of the Gaussian (arbitrary unit). - /// @param[in] d The width of the Lorentzian (arbitrary unit). - explicit Pseudo_Voigt(const real &b = 0.5, const real &d = 0.5); - - /// The destructor. - ~Pseudo_Voigt(); - - /// Returns the value of the pseudo-Voigt approximation at a given abscissa value. - /// - /// @param[in] x The abscissa value (arbitrary unit). - /// @return the value of the pseudo-Voigt approximation at @c x. - real operator()(const real &x) const; - - private: - const real rho; - const real h; - const real gamma_l; - const real gamma_g; - const real eta; - - static const real c_g; - }; - - - /// The extended pseudo-Voigt approximation to the Voigt function. The Voigt function - /// is defined as the convolution of a Gaussian and a Lorentzian function. - /// - /// Further reading: - /// - /// T. Ida, M. Ando, H. Toraya (2000) - /// *Extended pseudo-Voigt function for approximating the Voigt profile.* - /// J. Appl. Chryst., 33, 1311, ISSN 0021-8898. - /// - /// @remark This class is thread safe. - class Extended_Pseudo_Voigt { - public: - /// Creates a new extended pseudo-Voigt approximation to the Voigt function. - /// - /// @param[in] b The width of the Gaussian (arbitrary unit). - /// @param[in] d The width of the Lorentzian (arbitrary unit). - explicit Extended_Pseudo_Voigt(const real &b = 0.5, const real &d = 0.5); - - /// The destructor. - ~Extended_Pseudo_Voigt(); - - /// Returns the value of the extended pseudo-Voigt approximation at a given abscissa value. - /// - /// @param[in] x The abscissa value (arbitrary unit). - /// @return the value of the extended pseudo-Voigt approximation at @c x. - real operator()(const real &x) const; - - private: - const real g; - const real rho; - const real gamma_g; - const real gamma_l; - const real gamma_i; - const real gamma_p; - const real eta_l; - const real eta_i; - const real eta_p; - - static const real c_g; - static const real c_i; - static const real c_p; - }; - - /// The (Doppler) profile to infer the variation of the fine-structure constant - /// alpha by means of a many-multiplet analysis. - /// - /// Further reading: - /// - /// R. Quast, D. Reimers and S. A. Levshakov (2004). - /// *Probing the variability of the fine-structure constant with the VLT/UVES.* - /// Astronomy and Astrophysics, 415, L7. - /// https://dx.doi.org/10.1051/0004-6361:20040013 - /// - /// @remark This class is thread safe. - class Many_Multiplet { - public: - /// Default constructor. - Many_Multiplet(); - - /// Creates a new Doppler profile with the parameter values specified. - /// - /// @param[in] q - /// @parblock - /// The parameter values. The parameters are: - /// - /// @c q[0] The rest wavelength (Angstrom). - /// - /// @c q[1] The oscillator strength. - /// - /// @c q[2] The cosmological redshift. - /// - /// @c q[3] The radial velocity (km s-1). - /// - /// @c q[4] The line broadening velocity (km s-1). - /// - /// @c q[5] The decadic logarithm of the particle column number density (cm-2). - /// - /// @c q[6] The relativistic correction coefficient. - /// - /// @c q[7] The variation of the fine-structure constant (1E-6). - /// @endparblock - explicit Many_Multiplet(const real q[]); - - /// The destructor. - ~Many_Multiplet(); - - /// Returns the optical depth of the profile at a given wavelength. - /// - /// @param[in] x The wavelength (Angstrom). - /// @return the optical depth of the profile at @c x. - real operator()(const real &x) const; - - /// Returns the central wavelength of the profile. - /// - /// @return the central wavelength of the profile (Angstrom). - real center() const { - return c; - } - - /// Returns the redshift factor of the profile due to cosmology and proper motion. - /// - /// @return the redshift factor. - real redshift_factor() const { - return z; - } - - /// Returns the number of parameters. - static natural parameter_count() { - return n; - }; - - private: - /// The modified rest wavelength (Angstrom). - const real u; - - /// The redshift factor due to cosmology and proper motion. - const real z; - - /// The central wavelength (Angstrom). - const real c; - - /// The Doppler width (Angstrom). - const real b; - - /// The amplitude. - const real a; - - /// The number of parameters. - static const natural n = 8; - - static const real c0; - static const real c1; - }; - - - /// The Doppler profile to model intergalactic absorption lines. - /// - /// @remark This class is thread safe. - class Intergalactic_Doppler { - public: - /// Default constructor. - Intergalactic_Doppler(); - - /// Creates a new Doppler profile with the parameter values specified. - /// - /// @param[in] q - /// @parblock - /// The parameter values. The parameters are: - /// - /// @c q[0] The rest wavelength (Angstrom). - /// - /// @c q[1] The oscillator strength. - /// - /// @c q[2] The cosmological redshift. - /// - /// @c q[3] The radial velocity (km s-1). - /// - /// @c q[4] The line broadening velocity (km s-1). - /// - /// @c q[5] The decadic logarithm of the particle column number density (cm-2). - /// @endparblock - explicit Intergalactic_Doppler(const real q[]); - - /// The destructor. - ~Intergalactic_Doppler(); - - /// Returns the optical depth of the profile at a given wavelength. - /// - /// @param[in] x The wavelength (Angstrom). - /// @return the optical depth of the profile at @c x. - real operator()(const real &x) const; - - /// Returns the central wavelength of the profile. - /// - /// @return the central wavelength of the profile (Angstrom). - real center() const { - return c; - } - - /// Returns the redshift factor of the profile due to cosmology and proper motion. - /// - /// @return the redshift factor. - real redshift_factor() const { - return z; - } - - /// Returns the number of parameters. - static natural parameter_count() { - return n; - }; - - private: - /// The redshift factor due to cosmology and proper motion. - const real z; - - /// The central wavelength (Angstrom). - const real c; - - /// The Doppler width (Angstrom). - const real b; - - /// The amplitude. - const real a; - - /// The number of parameters. - static const natural n = 6; - - static const real c0; - static const real c1; - }; - - - /// The Voigt profile to model intergalactic spectral lines. - /// - /// @tparam A The strategy to approximate the Voigt function. - /// - /// @remark This class is thread safe. - template - class Intergalactic_Voigt { - public: - /// Default constructor. - Intergalactic_Voigt() - : z(1.0), c(0.0), a(1.0), approximation() { - }; - - /// Creates a new Voigt profile with the parameter values specified. - /// - /// @param[in] q - /// @parblock - /// The parameter values. The parameters are: - /// - /// @c q[0] The rest wavelength (Angstrom). - /// - /// @c q[1] The oscillator strength. - /// - /// @c q[2] The cosmological redshift. - /// - /// @c q[3] The radial velocity (km s-1). - /// - /// @c q[4] The line broadening velocity (km s-1). - /// - /// @c q[5] The decadic logarithm of the particle column number density (cm-2). - /// - /// @c q[6] The damping constant (s-1). - /// @endparblock - explicit Intergalactic_Voigt(const real q[]) - : z((1.0 + q[2]) * (1.0 + q[3] / c0)), - c(q[0] * z), - a(c1 * q[1] * std::pow(10.0, q[5]) * (q[0] * c)), - approximation(q[4] * c / c0, c2 * q[6] * (q[0] * c)) { - } - - /// The destructor. - ~Intergalactic_Voigt() = default; - - /// Returns the optical depth of the profile at a given wavelength. - /// - /// @param[in] x The wavelength (Angstrom). - /// @return the optical depth of the profile at @c x. - real operator()(const real &x) const { - return a * approximation(x - c); - }; - - /// Returns the central wavelength of the profile. - /// - /// @return the central wavelength of the profile (Angstrom). - real center() const { - return c; - } - - /// Returns the redshift factor of the profile due to cosmology and proper motion. - /// - /// @return the redshift factor. - real redshift_factor() const { - return z; - } - - /// Returns the number of parameters. - static natural parameter_count() { - return n; - }; - - private: - /// The redshift factor due to cosmology and proper motion. - const real z; - - /// The central wavelength (Angstrom). - const real c; - - /// The amplitude. - const real a; - - /// The approximation. - const A approximation; - - /// The number of parameters. - static const natural n = 7; - - static const real c0; - static const real c1; - static const real c2; - }; - - template - const real Intergalactic_Voigt::c0 = 1.0E-03 * speed_of_light; - - template - const real Intergalactic_Voigt::c1 = 1.0E-06 * sq(elementary_charge) / // NOLINT - (4.0 * electric_constant * electron_mass * sq(speed_of_light)); - - template - const real Intergalactic_Voigt::c2 = 1.0E-10 / (4.0 * pi * speed_of_light); - - - /// The superposition of many optical depth profiles. - /// - /// @tparam Function The profile type. - /// - /// @remark This class is thread safe, if the profile type is thread safe. - template - class Superposition { - public: - /// Constructs a new superposition of profiles with the parameter values specified. - /// - /// @param[in] n The number of profiles. - /// @param[in] q The parameter values. The semantics of parameter values and the - /// number of parameters per component are defined by the profile type. - Superposition(natural n, const real q[]) : profiles() { - profiles.reserve(n); - for (natural i = 0; i < n; ++i, q += Function::parameter_count()) { - profiles.emplace_back(q); - } - } - - /// The destructor. - ~Superposition() = default; - - /// Returns the optical depth of the profile superposition at a given wavelength. - /// - /// @param[in] x The wavelength (Angstrom). - /// @return the optical depth of the profile superposition at @c x. - real operator()(const real &x) const { - real t = 0.0; - - for (const Function &profile : profiles) { - t += profile(x); - } - - return t; - } - - private: - /// The line profiles. - std::vector profiles; - }; - - - /// Calculates the convolution of a line profile with an instrumental function. - /// - /// @tparam Integrate The strategy to compute the convolution integral. - template - class Convolutor { - public: - /// Constructs a new instance of this class using the integrator supplied as argument. - /// - /// @param integrator The integrator. - explicit Convolutor(const Integrate &integrator = Integrate()) : integrator(integrator) { - } - - /// The destructor. - ~Convolutor() = default; - - /// Calculates the convolution a line profile with an instrumental function, i.e the infinite integral - /// @f[ I(x) = \int_{-\infty}^{\infty} \exp(-f(x - y)) g(y) dy @f]. - /// - /// @tparam F The line profile function type. - /// @tparam G The instrumental function type. - /// - /// @param f The line profile function (optical depth profile). - /// @param g The instrumental function. - /// @param x The abscissa value. - /// @return the convolution integral. - template - real convolute(const F &f, const G &g, real x) const { - using std::exp; - - return integrator.integrate_infinite([&f, &g, x, this](real y) -> real { return exp(-f(x - y)) * g(y); }); - } - - private: - /// The strategy to compute the convolution integral. - const Integrate integrator; - }; - - - /// Calculates the equivalent width of a line profile. - /// - /// @tparam Integrate The strategy to integrate the line profile. - template - class Equivalent_Width_Calculator { - public: - /// Constructs a new instance of this class using the integrator supplied as argument. - /// - /// @param integrator The integrator. - explicit Equivalent_Width_Calculator(const Integrate &integrator = Integrate()) : integrator(integrator) { - - } - - /// The destructor. - ~Equivalent_Width_Calculator() = default; - - /// Calculates the rest equivalent width of an optical depth profile. - /// - /// @tparam Function The profile function type. - /// - /// @param f The profile function. - /// @param f The profile function. - /// @param prefix The unit prefix. - /// @return the equivalent width (@c prefix Angstrom). - template - real calculate(const Function &f, const real &prefix = 1.0) const { - using std::exp; - - const real integral = integrator.integrate_positive_infinite( - [&f](real x) -> real { return 1.0 - exp(-f(x + f.center())); }); - - return (2.0 / prefix) * integral / f.redshift_factor(); - } - - private: - /// The strategy to integrate the line profile. - const Integrate integrator; - }; +namespace especia +{ + +/// The pseudo-Voigt approximation to the Voigt function. The Voigt function is +/// defined as the convolution of a Gaussian and a Lorentzian function. +/// +/// Further reading: +/// +/// T. Ida, M. Ando, H. Toraya (2000). +/// *Extended pseudo-Voigt function for approximating the Voigt profile.* +/// J. Appl. Chryst., 33, 1311, ISSN 0021-8898. +/// +/// @remark This class is thread safe. +class Pseudo_Voigt +{ +public: + /// Creates a new pseudo-Voigt approximation to the Voigt function. + /// + /// @param[in] b The width of the Gaussian (arbitrary unit). + /// @param[in] d The width of the Lorentzian (arbitrary unit). + explicit Pseudo_Voigt (const real &b = 0.5, const real &d = 0.5); + + /// The destructor. + ~Pseudo_Voigt (); + + /// Returns the value of the pseudo-Voigt approximation at a given abscissa + /// value. + /// + /// @param[in] x The abscissa value (arbitrary unit). + /// @return the value of the pseudo-Voigt approximation at @c x. + real operator() (const real &x) const; + +private: + const real rho; + const real h; + const real gamma_l; + const real gamma_g; + const real eta; + + static const real c_g; +}; + +/// The extended pseudo-Voigt approximation to the Voigt function. The Voigt +/// function is defined as the convolution of a Gaussian and a Lorentzian +/// function. +/// +/// Further reading: +/// +/// T. Ida, M. Ando, H. Toraya (2000) +/// *Extended pseudo-Voigt function for approximating the Voigt profile.* +/// J. Appl. Chryst., 33, 1311, ISSN 0021-8898. +/// +/// @remark This class is thread safe. +class Extended_Pseudo_Voigt +{ +public: + /// Creates a new extended pseudo-Voigt approximation to the Voigt function. + /// + /// @param[in] b The width of the Gaussian (arbitrary unit). + /// @param[in] d The width of the Lorentzian (arbitrary unit). + explicit Extended_Pseudo_Voigt (const real &b = 0.5, const real &d = 0.5); + + /// The destructor. + ~Extended_Pseudo_Voigt (); + + /// Returns the value of the extended pseudo-Voigt approximation at a given + /// abscissa value. + /// + /// @param[in] x The abscissa value (arbitrary unit). + /// @return the value of the extended pseudo-Voigt approximation at @c x. + real operator() (const real &x) const; + +private: + const real g; + const real rho; + const real gamma_g; + const real gamma_l; + const real gamma_i; + const real gamma_p; + const real eta_l; + const real eta_i; + const real eta_p; + + static const real c_g; + static const real c_i; + static const real c_p; +}; + +/// The (Doppler) profile to infer the variation of the fine-structure constant +/// alpha by means of a many-multiplet analysis. +/// +/// Further reading: +/// +/// R. Quast, D. Reimers and S. A. Levshakov (2004). +/// *Probing the variability of the fine-structure constant with the +/// VLT/UVES.* Astronomy and Astrophysics, 415, L7. +/// https://dx.doi.org/10.1051/0004-6361:20040013 +/// +/// @remark This class is thread safe. +class Many_Multiplet +{ +public: + /// Default constructor. + Many_Multiplet (); + + /// Creates a new Doppler profile with the parameter values specified. + /// + /// @param[in] q + /// @parblock + /// The parameter values. The parameters are: + /// + /// @c q[0] The rest wavelength (Angstrom). + /// + /// @c q[1] The oscillator strength. + /// + /// @c q[2] The cosmological redshift. + /// + /// @c q[3] The radial velocity (km s-1). + /// + /// @c q[4] The line broadening velocity (km s-1). + /// + /// @c q[5] The decadic logarithm of the particle column number density + /// (cm-2). + /// + /// @c q[6] The relativistic correction coefficient. + /// + /// @c q[7] The variation of the fine-structure constant (1E-6). + /// @endparblock + explicit Many_Multiplet (const real q[]); + + /// The destructor. + ~Many_Multiplet (); + + /// Returns the optical depth of the profile at a given wavelength. + /// + /// @param[in] x The wavelength (Angstrom). + /// @return the optical depth of the profile at @c x. + real operator() (const real &x) const; + + /// Returns the central wavelength of the profile. + /// + /// @return the central wavelength of the profile (Angstrom). + real + center () const + { + return c; + } + + /// Returns the redshift factor of the profile due to cosmology and proper + /// motion. + /// + /// @return the redshift factor. + real + redshift_factor () const + { + return z; + } + + /// Returns the number of parameters. + static natural + parameter_count () + { + return n; + }; + +private: + /// The modified rest wavelength (Angstrom). + const real u; + + /// The redshift factor due to cosmology and proper motion. + const real z; + + /// The central wavelength (Angstrom). + const real c; + + /// The Doppler width (Angstrom). + const real b; + + /// The amplitude. + const real a; + + /// The number of parameters. + static const natural n = 8; + + static const real c0; + static const real c1; +}; + +/// The Doppler profile to model intergalactic absorption lines. +/// +/// @remark This class is thread safe. +class Intergalactic_Doppler +{ +public: + /// Default constructor. + Intergalactic_Doppler (); + + /// Creates a new Doppler profile with the parameter values specified. + /// + /// @param[in] q + /// @parblock + /// The parameter values. The parameters are: + /// + /// @c q[0] The rest wavelength (Angstrom). + /// + /// @c q[1] The oscillator strength. + /// + /// @c q[2] The cosmological redshift. + /// + /// @c q[3] The radial velocity (km s-1). + /// + /// @c q[4] The line broadening velocity (km s-1). + /// + /// @c q[5] The decadic logarithm of the particle column number density + /// (cm-2). + /// @endparblock + explicit Intergalactic_Doppler (const real q[]); + + /// The destructor. + ~Intergalactic_Doppler (); + + /// Returns the optical depth of the profile at a given wavelength. + /// + /// @param[in] x The wavelength (Angstrom). + /// @return the optical depth of the profile at @c x. + real operator() (const real &x) const; + + /// Returns the central wavelength of the profile. + /// + /// @return the central wavelength of the profile (Angstrom). + real + center () const + { + return c; + } + + /// Returns the redshift factor of the profile due to cosmology and proper + /// motion. + /// + /// @return the redshift factor. + real + redshift_factor () const + { + return z; + } + + /// Returns the number of parameters. + static natural + parameter_count () + { + return n; + }; + +private: + /// The redshift factor due to cosmology and proper motion. + const real z; + + /// The central wavelength (Angstrom). + const real c; + + /// The Doppler width (Angstrom). + const real b; + + /// The amplitude. + const real a; + + /// The number of parameters. + static const natural n = 6; + + static const real c0; + static const real c1; +}; + +/// The Voigt profile to model intergalactic spectral lines. +/// +/// @tparam A The strategy to approximate the Voigt function. +/// +/// @remark This class is thread safe. +template class Intergalactic_Voigt +{ +public: + /// Default constructor. + Intergalactic_Voigt () : z (1.0), c (0.0), a (1.0), approximation () {}; + + /// Creates a new Voigt profile with the parameter values specified. + /// + /// @param[in] q + /// @parblock + /// The parameter values. The parameters are: + /// + /// @c q[0] The rest wavelength (Angstrom). + /// + /// @c q[1] The oscillator strength. + /// + /// @c q[2] The cosmological redshift. + /// + /// @c q[3] The radial velocity (km s-1). + /// + /// @c q[4] The line broadening velocity (km s-1). + /// + /// @c q[5] The decadic logarithm of the particle column number density + /// (cm-2). + /// + /// @c q[6] The damping constant (s-1). + /// @endparblock + explicit Intergalactic_Voigt (const real q[]) + : z ((1.0 + q[2]) * (1.0 + q[3] / c0)), c (q[0] * z), + a (c1 * q[1] * std::pow (10.0, q[5]) * (q[0] * c)), + approximation (q[4] * c / c0, c2 * q[6] * (q[0] * c)) + { + } + + /// The destructor. + ~Intergalactic_Voigt () = default; + + /// Returns the optical depth of the profile at a given wavelength. + /// + /// @param[in] x The wavelength (Angstrom). + /// @return the optical depth of the profile at @c x. + real + operator() (const real &x) const + { + return a * approximation (x - c); + }; + + /// Returns the central wavelength of the profile. + /// + /// @return the central wavelength of the profile (Angstrom). + real + center () const + { + return c; + } + + /// Returns the redshift factor of the profile due to cosmology and proper + /// motion. + /// + /// @return the redshift factor. + real + redshift_factor () const + { + return z; + } + + /// Returns the number of parameters. + static natural + parameter_count () + { + return n; + }; + +private: + /// The redshift factor due to cosmology and proper motion. + const real z; + + /// The central wavelength (Angstrom). + const real c; + + /// The amplitude. + const real a; + + /// The approximation. + const A approximation; + + /// The number of parameters. + static const natural n = 7; + + static const real c0; + static const real c1; + static const real c2; +}; + +template +const real Intergalactic_Voigt::c0 = 1.0E-03 * speed_of_light; + +template +const real Intergalactic_Voigt::c1 + = 1.0E-06 * sq (elementary_charge) / // NOLINT + (4.0 * electric_constant * electron_mass * sq (speed_of_light)); + +template +const real Intergalactic_Voigt::c2 = 1.0E-10 / (4.0 * pi * speed_of_light); + +/// The superposition of many optical depth profiles. +/// +/// @tparam Function The profile type. +/// +/// @remark This class is thread safe, if the profile type is thread safe. +template class Superposition +{ +public: + /// Constructs a new superposition of profiles with the parameter values + /// specified. + /// + /// @param[in] n The number of profiles. + /// @param[in] q The parameter values. The semantics of parameter values and + /// the number of parameters per component are defined by the profile type. + Superposition (natural n, const real q[]) : profiles () + { + profiles.reserve (n); + for (natural i = 0; i < n; ++i, q += Function::parameter_count ()) + { + profiles.emplace_back (q); + } + } + + /// The destructor. + ~Superposition () = default; + + /// Returns the optical depth of the profile superposition at a given + /// wavelength. + /// + /// @param[in] x The wavelength (Angstrom). + /// @return the optical depth of the profile superposition at @c x. + real + operator() (const real &x) const + { + real t = 0.0; + + for (const Function &profile : profiles) + { + t += profile (x); + } + + return t; + } + +private: + /// The line profiles. + std::vector profiles; +}; + +/// Calculates the convolution of a line profile with an instrumental function. +/// +/// @tparam Integrate The strategy to compute the convolution integral. +template class Convolutor +{ +public: + /// Constructs a new instance of this class using the integrator supplied as + /// argument. + /// + /// @param integrator The integrator. + explicit Convolutor (const Integrate &integrator = Integrate ()) + : integrator (integrator) + { + } + + /// The destructor. + ~Convolutor () = default; + + /// Calculates the convolution a line profile with an instrumental function, + /// i.e the infinite integral + /// @f[ I(x) = \int_{-\infty}^{\infty} \exp(-f(x - y)) g(y) dy @f]. + /// + /// @tparam F The line profile function type. + /// @tparam G The instrumental function type. + /// + /// @param f The line profile function (optical depth profile). + /// @param g The instrumental function. + /// @param x The abscissa value. + /// @return the convolution integral. + template + real + convolute (const F &f, const G &g, real x) const + { + using std::exp; + + return integrator.integrate_infinite ([&f, &g, x, this] (real y) -> real { + return exp (-f (x - y)) * g (y); + }); + } + +private: + /// The strategy to compute the convolution integral. + const Integrate integrator; +}; + +/// Calculates the equivalent width of a line profile. +/// +/// @tparam Integrate The strategy to integrate the line profile. +template class Equivalent_Width_Calculator +{ +public: + /// Constructs a new instance of this class using the integrator supplied as + /// argument. + /// + /// @param integrator The integrator. + explicit Equivalent_Width_Calculator (const Integrate &integrator + = Integrate ()) + : integrator (integrator) + { + } + + /// The destructor. + ~Equivalent_Width_Calculator () = default; + + /// Calculates the rest equivalent width of an optical depth profile. + /// + /// @tparam Function The profile function type. + /// + /// @param f The profile function. + /// @param f The profile function. + /// @param prefix The unit prefix. + /// @return the equivalent width (@c prefix Angstrom). + template + real + calculate (const Function &f, const real &prefix = 1.0) const + { + using std::exp; + + const real integral = integrator.integrate_positive_infinite ( + [&f] (real x) -> real { return 1.0 - exp (-f (x + f.center ())); }); + + return (2.0 / prefix) * integral / f.redshift_factor (); + } + +private: + /// The strategy to integrate the line profile. + const Integrate integrator; +}; } #endif // ESPECIA_PROFILES_H - diff --git a/src/main/cxx/core/random.h b/src/main/cxx/core/random.h index cba112d..2bc72b4 100644 --- a/src/main/cxx/core/random.h +++ b/src/main/cxx/core/random.h @@ -12,420 +12,524 @@ #include "base.h" -namespace especia { - - /// A maximally equidistributed F2-linear generator (MELG). - /// - /// Further reading: - /// - /// S. Harase and T. Kimoto (2018). - /// Implementing 64-bit maximally equidistributed F2-linear generators with Mersenne prime period. - /// ACM Transactions on Mathematical Software, 44, 3, 30. - /// , - /// - /// @tparam w The the number of bits in a word. - /// @tparam n The parameter n. - /// @tparam m The parameter m. - /// @tparam l The parameter l. - /// @tparam mult1 A multiplier (used for initialisation). - /// @tparam mult2 A multiplier (used for initialisation). - /// @tparam mult3 A multiplier (used for initialisation). - template - class Melg { - public: - /// Constructs a new instance of this functor. - /// - /// @param[in] seed The seed. - explicit Melg(const word64 seed) : state(n + 1) { // NOLINT - const word64 seeds[] = {seed & 0x00000000FFFFFFFFULL, seed & 0xFFFFFFFF00000000ULL}; - - reset(2, seeds); - } - - /// Constructs a new instance of this functor. - /// - /// @param[in] seed_count The number of seeds. - /// @param[in] seeds The seeds. - Melg(const natural seed_count, const word64 seeds[]) : state(n + 1) { // NOLINT - reset(seed_count, seeds); - } - - /// The destructor. - ~Melg() = default; - - /// Returns a new real-valued random number in the interval [0, 1]. - /// - /// @return a real-valued random number in [0, 1]. - real operator()() const { - using std::numeric_limits; - // the maximum mantissa value for a real number - const real max_mantissa = - numeric_limits::max() >> (numeric_limits::digits - (w < numeric_limits::digits ? w : numeric_limits::digits)); - - return (w < numeric_limits::digits ? rand() : rand() >> (w - numeric_limits::digits)) * (1.0 / max_mantissa); - } - - /// Returns a new random word. - /// - /// @return a random word. - word64 rand() const { - word64 next = 0ULL; - - switch (cycle) { - case 1: - next = rock(index, index + 1); - roll(next, index + m); - next = twist(next, index, index + l); - index++; - if (index == n - m) { - cycle = 2; - } - break; - case 2: - next = rock(index, index + 1); - roll(next, index + m - n); - next = twist(next, index, index + l); - index++; - if (index == n - l) { - cycle = 3; - } - break; - case 3: - next = rock(index, index + 1); - roll(next, index + m - n); - next = twist(next, index, index - (n - l)); - index++; - if (index == n - 1) { - cycle = 4; - } - break; - case 4: - next = rock(n - 1, 0); - roll(next, m - 1); - next = twist(next, n - 1, index - (n - l)); - index = 0; - cycle = 1; - break; - } - - return next; - } - - private: - /// Resets this algorithm. - /// - /// @param[in] seed The seed. - void reset(const word64 seed) { - state[0] = seed; - - for (index = 1; index < n + 1; index++) { - state[index] = (state[index - 1] ^ (state[index - 1] >> (w - 2))) * mult1 + index; - } - - index = 0; - cycle = 1; - } - - /// Resets this algorithm. - /// - /// @param[in] seed_count The number of seeds. - /// @param[in] seeds The seeds. - void reset(const natural seed_count, const word64 seeds[]) { - using std::max; - - reset(19650218ULL); - - natural i = 1; - natural j = 0; - for (natural k = max(n, seed_count); k > 0; k--) { - state[i] = (state[i] ^ ((state[i - 1] ^ (state[i - 1] >> (w - 2))) * mult2)) + seeds[j] + j; - i++; - j++; - if (i >= n) { - state[0] = state[n - 1]; - i = 1; - } - if (j >= seed_count) { - j = 0; - } - } - for (natural k = n - 1; k > 0; k--) { - state[i] = (state[i] ^ ((state[i - 1] ^ (state[i - 1] >> (w - 2))) * mult3)) - i; - i++; - if (i >= n) { - state[0] = state[n - 1]; - i = 1; - } - } - state[n] = (state[n] ^ ((state[n - 1] ^ (state[n - 1] >> (w - 2))) * mult3)) - n; - state[0] = (state[0] | (1ULL << (w - 1))); - - index = 0; - cycle = 1; - } - - word64 rock(const natural i, const natural k) const { - return (state[i] & 0xFFFFFFFF80000000ULL) | (state[k] & 0x7FFFFFFFULL); - } - - void roll(const word64 word, const natural i) const { - state[n] = (word >> 1) ^ (((word & 1ULL) != 0ULL) ? 0x5C32E06DF730FC42ULL : 0ULL) ^ state[i] ^ (state[n] ^ (state[n] << 23)); - } - - word64 twist(const word64 word, const natural i, const natural k) const { - state[i] = word ^ (state[n] ^ (state[n] >> 33)); - return state[i] ^ (state[i] << 16) ^ (state[k] & 0x6AEDE6FD97B338ECULL); - } - - mutable std::valarray state; - mutable natural index; - mutable natural cycle; - }; - - /// The MELG19937-64 with 2,496 bytes of state and 64-bit output. - typedef Melg<64, 311, 81, 19, 6364136223846793005ULL, 3935559000370003845ULL, 2862933555777941757ULL> Melg19937_64; - - - /// The Mersenne twister algorithm to generate [0,1] uniformly distributed - /// random deviates. - /// - /// This functor template is based on the 2002/01/26 version coded by Takuji - /// Nishimura and Makoto Matsumoto(Matsumoto and Nishimura, 1998). - /// - /// The notation of template parameters follows Matsumoto and - /// Nishimura (1998, Table 2). - /// - /// Further reading: - /// - /// m. Matsumoto, T. Nishimura (1998). - /// *Mersenne Twister: A 623-dimensionally equidistributed uniform pseudorandom number generator.* - /// ACM Transactions on Modeling and Computer Simulation, 8, 3, ISSN 1049-3301. - /// - /// D. Knuth (1998). - /// *The art of computer programming 2. Seminumerical algorithms*. - /// Addison Wesley Longman, ISBN 0-201-89684-2. - /// - /// @tparam w The number of bits in a word. - /// @tparam n The parameter n. - /// @tparam m The parameter m. - /// @tparam r The parameter r. - /// @tparam a The parameter a. - /// @tparam u The parameter u. - /// @tparam d The parameter d. - /// @tparam s The parameter s. - /// @tparam b The parameter b. - /// @tparam t The parameter t. - /// @tparam c The parameter c. - /// @tparam l The parameter l. - /// @tparam mult1 A multiplier (used for initialisation). - /// @tparam mult2 A multiplier (used for initialisation). - /// @tparam mult3 A multiplier (used for initialisation). - template - class Mersenne_Twister { - public: - /// Constructs a new instance of this functor. - /// - /// @param[in] seed The seed. - explicit Mersenne_Twister(const word64 seed = 9600629759793949339ULL) : state(n) { // NOLINT - const word64 seeds[] = {seed & 0x00000000FFFFFFFFULL, seed & 0xFFFFFFFF00000000ULL}; - - reset(2, seeds); - } - - /// Constructs a new instance of this functor. - /// - /// @param[in] seed_count The number of seeds. - /// @param[in] seeds The seeds. - Mersenne_Twister(const natural seed_count, const word64 seeds[]) : state(n) { // NOLINT - reset(seed_count, seeds); - } - - /// The destructor. - ~Mersenne_Twister() = default; - - /// Returns a new real-valued random number in the interval [0, 1]. - /// - /// @return a real-valued random number in [0, 1]. - real operator()() const { - using std::numeric_limits; - // the maximum mantissa value for a real number - const real max_mantissa = - numeric_limits::max() >> (numeric_limits::digits - (w < numeric_limits::digits ? w : numeric_limits::digits)); - - return (w < numeric_limits::digits ? rand() : rand() >> (w - numeric_limits::digits)) * (1.0 / max_mantissa); - } - - /// Returns a new random word. - /// - /// @return a random word. - word64 rand() const { - if (index == n) { - for (natural k = 0; k < n - m; ++k) { - twist(k + m, k, k + 1); - } - - for (natural k = n - m; k < n - 1; ++k) { - twist(k + m - n, k, k + 1); - } - - twist(m - 1, n - 1, 0); - index = 0; - } - - word64 next = state[index]; - ++index; - - next ^= (next >> u) & d; - next ^= (next << s) & b; - next ^= (next << t) & c; - next ^= (next >> l); - - return next; - } - - private: - /// Resets this algorithm. - /// - /// @param[in] seed The seed. - void reset(const word64 seed) { - using std::numeric_limits; - - state[0] = seed & (numeric_limits::max() >> (numeric_limits::digits - w)); - for (natural k = 1; k < n; ++k) { - state[k] = ((state[k - 1] ^ (state[k - 1] >> (w - 2))) * mult1 + k) & - (numeric_limits::max() >> (numeric_limits::digits - w)); - } - index = n; - } - - /// Resets this algorithm. - /// - /// @param[in] seed_count The number of seeds. - /// @param[in] seeds The seeds. - void reset(const natural seed_count, const word64 seeds[]) { - using std::max; - using std::numeric_limits; - - reset(19650218ULL); - - natural i = 1; - for (natural j = 0, k = max(n, seed_count); k > 0; --k) { - state[i] = ((state[i] ^ ((state[i - 1] ^ (state[i - 1] >> (w - 2))) * mult2)) + seeds[j] + j) & - (numeric_limits::max() >> (numeric_limits::digits - w)); - if (++i >= n) { - state[0] = state[n - 1]; - i = 1; - } - if (++j >= seed_count) { - j = 0; - } - } - for (natural k = n - 1; k > 0; --k) { - state[i] = ((state[i] ^ ((state[i - 1] ^ (state[i - 1] >> (w - 2))) * mult3)) - i) & - (numeric_limits::max() >> (numeric_limits::digits - w)); - if (++i >= n) { - state[0] = state[n - 1]; - i = 1; - } - } - state[0] = (1ULL << (w - 1)); - index = n; - } - - void twist(const natural i, const natural j, const natural k) const { - using std::numeric_limits; - - state[j] = state[i] ^ (((state[j] & ((numeric_limits::max() - << (numeric_limits::digits - w + r)) - >> (numeric_limits::digits - w))) | (state[k] & (numeric_limits::max() - >> (numeric_limits::digits - r)))) >> 1); - if ((state[k] & 1ULL) == 1ULL) { - state[j] ^= a; - } - } - - mutable std::valarray state; - mutable natural index = 0; - }; - - /// The MT-1121A-32. - typedef Mersenne_Twister<32, 351, 175, 19, 0xE4BD75F5ULL, 11, 0xFFFFFFFFULL, 7, 0x655E5280ULL, - 15, 0xFFD58000ULL, 17, 1812433253ULL, 1664525ULL, 1566083941ULL> Mt11213a_32; - /// The MT-1121B-32. - typedef Mersenne_Twister<32, 351, 175, 19, 0xCCAB8EE7ULL, 11, 0xFFFFFFFFULL, 7, 0x31B6AB00ULL, - 15, 0xFFE50000ULL, 17, 1812433253ULL, 1664525ULL, 1566083941ULL> Mt11213b_32; - /// The MT-19937-32. - typedef Mersenne_Twister<32, 624, 397, 31, 0x9908B0DFULL, 11, 0xFFFFFFFFULL, 7, 0x9D2C5680ULL, - 15, 0xEFC60000ULL, 18, 1812433253ULL, 1664525ULL, 1566083941ULL> Mt19937_32; - - /// The MT-19937-64. - typedef Mersenne_Twister<64, 312, 156, 31, 0xB5026F5AA96619E9ULL, 29, 0x5555555555555555ULL, 17, 0x71D67FFFEDA60000ULL, - 37, 0xFFF7EEE000000000ULL, 43, 6364136223846793005ULL, 3935559000370003845ULL, 2862933555777941757ULL> Mt19937_64; - - - /// PCG algorithm to generate [0,1] uniformly distributed random deviates. Based on - /// Melissa E. O'Neill (2014) and . - /// - /// Further reading: - /// - /// Melissa E. O'Neill (2014). - /// *PCG: A Family of Simple Fast Space-Efficient Statistically Good Algorithms for Random Number Generation.* - /// . - /// - /// @tparam m The multiplier. - template - class Pcg { - public: - /// Constructs a new instance of this functor. - /// - /// @param[in] seed The seed. - /// @param[in] selector The sequence selector. - explicit Pcg(const word64 seed = 9600629759793949339ULL, const word64 selector = 7863035247680335341UL) : inc((selector << 1) | 1ULL) { - state = 0ULL; - rand(); - state += seed; - rand(); - } - - /// The destructor. - ~Pcg() = default; - - /// Returns a new real-valued random number in the interval [0, 1]. - /// - /// @return a real-valued random number in [0, 1]. - real operator()() const { - return rand() / real(0xFFFFFFFFUL); - } - - /// Returns a new random word. - /// - /// @return a random word. - word32 rand() const { - const word64 saved = state; - state = saved * mult + inc; - const word32 s = (((saved >> 18) ^ saved) >> 27); - const word32 r = saved >> 59; - return ((s >> r) | (s << ((-r) & 31UL))); - } - - private: - /// The increment. - const word64 inc; - - /// The state. - mutable word64 state; - }; - - /// The PCG-XSH-RR with 64-bit state and 32-bit output. - typedef Pcg<6364136223846793005ULL> Pcg_32; +namespace especia +{ + +/// A maximally equidistributed F2-linear generator (MELG). +/// +/// Further reading: +/// +/// S. Harase and T. Kimoto (2018). +/// Implementing 64-bit maximally equidistributed F2-linear generators with +/// Mersenne prime period. ACM Transactions on Mathematical Software, 44, +/// 3, 30. , +/// +/// +/// @tparam w The the number of bits in a word. +/// @tparam n The parameter n. +/// @tparam m The parameter m. +/// @tparam l The parameter l. +/// @tparam mult1 A multiplier (used for initialisation). +/// @tparam mult2 A multiplier (used for initialisation). +/// @tparam mult3 A multiplier (used for initialisation). +template +class Melg +{ +public: + /// Constructs a new instance of this functor. + /// + /// @param[in] seed The seed. + explicit Melg (const word64 seed) : state (n + 1) + { // NOLINT + const word64 seeds[] + = { seed & 0x00000000FFFFFFFFULL, seed & 0xFFFFFFFF00000000ULL }; + + reset (2, seeds); + } + + /// Constructs a new instance of this functor. + /// + /// @param[in] seed_count The number of seeds. + /// @param[in] seeds The seeds. + Melg (const natural seed_count, const word64 seeds[]) : state (n + 1) + { // NOLINT + reset (seed_count, seeds); + } + + /// The destructor. + ~Melg () = default; + + /// Returns a new real-valued random number in the interval [0, 1]. + /// + /// @return a real-valued random number in [0, 1]. + real + operator() () const + { + using std::numeric_limits; + // the maximum mantissa value for a real number + const real max_mantissa = numeric_limits::max () + >> (numeric_limits::digits + - (w < numeric_limits::digits + ? w + : numeric_limits::digits)); + + return (w < numeric_limits::digits + ? rand () + : rand () >> (w - numeric_limits::digits)) + * (1.0 / max_mantissa); + } + + /// Returns a new random word. + /// + /// @return a random word. + word64 + rand () const + { + word64 next = 0ULL; + + switch (cycle) + { + case 1: + next = rock (index, index + 1); + roll (next, index + m); + next = twist (next, index, index + l); + index++; + if (index == n - m) + { + cycle = 2; + } + break; + case 2: + next = rock (index, index + 1); + roll (next, index + m - n); + next = twist (next, index, index + l); + index++; + if (index == n - l) + { + cycle = 3; + } + break; + case 3: + next = rock (index, index + 1); + roll (next, index + m - n); + next = twist (next, index, index - (n - l)); + index++; + if (index == n - 1) + { + cycle = 4; + } + break; + case 4: + next = rock (n - 1, 0); + roll (next, m - 1); + next = twist (next, n - 1, index - (n - l)); + index = 0; + cycle = 1; + break; + } + + return next; + } + +private: + /// Resets this algorithm. + /// + /// @param[in] seed The seed. + void + reset (const word64 seed) + { + state[0] = seed; + + for (index = 1; index < n + 1; index++) + { + state[index] + = (state[index - 1] ^ (state[index - 1] >> (w - 2))) * mult1 + + index; + } + + index = 0; + cycle = 1; + } + + /// Resets this algorithm. + /// + /// @param[in] seed_count The number of seeds. + /// @param[in] seeds The seeds. + void + reset (const natural seed_count, const word64 seeds[]) + { + using std::max; + + reset (19650218ULL); + + natural i = 1; + natural j = 0; + for (natural k = max (n, seed_count); k > 0; k--) + { + state[i] + = (state[i] ^ ((state[i - 1] ^ (state[i - 1] >> (w - 2))) * mult2)) + + seeds[j] + j; + i++; + j++; + if (i >= n) + { + state[0] = state[n - 1]; + i = 1; + } + if (j >= seed_count) + { + j = 0; + } + } + for (natural k = n - 1; k > 0; k--) + { + state[i] + = (state[i] ^ ((state[i - 1] ^ (state[i - 1] >> (w - 2))) * mult3)) + - i; + i++; + if (i >= n) + { + state[0] = state[n - 1]; + i = 1; + } + } + state[n] + = (state[n] ^ ((state[n - 1] ^ (state[n - 1] >> (w - 2))) * mult3)) + - n; + state[0] = (state[0] | (1ULL << (w - 1))); + + index = 0; + cycle = 1; + } + + word64 + rock (const natural i, const natural k) const + { + return (state[i] & 0xFFFFFFFF80000000ULL) | (state[k] & 0x7FFFFFFFULL); + } + + void + roll (const word64 word, const natural i) const + { + state[n] = (word >> 1) + ^ (((word & 1ULL) != 0ULL) ? 0x5C32E06DF730FC42ULL : 0ULL) + ^ state[i] ^ (state[n] ^ (state[n] << 23)); + } + + word64 + twist (const word64 word, const natural i, const natural k) const + { + state[i] = word ^ (state[n] ^ (state[n] >> 33)); + return state[i] ^ (state[i] << 16) ^ (state[k] & 0x6AEDE6FD97B338ECULL); + } + + mutable std::valarray state; + mutable natural index; + mutable natural cycle; +}; + +/// The MELG19937-64 with 2,496 bytes of state and 64-bit output. +typedef Melg<64, 311, 81, 19, 6364136223846793005ULL, 3935559000370003845ULL, + 2862933555777941757ULL> + Melg19937_64; + +/// The Mersenne twister algorithm to generate [0,1] uniformly distributed +/// random deviates. +/// +/// This functor template is based on the 2002/01/26 version coded by Takuji +/// Nishimura and Makoto Matsumoto(Matsumoto and Nishimura, 1998). +/// +/// The notation of template parameters follows Matsumoto and +/// Nishimura (1998, Table 2). +/// +/// Further reading: +/// +/// m. Matsumoto, T. Nishimura (1998). +/// *Mersenne Twister: A 623-dimensionally equidistributed uniform +/// pseudorandom number generator.* ACM Transactions on Modeling and Computer +/// Simulation, 8, 3, ISSN 1049-3301. +/// +/// D. Knuth (1998). +/// *The art of computer programming 2. Seminumerical algorithms*. +/// Addison Wesley Longman, ISBN 0-201-89684-2. +/// +/// @tparam w The number of bits in a word. +/// @tparam n The parameter n. +/// @tparam m The parameter m. +/// @tparam r The parameter r. +/// @tparam a The parameter a. +/// @tparam u The parameter u. +/// @tparam d The parameter d. +/// @tparam s The parameter s. +/// @tparam b The parameter b. +/// @tparam t The parameter t. +/// @tparam c The parameter c. +/// @tparam l The parameter l. +/// @tparam mult1 A multiplier (used for initialisation). +/// @tparam mult2 A multiplier (used for initialisation). +/// @tparam mult3 A multiplier (used for initialisation). +template +class Mersenne_Twister +{ +public: + /// Constructs a new instance of this functor. + /// + /// @param[in] seed The seed. + explicit Mersenne_Twister (const word64 seed = 9600629759793949339ULL) + : state (n) + { // NOLINT + const word64 seeds[] + = { seed & 0x00000000FFFFFFFFULL, seed & 0xFFFFFFFF00000000ULL }; + + reset (2, seeds); + } + + /// Constructs a new instance of this functor. + /// + /// @param[in] seed_count The number of seeds. + /// @param[in] seeds The seeds. + Mersenne_Twister (const natural seed_count, const word64 seeds[]) : state (n) + { // NOLINT + reset (seed_count, seeds); + } + + /// The destructor. + ~Mersenne_Twister () = default; + + /// Returns a new real-valued random number in the interval [0, 1]. + /// + /// @return a real-valued random number in [0, 1]. + real + operator() () const + { + using std::numeric_limits; + // the maximum mantissa value for a real number + const real max_mantissa = numeric_limits::max () + >> (numeric_limits::digits + - (w < numeric_limits::digits + ? w + : numeric_limits::digits)); + + return (w < numeric_limits::digits + ? rand () + : rand () >> (w - numeric_limits::digits)) + * (1.0 / max_mantissa); + } + + /// Returns a new random word. + /// + /// @return a random word. + word64 + rand () const + { + if (index == n) + { + for (natural k = 0; k < n - m; ++k) + { + twist (k + m, k, k + 1); + } + + for (natural k = n - m; k < n - 1; ++k) + { + twist (k + m - n, k, k + 1); + } + + twist (m - 1, n - 1, 0); + index = 0; + } + + word64 next = state[index]; + ++index; + + next ^= (next >> u) & d; + next ^= (next << s) & b; + next ^= (next << t) & c; + next ^= (next >> l); + + return next; + } + +private: + /// Resets this algorithm. + /// + /// @param[in] seed The seed. + void + reset (const word64 seed) + { + using std::numeric_limits; + + state[0] = seed + & (numeric_limits::max () + >> (numeric_limits::digits - w)); + for (natural k = 1; k < n; ++k) + { + state[k] = ((state[k - 1] ^ (state[k - 1] >> (w - 2))) * mult1 + k) + & (numeric_limits::max () + >> (numeric_limits::digits - w)); + } + index = n; + } + + /// Resets this algorithm. + /// + /// @param[in] seed_count The number of seeds. + /// @param[in] seeds The seeds. + void + reset (const natural seed_count, const word64 seeds[]) + { + using std::max; + using std::numeric_limits; + + reset (19650218ULL); + + natural i = 1; + for (natural j = 0, k = max (n, seed_count); k > 0; --k) + { + state[i] = ((state[i] + ^ ((state[i - 1] ^ (state[i - 1] >> (w - 2))) * mult2)) + + seeds[j] + j) + & (numeric_limits::max () + >> (numeric_limits::digits - w)); + if (++i >= n) + { + state[0] = state[n - 1]; + i = 1; + } + if (++j >= seed_count) + { + j = 0; + } + } + for (natural k = n - 1; k > 0; --k) + { + state[i] = ((state[i] + ^ ((state[i - 1] ^ (state[i - 1] >> (w - 2))) * mult3)) + - i) + & (numeric_limits::max () + >> (numeric_limits::digits - w)); + if (++i >= n) + { + state[0] = state[n - 1]; + i = 1; + } + } + state[0] = (1ULL << (w - 1)); + index = n; + } + + void + twist (const natural i, const natural j, const natural k) const + { + using std::numeric_limits; + + state[j] = state[i] + ^ (((state[j] + & ((numeric_limits::max () + << (numeric_limits::digits - w + r)) + >> (numeric_limits::digits - w))) + | (state[k] + & (numeric_limits::max () + >> (numeric_limits::digits - r)))) + >> 1); + if ((state[k] & 1ULL) == 1ULL) + { + state[j] ^= a; + } + } + + mutable std::valarray state; + mutable natural index = 0; +}; + +/// The MT-1121A-32. +typedef Mersenne_Twister<32, 351, 175, 19, 0xE4BD75F5ULL, 11, 0xFFFFFFFFULL, 7, + 0x655E5280ULL, 15, 0xFFD58000ULL, 17, 1812433253ULL, + 1664525ULL, 1566083941ULL> + Mt11213a_32; +/// The MT-1121B-32. +typedef Mersenne_Twister<32, 351, 175, 19, 0xCCAB8EE7ULL, 11, 0xFFFFFFFFULL, 7, + 0x31B6AB00ULL, 15, 0xFFE50000ULL, 17, 1812433253ULL, + 1664525ULL, 1566083941ULL> + Mt11213b_32; +/// The MT-19937-32. +typedef Mersenne_Twister<32, 624, 397, 31, 0x9908B0DFULL, 11, 0xFFFFFFFFULL, 7, + 0x9D2C5680ULL, 15, 0xEFC60000ULL, 18, 1812433253ULL, + 1664525ULL, 1566083941ULL> + Mt19937_32; + +/// The MT-19937-64. +typedef Mersenne_Twister<64, 312, 156, 31, 0xB5026F5AA96619E9ULL, 29, + 0x5555555555555555ULL, 17, 0x71D67FFFEDA60000ULL, 37, + 0xFFF7EEE000000000ULL, 43, 6364136223846793005ULL, + 3935559000370003845ULL, 2862933555777941757ULL> + Mt19937_64; + +/// PCG algorithm to generate [0,1] uniformly distributed random deviates. +/// Based on Melissa E. O'Neill (2014) and . +/// +/// Further reading: +/// +/// Melissa E. O'Neill (2014). +/// *PCG: A Family of Simple Fast Space-Efficient Statistically Good +/// Algorithms for Random Number Generation.* +/// . +/// +/// @tparam m The multiplier. +template class Pcg +{ +public: + /// Constructs a new instance of this functor. + /// + /// @param[in] seed The seed. + /// @param[in] selector The sequence selector. + explicit Pcg (const word64 seed = 9600629759793949339ULL, + const word64 selector = 7863035247680335341UL) + : inc ((selector << 1) | 1ULL) + { + state = 0ULL; + rand (); + state += seed; + rand (); + } + + /// The destructor. + ~Pcg () = default; + + /// Returns a new real-valued random number in the interval [0, 1]. + /// + /// @return a real-valued random number in [0, 1]. + real + operator() () const + { + return rand () / real (0xFFFFFFFFUL); + } + + /// Returns a new random word. + /// + /// @return a random word. + word32 + rand () const + { + const word64 saved = state; + state = saved * mult + inc; + const word32 s = (((saved >> 18) ^ saved) >> 27); + const word32 r = saved >> 59; + return ((s >> r) | (s << ((-r) & 31UL))); + } + +private: + /// The increment. + const word64 inc; + + /// The state. + mutable word64 state; +}; + +/// The PCG-XSH-RR with 64-bit state and 32-bit output. +typedef Pcg<6364136223846793005ULL> Pcg_32; } diff --git a/src/main/cxx/core/readline.cxx b/src/main/cxx/core/readline.cxx index 2a51e11..57cc1f9 100644 --- a/src/main/cxx/core/readline.cxx +++ b/src/main/cxx/core/readline.cxx @@ -9,17 +9,21 @@ using namespace std; -istream &especia::readline(istream &is, string &s, char comment_mark, char eol) { - bool empty_line = true; +istream & +especia::readline (istream &is, string &s, char comment_mark, char eol) +{ + bool empty_line = true; - while (empty_line and getline(is, s, eol) and comment_mark != '\0') { - size_t i = 0; + while (empty_line and getline (is, s, eol) and comment_mark != '\0') + { + size_t i = 0; - while (i < s.length() and (comment_mark == '\0' or s[i] != comment_mark)) - empty_line = isspace(s[i++]) and empty_line; + while (i < s.length () + and (comment_mark == '\0' or s[i] != comment_mark)) + empty_line = isspace (s[i++]) and empty_line; - s.erase(i); + s.erase (i); } - return is; + return is; } diff --git a/src/main/cxx/core/readline.h b/src/main/cxx/core/readline.h index ecad692..1614a9b 100644 --- a/src/main/cxx/core/readline.h +++ b/src/main/cxx/core/readline.h @@ -13,367 +13,424 @@ #include #include -namespace especia { - - /// Reads a vector of data from an input stream. - /// - /// @tparam A The data type. - /// - /// @param is The input stream. - /// @param a The data vector. - /// @param n The number of data elements to read. - /// @param append If @c true, the data read are append to the data vector @c a. - /// - /// @return the input stream. - template - std::istream &read(std::istream &is, std::vector &a, size_t n, bool append = false) { - using namespace std; - - vector ta; ta.reserve(n); - - for (size_t i = 0; i < n; ++i) { - A aa; - - if (is >> aa) { - ta.push_back(aa); - } +namespace especia +{ + +/// Reads a vector of data from an input stream. +/// +/// @tparam A The data type. +/// +/// @param is The input stream. +/// @param a The data vector. +/// @param n The number of data elements to read. +/// @param append If @c true, the data read are append to the data vector @c a. +/// +/// @return the input stream. +template +std::istream & +read (std::istream &is, std::vector &a, size_t n, bool append = false) +{ + using namespace std; + + vector ta; + ta.reserve (n); + + for (size_t i = 0; i < n; ++i) + { + A aa; + + if (is >> aa) + { + ta.push_back (aa); } + } - if (is) { - if (append) { - a.insert(a.end(), ta.begin(), ta.end()); - } else { - a.assign(ta.begin(), ta.end()); - } + if (is) + { + if (append) + { + a.insert (a.end (), ta.begin (), ta.end ()); + } + else + { + a.assign (ta.begin (), ta.end ()); } - return is; } + return is; +} - /// Reads two vectors of data from an input stream. - /// - /// The procedure expects that the data for vectors @c a and @c b are alternating. - /// - /// @tparam A A data type. - /// @tparam B A data type. - /// - /// @param is The input stream. - /// @param a A data vector. - /// @param b A data vector. - /// @param n The number of data elements to read for each data vector. - /// @param append If @c true, the data read are append to the data vectors. - /// - /// @return the input stream. - template - std::istream &read(std::istream &is, std::vector &a, std::vector &b, size_t n, bool append = false) { - using namespace std; - - vector ta; - vector tb; - - ta.reserve(n); - tb.reserve(n); - - for (size_t i = 0; i < n; ++i) { - A aa; - B bb; - - if (is >> aa >> bb) { - ta.push_back(aa); - tb.push_back(bb); - } +/// Reads two vectors of data from an input stream. +/// +/// The procedure expects that the data for vectors @c a and @c b are +/// alternating. +/// +/// @tparam A A data type. +/// @tparam B A data type. +/// +/// @param is The input stream. +/// @param a A data vector. +/// @param b A data vector. +/// @param n The number of data elements to read for each data vector. +/// @param append If @c true, the data read are append to the data vectors. +/// +/// @return the input stream. +template +std::istream & +read (std::istream &is, std::vector &a, std::vector &b, size_t n, + bool append = false) +{ + using namespace std; + + vector ta; + vector tb; + + ta.reserve (n); + tb.reserve (n); + + for (size_t i = 0; i < n; ++i) + { + A aa; + B bb; + + if (is >> aa >> bb) + { + ta.push_back (aa); + tb.push_back (bb); } + } - if (is) { - if (append) { - a.insert(a.end(), ta.begin(), ta.end()); - b.insert(b.end(), tb.begin(), tb.end()); - } else { - a.assign(ta.begin(), ta.end()); - b.assign(tb.begin(), tb.end()); - } + if (is) + { + if (append) + { + a.insert (a.end (), ta.begin (), ta.end ()); + b.insert (b.end (), tb.begin (), tb.end ()); + } + else + { + a.assign (ta.begin (), ta.end ()); + b.assign (tb.begin (), tb.end ()); } - return is; } + return is; +} - /// Reads three vectors of data from an input stream. - /// - /// The procedure expects that the data for vectors @c a, @c b, and @c c are alternating. - /// - /// @tparam A A data type. - /// @tparam B A data type. - /// @tparam C A data type. - /// - /// @param is The input stream. - /// @param a A data vector. - /// @param b A data vector. - /// @param c A data vector. - /// @param n The number of data elements to read for each data vector. - /// @param append If @c true, the data read are append to the data vectors. - /// - /// @return the input stream. - template - std::istream & - read(std::istream &is, std::vector &a, std::vector &b, std::vector &c, size_t n, bool append = false) { - using namespace std; - - vector ta; - vector tb; - vector tc; - - ta.reserve(n); - tb.reserve(n); - tc.reserve(n); - - for (size_t i = 0; i < n; ++i) { - A aa; - B bb; - C cc; - - if (is >> aa >> bb >> cc) { - ta.push_back(aa); - tb.push_back(bb); - tc.push_back(cc); - } +/// Reads three vectors of data from an input stream. +/// +/// The procedure expects that the data for vectors @c a, @c b, and @c c are +/// alternating. +/// +/// @tparam A A data type. +/// @tparam B A data type. +/// @tparam C A data type. +/// +/// @param is The input stream. +/// @param a A data vector. +/// @param b A data vector. +/// @param c A data vector. +/// @param n The number of data elements to read for each data vector. +/// @param append If @c true, the data read are append to the data vectors. +/// +/// @return the input stream. +template +std::istream & +read (std::istream &is, std::vector &a, std::vector &b, + std::vector &c, size_t n, bool append = false) +{ + using namespace std; + + vector ta; + vector tb; + vector tc; + + ta.reserve (n); + tb.reserve (n); + tc.reserve (n); + + for (size_t i = 0; i < n; ++i) + { + A aa; + B bb; + C cc; + + if (is >> aa >> bb >> cc) + { + ta.push_back (aa); + tb.push_back (bb); + tc.push_back (cc); } + } - if (is) { - if (append) { - a.insert(a.end(), ta.begin(), ta.end()); - b.insert(b.end(), tb.begin(), tb.end()); - c.insert(c.end(), tc.begin(), tc.end()); - } else { - a.assign(ta.begin(), ta.end()); - b.assign(tb.begin(), tb.end()); - c.assign(tc.begin(), tc.end()); - } + if (is) + { + if (append) + { + a.insert (a.end (), ta.begin (), ta.end ()); + b.insert (b.end (), tb.begin (), tb.end ()); + c.insert (c.end (), tc.begin (), tc.end ()); + } + else + { + a.assign (ta.begin (), ta.end ()); + b.assign (tb.begin (), tb.end ()); + c.assign (tc.begin (), tc.end ()); } - return is; } + return is; +} - - /// Reads four vectors of data from an input stream. - /// - /// The procedure expects that the data for vectors @c a, @c b, c, and @c d are alternating. - /// - /// @tparam A A data type. - /// @tparam B A data type. - /// @tparam C A data type. - /// @tparam D A data type. - /// - /// @param is The input stream. - /// @param a A data vector. - /// @param b A data vector. - /// @param c A data vector. - /// @param d A data vector. - /// @param n The number of data elements to read for each data vector. - /// @param append If @c true, the data read are append to the data vectors. - /// - /// @return the input stream. - template - std::istream & - read(std::istream &is, std::vector &a, std::vector &b, std::vector &c, std::vector &d, size_t n, - bool append = false) { - using namespace std; - - vector ta; - vector tb; - vector tc; - vector td; - - ta.reserve(n); - tb.reserve(n); - tc.reserve(n); - td.reserve(n); - - for (size_t i = 0; i < n; ++i) { - A aa; - B bb; - C cc; - D dd; - - if (is >> aa >> bb >> cc >> dd) { - ta.push_back(aa); - tb.push_back(bb); - tc.push_back(cc); - td.push_back(dd); - } +/// Reads four vectors of data from an input stream. +/// +/// The procedure expects that the data for vectors @c a, @c b, c, and @c d are +/// alternating. +/// +/// @tparam A A data type. +/// @tparam B A data type. +/// @tparam C A data type. +/// @tparam D A data type. +/// +/// @param is The input stream. +/// @param a A data vector. +/// @param b A data vector. +/// @param c A data vector. +/// @param d A data vector. +/// @param n The number of data elements to read for each data vector. +/// @param append If @c true, the data read are append to the data vectors. +/// +/// @return the input stream. +template +std::istream & +read (std::istream &is, std::vector &a, std::vector &b, + std::vector &c, std::vector &d, size_t n, bool append = false) +{ + using namespace std; + + vector ta; + vector tb; + vector tc; + vector td; + + ta.reserve (n); + tb.reserve (n); + tc.reserve (n); + td.reserve (n); + + for (size_t i = 0; i < n; ++i) + { + A aa; + B bb; + C cc; + D dd; + + if (is >> aa >> bb >> cc >> dd) + { + ta.push_back (aa); + tb.push_back (bb); + tc.push_back (cc); + td.push_back (dd); } + } - if (is) { - if (append) { - a.insert(a.end(), ta.begin(), ta.end()); - b.insert(b.end(), tb.begin(), tb.end()); - c.insert(c.end(), tc.begin(), tc.end()); - d.insert(d.end(), td.begin(), td.end()); - } else { - a.assign(ta.begin(), ta.end()); - b.assign(tb.begin(), tb.end()); - c.assign(tc.begin(), tc.end()); - d.assign(td.begin(), td.end()); - } + if (is) + { + if (append) + { + a.insert (a.end (), ta.begin (), ta.end ()); + b.insert (b.end (), tb.begin (), tb.end ()); + c.insert (c.end (), tc.begin (), tc.end ()); + d.insert (d.end (), td.begin (), td.end ()); + } + else + { + a.assign (ta.begin (), ta.end ()); + b.assign (tb.begin (), tb.end ()); + c.assign (tc.begin (), tc.end ()); + d.assign (td.begin (), td.end ()); } - return is; } + return is; +} - /// Reads five vectors of data from an input stream. - /// - /// The procedure expects that the data for vectors @c a, @c b, c, d and @c e are alternating. - /// - /// @tparam A A data type. - /// @tparam B A data type. - /// @tparam C A data type. - /// @tparam D A data type. - /// @tparam E A data type. - /// - /// @param is The input stream. - /// @param a A data vector. - /// @param b A data vector. - /// @param c A data vector. - /// @param d A data vector. - /// @param e A data vector. - /// @param n The number of data elements to read for each data vector. - /// @param append If @c true, the data read are append to the data vectors. - /// - /// @return the input stream. - template - std::istream &read(std::istream &is, std::vector &a, std::vector &b, - std::vector &c, std::vector &d, std::vector &e, size_t n, - bool append = false) { - using namespace std; - - vector ta; - vector tb; - vector tc; - vector td; - vector te; - - ta.reserve(n); - tb.reserve(n); - tc.reserve(n); - td.reserve(n); - te.reserve(n); - - for (size_t i = 0; i < n; ++i) { - A aa; - B bb; - C cc; - D dd; - E ee; - - if (is >> aa >> bb >> cc >> dd >> ee) { - ta.push_back(aa); - tb.push_back(bb); - tc.push_back(cc); - td.push_back(dd); - te.push_back(ee); - } +/// Reads five vectors of data from an input stream. +/// +/// The procedure expects that the data for vectors @c a, @c b, c, d and @c e +/// are alternating. +/// +/// @tparam A A data type. +/// @tparam B A data type. +/// @tparam C A data type. +/// @tparam D A data type. +/// @tparam E A data type. +/// +/// @param is The input stream. +/// @param a A data vector. +/// @param b A data vector. +/// @param c A data vector. +/// @param d A data vector. +/// @param e A data vector. +/// @param n The number of data elements to read for each data vector. +/// @param append If @c true, the data read are append to the data vectors. +/// +/// @return the input stream. +template +std::istream & +read (std::istream &is, std::vector &a, std::vector &b, + std::vector &c, std::vector &d, std::vector &e, size_t n, + bool append = false) +{ + using namespace std; + + vector ta; + vector tb; + vector tc; + vector td; + vector te; + + ta.reserve (n); + tb.reserve (n); + tc.reserve (n); + td.reserve (n); + te.reserve (n); + + for (size_t i = 0; i < n; ++i) + { + A aa; + B bb; + C cc; + D dd; + E ee; + + if (is >> aa >> bb >> cc >> dd >> ee) + { + ta.push_back (aa); + tb.push_back (bb); + tc.push_back (cc); + td.push_back (dd); + te.push_back (ee); } + } - if (is) { - if (append) { - a.insert(a.end(), ta.begin(), ta.end()); - b.insert(b.end(), tb.begin(), tb.end()); - c.insert(c.end(), tc.begin(), tc.end()); - d.insert(d.end(), td.begin(), td.end()); - e.insert(e.end(), te.begin(), te.end()); - } else { - a.assign(ta.begin(), ta.end()); - b.assign(tb.begin(), tb.end()); - c.assign(tc.begin(), tc.end()); - d.assign(td.begin(), td.end()); - e.assign(te.begin(), te.end()); - } + if (is) + { + if (append) + { + a.insert (a.end (), ta.begin (), ta.end ()); + b.insert (b.end (), tb.begin (), tb.end ()); + c.insert (c.end (), tc.begin (), tc.end ()); + d.insert (d.end (), td.begin (), td.end ()); + e.insert (e.end (), te.begin (), te.end ()); + } + else + { + a.assign (ta.begin (), ta.end ()); + b.assign (tb.begin (), tb.end ()); + c.assign (tc.begin (), tc.end ()); + d.assign (td.begin (), td.end ()); + e.assign (te.begin (), te.end ()); } - return is; } + return is; +} - /// Reads four vectors of data and a single vector of text from an input stream. - /// - /// The procedure expects that the data are alternating and text consists of a single word. - /// - /// @tparam A A data type. - /// @tparam B A data type. - /// @tparam C A data type. - /// @tparam D A data type. - /// - /// @param is The input stream. - /// @param a A data vector. - /// @param b A data vector. - /// @param c A data vector. - /// @param d A data vector. - /// @param s A text vector. - /// @param n The number of data elements to read for each vector. - /// @param eol The character marking the end of text (or end of line). - /// @param append If @c true, the data read are append to the vectors. - /// - /// @return the input stream. - template - std::istream &read(std::istream &is, std::vector &a, std::vector &b, std::vector &c, std::vector &d, - std::vector &s, size_t n, char eol = '\n', - bool append = false) { - using namespace std; - - vector ta; - vector tb; - vector tc; - vector td; - vector ts; - - ta.reserve(n); - tb.reserve(n); - tc.reserve(n); - td.reserve(n); - ts.reserve(n); - - for (size_t i = 0; i < n; ++i) { - A aa; - B bb; - C cc; - D dd; - string ss; - - if (is >> aa >> bb >> cc >> dd and getline(is, ss, eol)) { - istringstream ist(ss); - - ist >> ss; - if (!ist) - ss.erase(); - - ta.push_back(aa); - tb.push_back(bb); - tc.push_back(cc); - td.push_back(dd); - ts.push_back(ss); - } +/// Reads four vectors of data and a single vector of text from an input +/// stream. +/// +/// The procedure expects that the data are alternating and text consists of a +/// single word. +/// +/// @tparam A A data type. +/// @tparam B A data type. +/// @tparam C A data type. +/// @tparam D A data type. +/// +/// @param is The input stream. +/// @param a A data vector. +/// @param b A data vector. +/// @param c A data vector. +/// @param d A data vector. +/// @param s A text vector. +/// @param n The number of data elements to read for each vector. +/// @param eol The character marking the end of text (or end of line). +/// @param append If @c true, the data read are append to the vectors. +/// +/// @return the input stream. +template +std::istream & +read (std::istream &is, std::vector &a, std::vector &b, + std::vector &c, std::vector &d, std::vector &s, + size_t n, char eol = '\n', bool append = false) +{ + using namespace std; + + vector ta; + vector tb; + vector tc; + vector td; + vector ts; + + ta.reserve (n); + tb.reserve (n); + tc.reserve (n); + td.reserve (n); + ts.reserve (n); + + for (size_t i = 0; i < n; ++i) + { + A aa; + B bb; + C cc; + D dd; + string ss; + + if (is >> aa >> bb >> cc >> dd and getline (is, ss, eol)) + { + istringstream ist (ss); + + ist >> ss; + if (!ist) + ss.erase (); + + ta.push_back (aa); + tb.push_back (bb); + tc.push_back (cc); + td.push_back (dd); + ts.push_back (ss); } + } - if (is) { - if (append) { - a.insert(a.end(), ta.begin(), ta.end()); - b.insert(b.end(), tb.begin(), tb.end()); - c.insert(c.end(), tc.begin(), tc.end()); - d.insert(d.end(), td.begin(), td.end()); - s.insert(s.end(), ts.begin(), ts.end()); - } else { - a.assign(ta.begin(), ta.end()); - b.assign(tb.begin(), tb.end()); - c.assign(tc.begin(), tc.end()); - d.assign(td.begin(), td.end()); - s.assign(ts.begin(), ts.end()); - } + if (is) + { + if (append) + { + a.insert (a.end (), ta.begin (), ta.end ()); + b.insert (b.end (), tb.begin (), tb.end ()); + c.insert (c.end (), tc.begin (), tc.end ()); + d.insert (d.end (), td.begin (), td.end ()); + s.insert (s.end (), ts.begin (), ts.end ()); + } + else + { + a.assign (ta.begin (), ta.end ()); + b.assign (tb.begin (), tb.end ()); + c.assign (tc.begin (), tc.end ()); + d.assign (td.begin (), td.end ()); + s.assign (ts.begin (), ts.end ()); } - return is; } + return is; +} - /// Reads a line of text from an input stream. - /// - /// @param is The input stream. - /// @param s The line of text. - /// @param comment_mark The character that marks the begin of comment. If set, empty lines are ignored, too. - /// @param eol The character that marks the end of line. - /// - /// @return the input stream. - std::istream &readline(std::istream &is, std::string &s, char comment_mark = '\0', char eol = '\n'); +/// Reads a line of text from an input stream. +/// +/// @param is The input stream. +/// @param s The line of text. +/// @param comment_mark The character that marks the begin of comment. If set, +/// empty lines are ignored, too. +/// @param eol The character that marks the end of line. +/// +/// @return the input stream. +std::istream &readline (std::istream &is, std::string &s, + char comment_mark = '\0', char eol = '\n'); } diff --git a/src/main/cxx/core/runner.cxx b/src/main/cxx/core/runner.cxx index 4f44ac5..2902392 100644 --- a/src/main/cxx/core/runner.cxx +++ b/src/main/cxx/core/runner.cxx @@ -5,64 +5,80 @@ /// @copyright MIT License #include "runner.h" -especia::Runner::Runner(int argc, char *argv[]) { - using std::string; +especia::Runner::Runner (int argc, char *argv[]) +{ + using std::string; - for (int i = 0; i < argc; ++i) { - args.emplace_back(argv[i]); + for (int i = 0; i < argc; ++i) + { + args.emplace_back (argv[i]); } } -especia::Runner::~Runner() = default; +especia::Runner::~Runner () = default; -void especia::Runner::write_command_line(std::ostream &os) const { - using std::endl; +void +especia::Runner::write_command_line (std::ostream &os) const +{ + using std::endl; - os << "" << endl; - os << "" << endl; - os << "" << endl; - os << "" << endl; + os << std::endl; + os << "" << endl; + os << "-->" << endl; + os << "" << endl; } -void especia::Runner::write_result_messages(std::ostream &os, const Optimizer::Result &result) const { - using std::cout; - using std::endl; +void +especia::Runner::write_result_messages (std::ostream &os, + const Optimizer::Result &result) const +{ + using std::cout; + using std::endl; - os << "" << endl; + os << "" << endl; + os << "-->" << endl; } -void especia::Runner::write_usage_message(std::ostream &os) const { - using std::endl; +void +especia::Runner::write_usage_message (std::ostream &os) const +{ + using std::endl; - os << project_long_name << " " << project_doi << endl; - os << "usage: " << get_program_name() << ": " - << "{seed} {parents} {population} {step} {accuracy} {stop} {trace} < {model file} [> {result file}]" - << endl; + os << project_long_name << " " << project_doi << endl; + os << "usage: " << get_program_name () << ": " + << "{seed} {parents} {population} {step} {accuracy} {stop} {trace} < " + "{model file} [> {result file}]" + << endl; } diff --git a/src/main/cxx/core/runner.h b/src/main/cxx/core/runner.h index 444fa04..28c0229 100644 --- a/src/main/cxx/core/runner.h +++ b/src/main/cxx/core/runner.h @@ -16,269 +16,306 @@ #include "exitcodes.h" #include "optimizer.h" -namespace especia { - - /// Carries out an optimization run. - class Runner { - public: - - /// Constructs a new runner from the command line arguments supplied. - /// - /// @param argc The number of command line arguments. - /// @param argv The command line arguments: - /// @parblock - /// @c argv[0] The program name. - /// - /// @c argv[1] The random seed. - /// - /// @c argv[2] The parent number. - /// - /// @c argv[3] The population size. - /// - /// @c argv[4] The initial global step size. - /// - /// @c argv[5] The accuracy goal. - /// - /// @c argv[6] The stop generation number. - /// - /// @c argv[7] The trace modulus. - /// @endparblock - Runner(int argc, char *argv[]); - - /// The destructor. - ~Runner(); - - /// Returns the command line arguments. - /// - /// @return the command line arguments. - const std::vector &get_args() const { - return args; - } - - /// Returns the number of command line arguments. - /// - /// @return the number of command line arguments. - size_t get_arg_count() const { - return args.size(); - } - - /// Returns the program name. - /// - /// @return the program name. - std::string get_program_name() const { - return std::string(args[0]); - } - - /// Parses the accuracy goal. - /// - /// @return the accuracy goal. - real parse_accuracy_goal() const { - return convert(args[5]); - } - - /// Parses the initial global step size. - /// - /// @return the initial global step size. - real parse_global_step_size() const { - return convert(args[4]); - } - - /// Parses the parent number. - /// - /// @return the parent number. - natural parse_parent_number() const { - return convert(args[2]); - } - - /// Parses the population size. - /// - /// @return the population size. - natural parse_population_size() const { - return convert(args[3]); - } - - /// Parses the random seed. - /// - /// @return the random seed. - word64 parse_random_seed() const { - return convert(args[1]); - } - - /// Parses the stop generation. - /// - /// @return the stop generation. - natural parse_stop_generation() const { - return convert(args[6]); - } - - /// Parses the trace modulus. - /// - /// @return the trace modulus. - natural parse_trace_modulus() const { - return convert(args[7]); - } - - /// Runs the model supplied as argument. - /// - /// @tparam M The model type. - /// - /// @return an exit code - /// @throw invalid_argument when an invalid argument was supplied. - /// @throw runtime_error when a runtime error occurred. - template - int run() { - using std::cin; - using std::cout; - using std::endl; - using std::invalid_argument; - using std::runtime_error; - - if (get_arg_count() == 1) { - write_usage_message(cout); - return 0; - } - if (get_arg_count() != 8) { - throw invalid_argument( - "especia::Runner::run() Error: an invalid number of arguments was supplied"); - } - - write_command_line(cout); - - const word64 random_seed = parse_random_seed(); - const natural parent_number = parse_parent_number(); - const natural population_size = parse_population_size(); - const real global_step_size = parse_global_step_size(); - const real accuracy_goal = parse_accuracy_goal(); - const natural stop_generation = parse_stop_generation(); - const natural trace_modulus = parse_trace_modulus(); - - M model; - model.get(cin, cout); - - if (cin.fail()) { - throw runtime_error( - "especia::Runner::run() Error: an error occurred while reading the model definition"); - } - if (not cin.eof()) { - throw runtime_error( - "especia::Runner::run() Error: an error occurred while reading the model definition"); - } - - const Optimizer optimizer = Optimizer::Builder(). - with_problem_dimension(model.get_parameter_count()). - with_parent_number(parent_number). - with_population_size(population_size). - with_accuracy_goal(accuracy_goal). - with_stop_generation(stop_generation). - with_random_seed(random_seed). - build(); - - cout << "" << endl; - cout << "" << endl; - - cout << "" << endl; - - write_result_messages(cout, result); - - cout << "" << endl; - - model.set(&result.get_parameter_values()[0], &result.get_parameter_uncertainties()[0]); - model.put(cout); - - if (result.is_optimized()) { - return 0; - } - if (result.is_underflow()) { - return Exit_Codes::optimization_underflow; - } else { - return Exit_Codes::optimization_stopped; - } - } - - private: - /// Traces optimizer state information to an output stream. - /// - /// @tparam T The number type. - template - class Tracer { - public: - /// The constructor. - /// - /// @param[in] output_stream The output stream. - /// @param[in] modulus The trace modulus. - /// @param[in] precision The precision of numeric output. - /// @param[in] width The width of the numeric output fields. - Tracer(std::ostream &output_stream, natural modulus, natural precision = 4, natural width = 12) - : os(output_stream), m(modulus), p(precision), w(width) { - } - - /// The destructor. - ~Tracer() = default; - - /// Tests if tracing is enabled. - /// - /// @param[in] g The generation number. - /// @return @true if tracing is enabled, otherwise @c false. - bool is_tracing(natural g) const { - return m > 0 and g % m == 0; - } - - /// Traces state information to an output stream.. - /// - /// @param[in] g The generation number. - /// @param[in] y The value of the objective function. - /// @param[in] min_step The minimum step size. - /// @param[in] max_step The maximum step size. - void trace(natural g, T y, T min_step, T max_step) const { - using std::endl; - using std::ios_base; - using std::setw; - - const ios_base::fmtflags fmt = os.flags(); - - os.setf(ios_base::fmtflags()); - os.setf(ios_base::scientific, ios_base::floatfield); - os.setf(ios_base::right, ios_base::adjustfield); - os.precision(p); - - os << setw(8) << g; - os << setw(w) << y; - os << setw(w) << min_step; - os << setw(w) << max_step; - os << endl; - - os.flags(fmt); - } - - private: - std::ostream &os; - const natural m; - const natural p; - const natural w; - }; - - void write_command_line(std::ostream &os) const; - - void write_result_messages(std::ostream &os, const Optimizer::Result &result) const; - - void write_usage_message(std::ostream &os) const; - - /// The command line arguments. - std::vector args; - }; +namespace especia +{ + +/// Carries out an optimization run. +class Runner +{ +public: + /// Constructs a new runner from the command line arguments supplied. + /// + /// @param argc The number of command line arguments. + /// @param argv The command line arguments: + /// @parblock + /// @c argv[0] The program name. + /// + /// @c argv[1] The random seed. + /// + /// @c argv[2] The parent number. + /// + /// @c argv[3] The population size. + /// + /// @c argv[4] The initial global step size. + /// + /// @c argv[5] The accuracy goal. + /// + /// @c argv[6] The stop generation number. + /// + /// @c argv[7] The trace modulus. + /// @endparblock + Runner (int argc, char *argv[]); + + /// The destructor. + ~Runner (); + + /// Returns the command line arguments. + /// + /// @return the command line arguments. + const std::vector & + get_args () const + { + return args; + } + + /// Returns the number of command line arguments. + /// + /// @return the number of command line arguments. + size_t + get_arg_count () const + { + return args.size (); + } + + /// Returns the program name. + /// + /// @return the program name. + std::string + get_program_name () const + { + return std::string (args[0]); + } + + /// Parses the accuracy goal. + /// + /// @return the accuracy goal. + real + parse_accuracy_goal () const + { + return convert (args[5]); + } + + /// Parses the initial global step size. + /// + /// @return the initial global step size. + real + parse_global_step_size () const + { + return convert (args[4]); + } + + /// Parses the parent number. + /// + /// @return the parent number. + natural + parse_parent_number () const + { + return convert (args[2]); + } + + /// Parses the population size. + /// + /// @return the population size. + natural + parse_population_size () const + { + return convert (args[3]); + } + + /// Parses the random seed. + /// + /// @return the random seed. + word64 + parse_random_seed () const + { + return convert (args[1]); + } + + /// Parses the stop generation. + /// + /// @return the stop generation. + natural + parse_stop_generation () const + { + return convert (args[6]); + } + + /// Parses the trace modulus. + /// + /// @return the trace modulus. + natural + parse_trace_modulus () const + { + return convert (args[7]); + } + + /// Runs the model supplied as argument. + /// + /// @tparam M The model type. + /// + /// @return an exit code + /// @throw invalid_argument when an invalid argument was supplied. + /// @throw runtime_error when a runtime error occurred. + template + int + run () + { + using std::cin; + using std::cout; + using std::endl; + using std::invalid_argument; + using std::runtime_error; + + if (get_arg_count () == 1) + { + write_usage_message (cout); + return 0; + } + if (get_arg_count () != 8) + { + throw invalid_argument ("especia::Runner::run() Error: an invalid " + "number of arguments was supplied"); + } + + write_command_line (cout); + + const word64 random_seed = parse_random_seed (); + const natural parent_number = parse_parent_number (); + const natural population_size = parse_population_size (); + const real global_step_size = parse_global_step_size (); + const real accuracy_goal = parse_accuracy_goal (); + const natural stop_generation = parse_stop_generation (); + const natural trace_modulus = parse_trace_modulus (); + + M model; + model.get (cin, cout); + + if (cin.fail ()) + { + throw runtime_error ("especia::Runner::run() Error: an error occurred " + "while reading the model definition"); + } + if (not cin.eof ()) + { + throw runtime_error ("especia::Runner::run() Error: an error occurred " + "while reading the model definition"); + } + + const Optimizer optimizer + = Optimizer::Builder () + .with_problem_dimension (model.get_parameter_count ()) + .with_parent_number (parent_number) + .with_population_size (population_size) + .with_accuracy_goal (accuracy_goal) + .with_stop_generation (stop_generation) + .with_random_seed (random_seed) + .build (); + + cout << "" << endl; + cout << "" << endl; + + cout << "" << endl; + + write_result_messages (cout, result); + + cout << "" << endl; + + model.set (&result.get_parameter_values ()[0], + &result.get_parameter_uncertainties ()[0]); + model.put (cout); + + if (result.is_optimized ()) + { + return 0; + } + if (result.is_underflow ()) + { + return Exit_Codes::optimization_underflow; + } + else + { + return Exit_Codes::optimization_stopped; + } + } + +private: + /// Traces optimizer state information to an output stream. + /// + /// @tparam T The number type. + template class Tracer + { + public: + /// The constructor. + /// + /// @param[in] output_stream The output stream. + /// @param[in] modulus The trace modulus. + /// @param[in] precision The precision of numeric output. + /// @param[in] width The width of the numeric output fields. + Tracer (std::ostream &output_stream, natural modulus, + natural precision = 4, natural width = 12) + : os (output_stream), m (modulus), p (precision), w (width) + { + } + + /// The destructor. + ~Tracer () = default; + + /// Tests if tracing is enabled. + /// + /// @param[in] g The generation number. + /// @return @true if tracing is enabled, otherwise @c false. + bool + is_tracing (natural g) const + { + return m > 0 and g % m == 0; + } + + /// Traces state information to an output stream.. + /// + /// @param[in] g The generation number. + /// @param[in] y The value of the objective function. + /// @param[in] min_step The minimum step size. + /// @param[in] max_step The maximum step size. + void + trace (natural g, T y, T min_step, T max_step) const + { + using std::endl; + using std::ios_base; + using std::setw; + + const ios_base::fmtflags fmt = os.flags (); + + os.setf (ios_base::fmtflags ()); + os.setf (ios_base::scientific, ios_base::floatfield); + os.setf (ios_base::right, ios_base::adjustfield); + os.precision (p); + + os << setw (8) << g; + os << setw (w) << y; + os << setw (w) << min_step; + os << setw (w) << max_step; + os << endl; + + os.flags (fmt); + } + + private: + std::ostream &os; + const natural m; + const natural p; + const natural w; + }; + + void write_command_line (std::ostream &os) const; + + void write_result_messages (std::ostream &os, + const Optimizer::Result &result) const; + + void write_usage_message (std::ostream &os) const; + + /// The command line arguments. + std::vector args; +}; } #endif // ESPECIA_RUNNER_H - diff --git a/src/main/cxx/core/section.cxx b/src/main/cxx/core/section.cxx index 3d7e2e7..d01a03b 100644 --- a/src/main/cxx/core/section.cxx +++ b/src/main/cxx/core/section.cxx @@ -15,348 +15,401 @@ using especia::real; using especia::sqrt_of_ln_two; using especia::sqrt_of_pi; -especia::Section::Section() - : wav(), - flx(), - unc(), - msk(), - opt(), - atm(), - cat(), - cfl(), - tfl(), - fit(), - res(), - n(0) { +especia::Section::Section () + : wav (), flx (), unc (), msk (), opt (), atm (), cat (), cfl (), tfl (), + fit (), res (), n (0) +{ } -especia::Section::Section(const size_t n_in) - : wav(0.0, n_in), - flx(0.0, n_in), - unc(0.0, n_in), - msk(true, n_in), - opt(0.0, n_in), - atm(0.0, n_in), - cat(0.0, n_in), - cfl(0.0, n_in), - tfl(0.0, n_in), - fit(0.0, n_in), - res(0.0, n_in), - n(n_in) { +especia::Section::Section (const size_t n_in) + : wav (0.0, n_in), flx (0.0, n_in), unc (0.0, n_in), msk (true, n_in), + opt (0.0, n_in), atm (0.0, n_in), cat (0.0, n_in), cfl (0.0, n_in), + tfl (0.0, n_in), fit (0.0, n_in), res (0.0, n_in), n (n_in) +{ } -especia::Section::Section(const size_t n_in, const real x[], const real y[], const real unc[]) - : wav(x, n_in), - flx(y, n_in), - unc(unc, n_in), - msk(true, n_in), - opt(0.0, n_in), - atm(0.0, n_in), - cat(0.0, n_in), - cfl(0.0, n_in), - tfl(0.0, n_in), - fit(0.0, n_in), - res(0.0, n_in), - n(n_in) { +especia::Section::Section (const size_t n_in, const real x[], const real y[], + const real unc[]) + : wav (x, n_in), flx (y, n_in), unc (unc, n_in), msk (true, n_in), + opt (0.0, n_in), atm (0.0, n_in), cat (0.0, n_in), cfl (0.0, n_in), + tfl (0.0, n_in), fit (0.0, n_in), res (0.0, n_in), n (n_in) +{ } -especia::Section::~Section() = default; - -void especia::Section::continuum(const natural m, const std::valarray &cat, std::valarray &cfl) const { - using std::fill; - using std::runtime_error; - using std::sqrt; - using std::valarray; - - if (m > 0) { - valarray b(0.0, m); - valarray c(0.0, m); - valarray> a(b, m); - valarray> l(valarray(1.0, n), m); - - if (m > 1) { - l[1] = 2.0 * (wav - lower_bound()) / width() - 1.0; - // Bonnet’s recursion formula - for (natural j = 1; j + 1 < m; ++j) { - l[j + 1] = (real(2 * j + 1) * l[1] * l[j] - real(j) * l[j - 1]) / real(j + 1); +especia::Section::~Section () = default; + +void +especia::Section::continuum (const natural m, const std::valarray &cat, + std::valarray &cfl) const +{ + using std::fill; + using std::runtime_error; + using std::sqrt; + using std::valarray; + + if (m > 0) + { + valarray b (0.0, m); + valarray c (0.0, m); + valarray > a (b, m); + valarray > l (valarray (1.0, n), m); + + if (m > 1) + { + l[1] = 2.0 * (wav - lower_bound ()) / width () - 1.0; + // Bonnet’s recursion formula + for (natural j = 1; j + 1 < m; ++j) + { + l[j + 1] = (real (2 * j + 1) * l[1] * l[j] - real (j) * l[j - 1]) + / real (j + 1); } } - // Optimizing the background continuum is a linear optimization problem. Here the normal - // equations are established. - const valarray p = cat / sq(unc); - for (natural j = 0; j < m; ++j) { - const valarray &lj = l[j]; - for (natural k = j; k < m; ++k) { - const valarray &lk = l[k]; - for (size_t i = 0; i < n; ++i) { - if (msk[i]) { - a[j][k] += cat[i] * p[i] * lj[i] * lk[i]; + // Optimizing the background continuum is a linear optimization problem. + // Here the normal equations are established. + const valarray p = cat / sq (unc); + for (natural j = 0; j < m; ++j) + { + const valarray &lj = l[j]; + for (natural k = j; k < m; ++k) + { + const valarray &lk = l[k]; + for (size_t i = 0; i < n; ++i) + { + if (msk[i]) + { + a[j][k] += cat[i] * p[i] * lj[i] * lk[i]; } } } - for (size_t i = 0; i < n; ++i) { - if (msk[i]) { - b[j] += flx[i] * p[i] * lj[i]; + for (size_t i = 0; i < n; ++i) + { + if (msk[i]) + { + b[j] += flx[i] * p[i] * lj[i]; } } } - // The normal equations are solved by means of a Cholesky decomposition (e.g. Press et al. 2002). - // - // W. H. Press, S. A. Teukolsky, W. T. Vetterling, B. P. Flannery (2002). - // Numerical Recipes in C: The Art of Scientific Computing. - // Cambridge University Press, ISBN 0-521-75033-4. - for (natural i = 0; i < m; ++i) { - for (natural j = i; j < m; ++j) { - real s = a[i][j]; - - for (natural k = 0; k < i; ++k) { - s -= a[i][k] * a[j][k]; + // The normal equations are solved by means of a Cholesky decomposition + // (e.g. Press et al. 2002). + // + // W. H. Press, S. A. Teukolsky, W. T. Vetterling, B. P. Flannery (2002). + // Numerical Recipes in C: The Art of Scientific Computing. + // Cambridge University Press, ISBN 0-521-75033-4. + for (natural i = 0; i < m; ++i) + { + for (natural j = i; j < m; ++j) + { + real s = a[i][j]; + + for (natural k = 0; k < i; ++k) + { + s -= a[i][k] * a[j][k]; } - if (i < j) { - a[j][i] = s / a[i][i]; - } else if (s > 0.0) { - a[i][i] = sqrt(s); - } else { - // The normal equations are (numerically) singular. - throw runtime_error( - "especia::section::continuum(): Error: normal equations are numerically singular"); + if (i < j) + { + a[j][i] = s / a[i][i]; + } + else if (s > 0.0) + { + a[i][i] = sqrt (s); + } + else + { + // The normal equations are (numerically) singular. + throw runtime_error ( + "especia::section::continuum(): Error: normal equations " + "are numerically singular"); } } } - for (natural i = 0; i < m; ++i) { - real s = b[i]; + for (natural i = 0; i < m; ++i) + { + real s = b[i]; - for (natural k = 0; k < i; ++k) { - s -= a[i][k] * c[k]; + for (natural k = 0; k < i; ++k) + { + s -= a[i][k] * c[k]; } - c[i] = s / a[i][i]; + c[i] = s / a[i][i]; } - for (natural i = m - 1; i + 1 > 0; --i) { - real s = c[i]; + for (natural i = m - 1; i + 1 > 0; --i) + { + real s = c[i]; - for (natural k = i + 1; k < m; ++k) { - s -= a[k][i] * c[k]; + for (natural k = i + 1; k < m; ++k) + { + s -= a[k][i] * c[k]; } - c[i] = s / a[i][i]; + c[i] = s / a[i][i]; } - // Compute the continuum flux. The first Legendre term is a constant. - cfl = c[0]; - // The other terms depend on the abcissa value. - for (natural k = 1; k < m; ++k) { - cfl += c[k] * l[k]; + // Compute the continuum flux. The first Legendre term is a constant. + cfl = c[0]; + // The other terms depend on the abcissa value. + for (natural k = 1; k < m; ++k) + { + cfl += c[k] * l[k]; } - } else { - fill(&cfl[0], &cfl[n], 1.0); + } + else + { + fill (&cfl[0], &cfl[n], 1.0); } } -size_t especia::Section::valid_data_count() const { - size_t count = 0; +size_t +especia::Section::valid_data_count () const +{ + size_t count = 0; - for (size_t i = 0; i < n; ++i) { - if (msk[i]) { - ++count; + for (size_t i = 0; i < n; ++i) + { + if (msk[i]) + { + ++count; } } - return count; + return count; } -real especia::Section::cost() const { - real cost = 0.0; +real +especia::Section::cost () const +{ + real cost = 0.0; - for (size_t i = 0; i < n; ++i) { - if (msk[i]) { - cost += res[i] * res[i]; + for (size_t i = 0; i < n; ++i) + { + if (msk[i]) + { + cost += res[i] * res[i]; } } - return 0.5 * cost; + return 0.5 * cost; } -void especia::Section::mask(const real a, const real b) { - for (size_t i = 0; i < n; ++i) { - if (a <= wav[i] and wav[i] <= b) { - msk[i] = false; +void +especia::Section::mask (const real a, const real b) +{ + for (size_t i = 0; i < n; ++i) + { + if (a <= wav[i] and wav[i] <= b) + { + msk[i] = false; } } } -void especia::Section::primitive(const real &x, const real &h, real &p, real &q) { - using std::erf; // C++11 - using std::exp; +void +especia::Section::primitive (const real &x, const real &h, real &p, real &q) +{ + using std::erf; // C++11 + using std::exp; - const real b = h / sqrt_of_ln_two; - const real d = b / sqrt_of_pi; + const real b = h / sqrt_of_ln_two; + const real d = b / sqrt_of_pi; - p = 0.5 * erf(x / b); - q = 0.5 * exp(-sq(x / b)) * (-d); + p = 0.5 * erf (x / b); + q = 0.5 * exp (-sq (x / b)) * (-d); } -void especia::Section::supersample(const std::valarray &source, const natural k, std::valarray &target) { - for (natural is = 0, it = 0; is < source.size(); ++is, it += k) { - target[it] = source[is]; +void +especia::Section::supersample (const std::valarray &source, + const natural k, std::valarray &target) +{ + for (natural is = 0, it = 0; is < source.size (); ++is, it += k) + { + target[it] = source[is]; } - for (natural j = 1; j < k; ++j) { - const real w = real(j) / real(k); + for (natural j = 1; j < k; ++j) + { + const real w = real (j) / real (k); - for (size_t is = 0, it = j; is + 1 < source.size(); ++is, it += k) { - target[it] = source[is] + w * (source[is + 1] - source[is]); + for (size_t is = 0, it = j; is + 1 < source.size (); ++is, it += k) + { + target[it] = source[is] + w * (source[is + 1] - source[is]); } } } -std::istream &especia::Section::get(std::istream &is, const real a, const real b) { - using namespace std; +std::istream & +especia::Section::get (std::istream &is, const real a, const real b) +{ + using namespace std; - const size_t room = 20000; + const size_t room = 20000; - vector w; - vector x; - vector y; - vector z; + vector w; + vector x; + vector y; + vector z; - w.reserve(room); - x.reserve(room); - y.reserve(room); - z.reserve(room); + w.reserve (room); + x.reserve (room); + y.reserve (room); + z.reserve (room); - size_t i = 0; - string line; + size_t i = 0; + string line; - while (getline(is, line) and !line.empty()) { - // Skip comments. - if (line[0] == '#' or line[0] == '%' or line[0] == '!') { - continue; + while (getline (is, line) and !line.empty ()) + { + // Skip comments. + if (line[0] == '#' or line[0] == '%' or line[0] == '!') + { + continue; } - istringstream ist(line); - bool tw; - real tx, ty, tz; + istringstream ist (line); + bool tw; + real tx, ty, tz; - if (ist >> tx >> ty) { - if (a <= tx and tx <= b) { - x.push_back(tx); - y.push_back(ty); + if (ist >> tx >> ty) + { + if (a <= tx and tx <= b) + { + x.push_back (tx); + y.push_back (ty); - if (ist >> tz) { - z.push_back(tz); - } else { - z.push_back(1.0); + if (ist >> tz) + { + z.push_back (tz); + } + else + { + z.push_back (1.0); + } + if (ist >> tw) + { + w.push_back (tw); } - if (ist >> tw) { - w.push_back(tw); - } else { - w.push_back(true); + else + { + w.push_back (true); } - ++i; + ++i; } - } else { - is.setstate(ios_base::badbit | ios_base::failbit); + } + else + { + is.setstate (ios_base::badbit | ios_base::failbit); - return is; + return is; } } - if (i > 0) { - wav.resize(i); - flx.resize(i); - unc.resize(i); - msk.resize(i); - - opt.resize(i, 0.0); - atm.resize(i, 0.0); - cat.resize(i, 0.0); - cfl.resize(i, 0.0); - tfl.resize(i, 0.0); - fit.resize(i, 0.0); - res.resize(i, 0.0); - - n = i; - - copy(x.begin(), x.end(), &wav[0]); - copy(y.begin(), y.end(), &flx[0]); - copy(z.begin(), z.end(), &unc[0]); - copy(w.begin(), w.end(), &msk[0]); - - is.clear(is.rdstate() & ~ios_base::failbit); - } else { - is.setstate(ios_base::failbit); + if (i > 0) + { + wav.resize (i); + flx.resize (i); + unc.resize (i); + msk.resize (i); + + opt.resize (i, 0.0); + atm.resize (i, 0.0); + cat.resize (i, 0.0); + cfl.resize (i, 0.0); + tfl.resize (i, 0.0); + fit.resize (i, 0.0); + res.resize (i, 0.0); + + n = i; + + copy (x.begin (), x.end (), &wav[0]); + copy (y.begin (), y.end (), &flx[0]); + copy (z.begin (), z.end (), &unc[0]); + copy (w.begin (), w.end (), &msk[0]); + + is.clear (is.rdstate () & ~ios_base::failbit); + } + else + { + is.setstate (ios_base::failbit); } - return is; + return is; } -std::ostream &especia::Section::put(std::ostream &os, const real a, const real b) const { - using namespace std; - - if (os) { - // The precision. - const natural p = 8; - // The width of the output field. - const natural w = 16; - - const ios_base::fmtflags f = os.flags(); - - os.setf(ios_base::fmtflags()); - os.setf(ios_base::scientific, ios_base::floatfield); - os.setf(ios_base::right, ios_base::adjustfield); - os.precision(p); - - for (size_t i = 0; i < n; ++i) - if (a <= wav[i] and wav[i] <= b) { - // The normalized observed spectral flux and its uncertainty. - const real nfl = flx[i] / cfl[i]; - const real nun = unc[i] / cfl[i]; - - os << setw(w) << wav[i]; // 1 - os << setw(w) << flx[i]; // 2 - os << setw(w) << unc[i]; // 3 - os << setw(3) << msk[i]; // 4 - os << setw(w) << opt[i]; // 5 - os << setw(w) << atm[i]; // 6 - os << setw(w) << cat[i]; // 7 - os << setw(w) << cfl[i]; // 8 - os << setw(w) << tfl[i]; // 9 - os << setw(w) << fit[i]; // 10 - os << setw(w) << res[i]; // 11 - os << setw(w) << nfl; // 12 - os << setw(w) << nun; // 13 - - os << '\n'; - } - - os.flush(); - os.flags(f); +std::ostream & +especia::Section::put (std::ostream &os, const real a, const real b) const +{ + using namespace std; + + if (os) + { + // The precision. + const natural p = 8; + // The width of the output field. + const natural w = 16; + + const ios_base::fmtflags f = os.flags (); + + os.setf (ios_base::fmtflags ()); + os.setf (ios_base::scientific, ios_base::floatfield); + os.setf (ios_base::right, ios_base::adjustfield); + os.precision (p); + + for (size_t i = 0; i < n; ++i) + if (a <= wav[i] and wav[i] <= b) + { + // The normalized observed spectral flux and its uncertainty. + const real nfl = flx[i] / cfl[i]; + const real nun = unc[i] / cfl[i]; + + os << setw (w) << wav[i]; // 1 + os << setw (w) << flx[i]; // 2 + os << setw (w) << unc[i]; // 3 + os << setw (3) << msk[i]; // 4 + os << setw (w) << opt[i]; // 5 + os << setw (w) << atm[i]; // 6 + os << setw (w) << cat[i]; // 7 + os << setw (w) << cfl[i]; // 8 + os << setw (w) << tfl[i]; // 9 + os << setw (w) << fit[i]; // 10 + os << setw (w) << res[i]; // 11 + os << setw (w) << nfl; // 12 + os << setw (w) << nun; // 13 + + os << '\n'; + } + + os.flush (); + os.flags (f); } - return os; + return os; } -std::istream &especia::operator>>(std::istream &is, std::vector
§ions) { - using std::vector; +std::istream & +especia::operator>> (std::istream &is, std::vector
§ions) +{ + using std::vector; - Section s; + Section s; - while (is >> s) { - sections.push_back(s); + while (is >> s) + { + sections.push_back (s); } - return is; + return is; } -std::ostream &especia::operator<<(std::ostream &os, const std::vector
§ions) { - size_t i; +std::ostream & +especia::operator<< (std::ostream &os, const std::vector
§ions) +{ + size_t i; - for (i = 0; i + 1 < sections.size(); ++i) { - os << sections[i] << '\n'; + for (i = 0; i + 1 < sections.size (); ++i) + { + os << sections[i] << '\n'; } - os << sections[i]; + os << sections[i]; - return os; + return os; } diff --git a/src/main/cxx/core/section.h b/src/main/cxx/core/section.h index cb3fc1e..f72cae1 100644 --- a/src/main/cxx/core/section.h +++ b/src/main/cxx/core/section.h @@ -17,351 +17,399 @@ #include "base.h" -namespace especia { - - /// Represents a section of (observed and modelled) spectroscopic data. - class Section { - public: - - /// Constructs a new instance of this class, which contains no data. - Section(); - - /// Constructs a new instance of this class for a certain number of data points. - /// - /// @param[in] n_in The number of data points. - explicit Section(size_t n_in); - - /// Constructs a new instance of this class for a certain number of data points, - /// with given wavelength, flux, and uncertainty data. - /// - /// @param[in] n_in The number of data points. - /// @param[in] wav The wavelength data. - /// @param[in] flx The spectral flux data. - /// @param[in] unc The spectral flux uncertainty data. - Section(size_t n_in, const real wav[], const real flx[], const real unc[]); - - /// The destructor. - ~Section(); - - /// Reads a data section from an input stream. - /// - /// @param[in,out] is The input stream. - /// @param[in] a The minimum wavelength to read. - /// @param[in] b The maximum wavelength to read. - /// @return the input stream. - std::istream &get(std::istream &is, real a = 0.0, real b = std::numeric_limits::max()); - - /// Writes a data section to an output stream. - /// - /// @param[in,out] is The output stream. - /// @param[in] a The minimum wavelength to write. - /// @param[in] b The maximum wavelength to write. - /// @return the output stream. - std::ostream &put(std::ostream &os, real a = 0.0, real b = std::numeric_limits::max()) const; - - /// Returns the lower wavelength bound of this data section. - /// - /// @return the lower wavelength bound of this data section. - real lower_bound() const { - return n > 0 ? wav[0] : 0.0; - } - - /// Returns the central wavelength of this data section. - /// - /// @return the central wavelength of this data section. - real center() const { - return 0.5 * (lower_bound() + upper_bound()); - } - - /// Returns the upper wavelength bound of this data section. - /// - /// @return the upper wavelength bound of this data section. - real upper_bound() const { - return (n > 0) ? wav[n - 1] : 0.0; - } - - /// Returns the width of this data section. - /// - /// @return the width of this data section. - real width() const { - return upper_bound() - lower_bound(); - } - - /// Returns the number of data points in this section. - /// - /// @return the number of data points. - size_t data_count() const { - return n; - } - - /// Returns the number of valid data points in this section. - /// - /// @return the number of valid data points. - size_t valid_data_count() const; - - /// Returns the current value of the cost function. - /// - /// @return the current value of the cost function. - real cost() const; - - - /// Returns the value of the cost function as a function of a given optical depth - /// function. - /// - /// @tparam Function The type of optical depth function. - /// - /// @param[in] tau The optical depth function. - /// @param[in] r The spectral resolution of the instrument. - /// @param[in] m The number of Legendre basis polynomials to model the background continuum. - /// - /// @return the value of the cost function. - /// - /// @remark calling this method is thread safe, if the optical depth model is thread safe. - template - real cost(const Function &tau, const real r, const natural m) const { - using std::abs; - using std::valarray; - - valarray opt(n); - valarray atm(n); - valarray cat(n); - valarray cfl(n); - valarray tfl(n); - valarray fit(n); - valarray res(n); - - convolute(r, tau, opt, atm, cat); - continuum(m, cat, cfl); - - tfl = cfl * atm; - fit = cfl * cat; - res = (flx - fit) / unc; - - real cost = 0.0; - for (size_t i = 0; i < n; ++i) { - if (msk[i]) { - cost += sq(res[i]); - } - } - - return 0.5 * cost; - } - - /// Masks the data in a certain interval as invalid. - /// - /// @param[in] a The lower bound of the interval. - /// @param[in] b The upper bound of the interval. - void mask(real a, real b); - - /// Applies an optical depth and background continuum model to this section. - /// - /// @tparam Function The type of optical depth function. - /// - /// @param[in] m The number of Legendre basis polynomials to model the background continuum. - /// @param[in] r The spectral resolution of the instrument. - /// @param[in] tau The optical depth function. - /// - /// @return this section. - template - Section &apply(const natural m, const real r, const Function &tau) { - convolute(r, tau, opt, atm, cat); - continuum(m, cat, cfl); - - tfl = cfl * atm; - fit = cfl * cat; - res = (flx - fit) / unc; - - return *this; - } - - private: - /// Calculates an optimized background continuum. - /// - /// @param[in] m The number of Legendre basis polynomials to model the background continuum. - /// @param[in] cat The evaluated convoluted absorption term. - /// @param[out] cfl The evaluated background continuum flux. - void continuum(natural m, const std::valarray &cat, std::valarray &cfl) const; - - /// Convolutes a given optical depth function with the instrumental line spread function. - /// - /// @tparam Function The type of optical depth function. - /// - /// @param[in] r The spectral resolution of the instrument. - /// @param[in] tau The optical depth function. - /// @param[out] opt The evaluated optical depth. - /// @param[out] atm The evaluated absorption term. - /// @param[out] cat The evaluated convoluted absorption term. - template - void convolute(const real r, const Function &tau, - std::valarray &opt, std::valarray &atm, std::valarray &cat) const { - using std::ceil; - using std::exp; - using std::transform; - using std::valarray; - - if (n > 2) { - // The half width at half maximum (HWHM) of the instrumental profile. - const real h = 0.5 * center() / (r * kilo); - // The data spacing. - const real d = width() / (n - 1); - // The super-sampling factor. Is greater than one, if the data spacing is greater than the HWHM. - const auto s = static_cast(ceil(d / h)); - // The super-sampled spacing. - const real w = d / s; - // The Gaussian line spread function is truncated at 4 HWHM. - const auto m = static_cast(4.0 * (h / w)) + 1; - - // Computation of the instrumental line spread function's primitive terms. - valarray p(m); - valarray q(m); - for (natural i = 0; i < m; ++i) { - primitive(i * w, h, p[i], q[i]); - } - - if (s == 1) { - // Computation of optical depth and absorption term. - transform(begin(wav), end(wav), begin(opt), tau); - atm = exp(-opt); - - // Convolution of the absorption term with the instrumental line spread function. - for (size_t i = 0; i < n; ++i) { - real a = 0.0; - real b = 0.0; - - for (natural j = 0; j + 1 < m; ++j) { - const size_t k = (i < j + 1) ? 0 : i - j - 1; - const size_t l = (i + j + 2 > n) ? n - 2 : i + j; - const real c = (atm[l + 1] - atm[l]) - (atm[k + 1] - atm[k]); - - a += (p[j + 1] - p[j]) * (atm[k + 1] + atm[l] - real(j) * c); - b += (q[j + 1] - q[j]) * c; - } - - cat[i] = a + b / w; - } - } else { - // The number of super-samples. - const size_t ns = s * (n - 1) + 1; - - valarray wavs(ns); - valarray opts(ns); - valarray atms(ns); - supersample(wav, s, wavs); - - // Super-sampled computation of optical depth and absorption term. - transform(begin(wavs), end(wavs), begin(opts), tau); - atms = exp(-opts); - - // Super-sampled convolution of the absorption term with the instrumental line spread function. - for (size_t is = 0, it = 0; it < n; is += s, ++it) { - real a = 0.0; - real b = 0.0; - - for (natural j = 0; j + 1 < m; ++j) { - const size_t k = (is < j + 1) ? 0 : is - j - 1; - const size_t l = (is + j + 2 > ns) ? ns - 2 : is + j; - const real c = (atms[l + 1] - atms[l]) - (atms[k + 1] - atms[k]); - - a += (p[j + 1] - p[j]) * (atms[k + 1] + atms[l] - real(j) * c); - b += (q[j + 1] - q[j]) * c; - } - - opt[it] = opts[is]; - atm[it] = atms[is]; - cat[it] = a + b / w; - } - } - } - } - - /// Evaluates the primitive functions of g(x) and x g(x), where g(x) is the (Gaussian) - /// line spread function of the instrument. - /// - /// @param[in] x The abscissa value where to evaluate the primitive functions. - /// @param[in] h The half width at half maximum (HWHM) of the Gaussian line spread function. - /// @param[out] p The primitive function of g(x) evaluated at @ x. - /// @param[out] q The primitive function of x g(x) evaluated at @ x. - static void primitive(const real &x, const real &h, real &p, real &q); - - /// Super-samples a given data vector. - /// - /// @param[in] source The source data vector. - /// @param[in] k The super-sampling factor. - /// @param[out] target The target data vector (super-sampled). - static void supersample(const std::valarray &source, natural k, std::valarray &target); - - /// The observed wavelength data (arbitrary units). - std::valarray wav; - - /// The observed spectral flux data (arbitrary units). - std::valarray flx; - - /// The observed spectral flux uncertainty data (arbitrary units). - std::valarray unc; - - /// The selection mask. - std::valarray msk; - - /// The evaluated optical depth model. - std::valarray opt; - - /// The evaluated absorption term. - std::valarray atm; - - /// The evaluated convoluted absorption term. - std::valarray cat; - - /// The evaluated background continuum flux. - std::valarray cfl; - - /// The evaluated spectral flux. - std::valarray tfl; - - /// The evaluated convoluted spectral flux. - std::valarray fit; - - /// The evaluated residual flux. - std::valarray res; - - /// The number of data points. - size_t n; - }; - - - /// The operator to read a data section from an input stream. - /// - /// @param is[in,out] The input stream. - /// @param section[out] The data section. - /// @return the input stream. - inline - std::istream &operator>>(std::istream &is, Section §ion) { - return section.get(is); - } - - /// The operator to write a data section to an output stream. - /// - /// @param os[in,out] The output stream. - /// @param section[in] The data section. - /// @return the output stream. - inline - std::ostream &operator<<(std::ostream &os, const Section §ion) { - return section.put(os); - } - - /// The operator to read data sections from an input stream. - /// - /// @param is[in,out] The input stream. - /// @param section[out] The data sections. - /// @return the input stream. - std::istream &operator>>(std::istream &is, std::vector
§ions); - - /// The operator to write data sections to an output stream. - /// - /// @param os[in,out] The output stream. - /// @param section[in] The data sections. - /// @return the output stream. - std::ostream &operator<<(std::ostream &os, const std::vector
§ions); +namespace especia +{ + +/// Represents a section of (observed and modelled) spectroscopic data. +class Section +{ +public: + /// Constructs a new instance of this class, which contains no data. + Section (); + + /// Constructs a new instance of this class for a certain number of data + /// points. + /// + /// @param[in] n_in The number of data points. + explicit Section (size_t n_in); + + /// Constructs a new instance of this class for a certain number of data + /// points, with given wavelength, flux, and uncertainty data. + /// + /// @param[in] n_in The number of data points. + /// @param[in] wav The wavelength data. + /// @param[in] flx The spectral flux data. + /// @param[in] unc The spectral flux uncertainty data. + Section (size_t n_in, const real wav[], const real flx[], const real unc[]); + + /// The destructor. + ~Section (); + + /// Reads a data section from an input stream. + /// + /// @param[in,out] is The input stream. + /// @param[in] a The minimum wavelength to read. + /// @param[in] b The maximum wavelength to read. + /// @return the input stream. + std::istream &get (std::istream &is, real a = 0.0, + real b = std::numeric_limits::max ()); + + /// Writes a data section to an output stream. + /// + /// @param[in,out] is The output stream. + /// @param[in] a The minimum wavelength to write. + /// @param[in] b The maximum wavelength to write. + /// @return the output stream. + std::ostream &put (std::ostream &os, real a = 0.0, + real b = std::numeric_limits::max ()) const; + + /// Returns the lower wavelength bound of this data section. + /// + /// @return the lower wavelength bound of this data section. + real + lower_bound () const + { + return n > 0 ? wav[0] : 0.0; + } + + /// Returns the central wavelength of this data section. + /// + /// @return the central wavelength of this data section. + real + center () const + { + return 0.5 * (lower_bound () + upper_bound ()); + } + + /// Returns the upper wavelength bound of this data section. + /// + /// @return the upper wavelength bound of this data section. + real + upper_bound () const + { + return (n > 0) ? wav[n - 1] : 0.0; + } + + /// Returns the width of this data section. + /// + /// @return the width of this data section. + real + width () const + { + return upper_bound () - lower_bound (); + } + + /// Returns the number of data points in this section. + /// + /// @return the number of data points. + size_t + data_count () const + { + return n; + } + + /// Returns the number of valid data points in this section. + /// + /// @return the number of valid data points. + size_t valid_data_count () const; + + /// Returns the current value of the cost function. + /// + /// @return the current value of the cost function. + real cost () const; + + /// Returns the value of the cost function as a function of a given optical + /// depth function. + /// + /// @tparam Function The type of optical depth function. + /// + /// @param[in] tau The optical depth function. + /// @param[in] r The spectral resolution of the instrument. + /// @param[in] m The number of Legendre basis polynomials to model the + /// background continuum. + /// + /// @return the value of the cost function. + /// + /// @remark calling this method is thread safe, if the optical depth model is + /// thread safe. + template + real + cost (const Function &tau, const real r, const natural m) const + { + using std::abs; + using std::valarray; + + valarray opt (n); + valarray atm (n); + valarray cat (n); + valarray cfl (n); + valarray tfl (n); + valarray fit (n); + valarray res (n); + + convolute (r, tau, opt, atm, cat); + continuum (m, cat, cfl); + + tfl = cfl * atm; + fit = cfl * cat; + res = (flx - fit) / unc; + + real cost = 0.0; + for (size_t i = 0; i < n; ++i) + { + if (msk[i]) + { + cost += sq (res[i]); + } + } + + return 0.5 * cost; + } + + /// Masks the data in a certain interval as invalid. + /// + /// @param[in] a The lower bound of the interval. + /// @param[in] b The upper bound of the interval. + void mask (real a, real b); + + /// Applies an optical depth and background continuum model to this section. + /// + /// @tparam Function The type of optical depth function. + /// + /// @param[in] m The number of Legendre basis polynomials to model the + /// background continuum. + /// @param[in] r The spectral resolution of the instrument. + /// @param[in] tau The optical depth function. + /// + /// @return this section. + template + Section & + apply (const natural m, const real r, const Function &tau) + { + convolute (r, tau, opt, atm, cat); + continuum (m, cat, cfl); + + tfl = cfl * atm; + fit = cfl * cat; + res = (flx - fit) / unc; + + return *this; + } + +private: + /// Calculates an optimized background continuum. + /// + /// @param[in] m The number of Legendre basis polynomials to model the + /// background continuum. + /// @param[in] cat The evaluated convoluted absorption term. + /// @param[out] cfl The evaluated background continuum flux. + void continuum (natural m, const std::valarray &cat, + std::valarray &cfl) const; + + /// Convolutes a given optical depth function with the instrumental line + /// spread function. + /// + /// @tparam Function The type of optical depth function. + /// + /// @param[in] r The spectral resolution of the instrument. + /// @param[in] tau The optical depth function. + /// @param[out] opt The evaluated optical depth. + /// @param[out] atm The evaluated absorption term. + /// @param[out] cat The evaluated convoluted absorption term. + template + void + convolute (const real r, const Function &tau, std::valarray &opt, + std::valarray &atm, std::valarray &cat) const + { + using std::ceil; + using std::exp; + using std::transform; + using std::valarray; + + if (n > 2) + { + // The half width at half maximum (HWHM) of the instrumental profile. + const real h = 0.5 * center () / (r * kilo); + // The data spacing. + const real d = width () / (n - 1); + // The super-sampling factor. Is greater than one, if the data spacing + // is greater than the HWHM. + const auto s = static_cast (ceil (d / h)); + // The super-sampled spacing. + const real w = d / s; + // The Gaussian line spread function is truncated at 4 HWHM. + const auto m = static_cast (4.0 * (h / w)) + 1; + + // Computation of the instrumental line spread function's primitive + // terms. + valarray p (m); + valarray q (m); + for (natural i = 0; i < m; ++i) + { + primitive (i * w, h, p[i], q[i]); + } + + if (s == 1) + { + // Computation of optical depth and absorption term. + transform (begin (wav), end (wav), begin (opt), tau); + atm = exp (-opt); + + // Convolution of the absorption term with the instrumental line + // spread function. + for (size_t i = 0; i < n; ++i) + { + real a = 0.0; + real b = 0.0; + + for (natural j = 0; j + 1 < m; ++j) + { + const size_t k = (i < j + 1) ? 0 : i - j - 1; + const size_t l = (i + j + 2 > n) ? n - 2 : i + j; + const real c + = (atm[l + 1] - atm[l]) - (atm[k + 1] - atm[k]); + + a += (p[j + 1] - p[j]) + * (atm[k + 1] + atm[l] - real (j) * c); + b += (q[j + 1] - q[j]) * c; + } + + cat[i] = a + b / w; + } + } + else + { + // The number of super-samples. + const size_t ns = s * (n - 1) + 1; + + valarray wavs (ns); + valarray opts (ns); + valarray atms (ns); + supersample (wav, s, wavs); + + // Super-sampled computation of optical depth and absorption term. + transform (begin (wavs), end (wavs), begin (opts), tau); + atms = exp (-opts); + + // Super-sampled convolution of the absorption term with the + // instrumental line spread function. + for (size_t is = 0, it = 0; it < n; is += s, ++it) + { + real a = 0.0; + real b = 0.0; + + for (natural j = 0; j + 1 < m; ++j) + { + const size_t k = (is < j + 1) ? 0 : is - j - 1; + const size_t l = (is + j + 2 > ns) ? ns - 2 : is + j; + const real c + = (atms[l + 1] - atms[l]) - (atms[k + 1] - atms[k]); + + a += (p[j + 1] - p[j]) + * (atms[k + 1] + atms[l] - real (j) * c); + b += (q[j + 1] - q[j]) * c; + } + + opt[it] = opts[is]; + atm[it] = atms[is]; + cat[it] = a + b / w; + } + } + } + } + + /// Evaluates the primitive functions of g(x) and x g(x), where g(x) is the + /// (Gaussian) line spread function of the instrument. + /// + /// @param[in] x The abscissa value where to evaluate the primitive + /// functions. + /// @param[in] h The half width at half maximum (HWHM) of the Gaussian line + /// spread function. + /// @param[out] p The primitive function of g(x) evaluated at @ x. + /// @param[out] q The primitive function of x g(x) evaluated at @ x. + static void primitive (const real &x, const real &h, real &p, real &q); + + /// Super-samples a given data vector. + /// + /// @param[in] source The source data vector. + /// @param[in] k The super-sampling factor. + /// @param[out] target The target data vector (super-sampled). + static void supersample (const std::valarray &source, natural k, + std::valarray &target); + + /// The observed wavelength data (arbitrary units). + std::valarray wav; + + /// The observed spectral flux data (arbitrary units). + std::valarray flx; + + /// The observed spectral flux uncertainty data (arbitrary units). + std::valarray unc; + + /// The selection mask. + std::valarray msk; + + /// The evaluated optical depth model. + std::valarray opt; + + /// The evaluated absorption term. + std::valarray atm; + + /// The evaluated convoluted absorption term. + std::valarray cat; + + /// The evaluated background continuum flux. + std::valarray cfl; + + /// The evaluated spectral flux. + std::valarray tfl; + + /// The evaluated convoluted spectral flux. + std::valarray fit; + + /// The evaluated residual flux. + std::valarray res; + + /// The number of data points. + size_t n; +}; + +/// The operator to read a data section from an input stream. +/// +/// @param is[in,out] The input stream. +/// @param section[out] The data section. +/// @return the input stream. +inline std::istream & +operator>> (std::istream &is, Section §ion) +{ + return section.get (is); } -#endif // ESPECIA_SECTION_H +/// The operator to write a data section to an output stream. +/// +/// @param os[in,out] The output stream. +/// @param section[in] The data section. +/// @return the output stream. +inline std::ostream & +operator<< (std::ostream &os, const Section §ion) +{ + return section.put (os); +} +/// The operator to read data sections from an input stream. +/// +/// @param is[in,out] The input stream. +/// @param section[out] The data sections. +/// @return the input stream. +std::istream &operator>> (std::istream &is, std::vector
§ions); + +/// The operator to write data sections to an output stream. +/// +/// @param os[in,out] The output stream. +/// @param section[in] The data sections. +/// @return the output stream. +std::ostream &operator<< (std::ostream &os, + const std::vector
§ions); +} + +#endif // ESPECIA_SECTION_H diff --git a/src/main/cxx/util/airtovac.cxx b/src/main/cxx/util/airtovac.cxx index d468769..95bbc8b 100644 --- a/src/main/cxx/util/airtovac.cxx +++ b/src/main/cxx/util/airtovac.cxx @@ -15,9 +15,8 @@ using namespace std; using especia::natural; using especia::real; - -/// Utility to convert photon wavelength (Angstrom) in spectroscopic data from air -/// to vacuum. +/// Utility to convert photon wavelength (Angstrom) in spectroscopic data from +/// air to vacuum. /// /// Further reading: /// @@ -32,47 +31,66 @@ using especia::real; /// /// @param argc The number of command line arguments supplied. /// @param argv[0] The program name. -/// @param argv[1] The number of lines to skip at the beginning (optional, default = 0). +/// @param argv[1] The number of lines to skip at the beginning (optional, +/// default = 0). /// @return an exit code. /// /// @remark Usage: airtovac [lines to skip] < {source file} [> {target file}] -int main(int argc, char *argv[]) { - using especia::Equations; +int +main (int argc, char *argv[]) +{ + using especia::Equations; - const string program_name(argv[0]); + const string program_name (argv[0]); - try { - if (argc != 1 and argc != 2) { - throw invalid_argument("Error: an invalid number of arguments was supplied"); + try + { + if (argc != 1 and argc != 2) + { + throw invalid_argument ( + "Error: an invalid number of arguments was supplied"); } - natural skip = 0; + natural skip = 0; - if (argc == 2) { - skip = especia::convert(string(argv[1])); + if (argc == 2) + { + skip = especia::convert (string (argv[1])); } - valarray x; - valarray y; - valarray z; + valarray x; + valarray y; + valarray z; - if (especia::get(cin, x, y, z, skip)) { - for (size_t i = 0; i < x.size(); ++i) { - x[i] = real(10.0) / especia::solve(Equations::edlen66, real(10.0) / x[i], real(10.0) / x[i], real(1.0E-08)); + if (especia::get (cin, x, y, z, skip)) + { + for (size_t i = 0; i < x.size (); ++i) + { + x[i] = real (10.0) + / especia::solve (Equations::edlen66, real (10.0) / x[i], + real (10.0) / x[i], real (1.0E-08)); } - especia::put(cout, x, y, z); - } else { - throw runtime_error("Error: an input error occurred"); + especia::put (cout, x, y, z); + } + else + { + throw runtime_error ("Error: an input error occurred"); } - return 0; - } catch (logic_error &e) { - cerr << e.what() << endl; - return especia::Exit_Codes::logic_error; - } catch (runtime_error &e) { - cerr << e.what() << endl; - return especia::Exit_Codes::runtime_error; - } catch (exception &e) { - cerr << e.what() << endl; - return especia::Exit_Codes::unspecific_exception; + return 0; + } + catch (logic_error &e) + { + cerr << e.what () << endl; + return especia::Exit_Codes::logic_error; + } + catch (runtime_error &e) + { + cerr << e.what () << endl; + return especia::Exit_Codes::runtime_error; + } + catch (exception &e) + { + cerr << e.what () << endl; + return especia::Exit_Codes::unspecific_exception; } } diff --git a/src/main/cxx/util/helicorr.cxx b/src/main/cxx/util/helicorr.cxx index d05c893..3cf1909 100644 --- a/src/main/cxx/util/helicorr.cxx +++ b/src/main/cxx/util/helicorr.cxx @@ -15,13 +15,17 @@ using namespace std; using especia::natural; using especia::real; - /// Writes the usage message to an output stream. /// /// @param os The ouput stream. /// @param pname The program name. -void write_usage_message(ostream &os, const string &pname) { - os << "usage: " << pname << " {velocity (m s-1)} [skip] < {source data file} [> {target data file}]" << endl; +void +write_usage_message (ostream &os, const string &pname) +{ + os << "usage: " << pname + << " {velocity (m s-1)} [skip] < {source data file} [> {target data " + "file}]" + << endl; } /// Utility to apply the heliocentric (or barycentric) velocity correction to @@ -32,51 +36,70 @@ void write_usage_message(ostream &os, const string &pname) { /// @param argv[1] The velocity of the observer relative to the heliocenter (or /// barycenter) of the solar system (m s-1) projected along the line of sight /// toward the observed object. -/// @param argv[2] The number of lines to skip at the beginning (optional, default = 0). +/// @param argv[2] The number of lines to skip at the beginning (optional, +/// default = 0). /// @return an exit code. /// -/// @remark Usage: helicorr {velocity (m s-1)} [lines to skip] < {source file} [> {target file}] -int main(int argc, char *argv[]) { - const string program_name(argv[0]); +/// @remark Usage: helicorr {velocity (m s-1)} [lines to skip] < {source file} +/// [> {target file}] +int +main (int argc, char *argv[]) +{ + const string program_name (argv[0]); - if (argc == 1) { - write_usage_message(cout, program_name); - return 0; + if (argc == 1) + { + write_usage_message (cout, program_name); + return 0; } - try { - if (argc != 2 and argc != 3) { - throw invalid_argument("Error: an invalid number of arguments was supplied"); + try + { + if (argc != 2 and argc != 3) + { + throw invalid_argument ( + "Error: an invalid number of arguments was supplied"); } - const auto v = especia::convert(string(argv[1])); + const auto v = especia::convert (string (argv[1])); - natural skip = 0; + natural skip = 0; - if (argc == 3) { - skip = especia::convert(string(argv[2])); + if (argc == 3) + { + skip = especia::convert (string (argv[2])); } - valarray x; - valarray y; - valarray z; + valarray x; + valarray y; + valarray z; - if (especia::get(cin, x, y, z, skip)) { - if (v != 0.0) { - x *= 1.0 + especia::redshift(v); + if (especia::get (cin, x, y, z, skip)) + { + if (v != 0.0) + { + x *= 1.0 + especia::redshift (v); } - especia::put(cout, x, y, z); - } else { - throw runtime_error("Error: an input error occurred"); + especia::put (cout, x, y, z); + } + else + { + throw runtime_error ("Error: an input error occurred"); } - return 0; - } catch (logic_error &e) { - cerr << e.what() << endl; - return especia::Exit_Codes::logic_error; - } catch (runtime_error &e) { - cerr << e.what() << endl; - return especia::Exit_Codes::runtime_error; - } catch (exception &e) { - cerr << e.what() << endl; - return especia::Exit_Codes::unspecific_exception; + return 0; + } + catch (logic_error &e) + { + cerr << e.what () << endl; + return especia::Exit_Codes::logic_error; + } + catch (runtime_error &e) + { + cerr << e.what () << endl; + return especia::Exit_Codes::runtime_error; + } + catch (exception &e) + { + cerr << e.what () << endl; + return especia::Exit_Codes::unspecific_exception; } } diff --git a/src/main/cxx/util/vactoair.cxx b/src/main/cxx/util/vactoair.cxx index c49857d..daa13fe 100644 --- a/src/main/cxx/util/vactoair.cxx +++ b/src/main/cxx/util/vactoair.cxx @@ -15,7 +15,6 @@ using namespace std; using especia::natural; using especia::real; - /// Utility to convert photon wavelength (Angstrom) in spectroscopic data from /// vacuum to air. /// @@ -32,47 +31,64 @@ using especia::real; /// /// @param argc The number of command line arguments supplied. /// @param argv[0] The program name. -/// @param argv[1] The number of lines to skip at the beginning (optional, default = 0). +/// @param argv[1] The number of lines to skip at the beginning (optional, +/// default = 0). /// @return an exit code. /// /// @remark Usage: vactoair [lines to skip] < {source file} [> {target file}] -int main(int argc, char *argv[]) { - using especia::Equations; +int +main (int argc, char *argv[]) +{ + using especia::Equations; - const string pname(argv[0]); + const string pname (argv[0]); - try { - if (argc != 1 and argc != 2) { - throw invalid_argument("Error: an invalid number of arguments was supplied"); + try + { + if (argc != 1 and argc != 2) + { + throw invalid_argument ( + "Error: an invalid number of arguments was supplied"); } - natural skip = 0; + natural skip = 0; - if (argc == 2) { - skip = especia::convert(string(argv[1])); + if (argc == 2) + { + skip = especia::convert (string (argv[1])); } - valarray x; - valarray y; - valarray z; + valarray x; + valarray y; + valarray z; - if (especia::get(cin, x, y, z, skip)) { - for (size_t i = 0; i < x.size(); ++i) { - x[i] = real(10.0) / Equations::edlen66(real(10.0) / x[i]); + if (especia::get (cin, x, y, z, skip)) + { + for (size_t i = 0; i < x.size (); ++i) + { + x[i] = real (10.0) / Equations::edlen66 (real (10.0) / x[i]); } - especia::put(cout, x, y, z); - } else { - throw runtime_error("Error: an input error occurred"); + especia::put (cout, x, y, z); + } + else + { + throw runtime_error ("Error: an input error occurred"); } - return 0; - } catch (logic_error &e) { - cerr << e.what() << endl; - return especia::Exit_Codes::logic_error; - } catch (runtime_error &e) { - cerr << e.what() << endl; - return especia::Exit_Codes::runtime_error; - } catch (exception &e) { - cerr << e.what() << endl; - return especia::Exit_Codes::unspecific_exception; + return 0; + } + catch (logic_error &e) + { + cerr << e.what () << endl; + return especia::Exit_Codes::logic_error; + } + catch (runtime_error &e) + { + cerr << e.what () << endl; + return especia::Exit_Codes::runtime_error; + } + catch (exception &e) + { + cerr << e.what () << endl; + return especia::Exit_Codes::unspecific_exception; } } diff --git a/src/test/cxx/core/decompose_test.cxx b/src/test/cxx/core/decompose_test.cxx index 093493c..ff192b0 100644 --- a/src/test/cxx/core/decompose_test.cxx +++ b/src/test/cxx/core/decompose_test.cxx @@ -9,204 +9,286 @@ using especia::natural; using especia::real; -class Decompose_Test : public Unit_Test { +class Decompose_Test : public Unit_Test +{ private: - - void test_decompose_diagonal_matrix_D() { - using especia::D_Decompose; - - const natural n = 3; - const D_Decompose decompose(n); - - const real A[n * n] = { real(1), real(0), real(0), - real(0), real(2), real(0), - real(0), real(0), real(3) }; - - real Z[n * n]; - real w[n]; - - decompose(A, Z, w); - - assert_equals(real(1), Z[0], real(0), "D decompose diagonal matrix (Z)"); - assert_equals(real(0), Z[1], real(0), "D decompose diagonal matrix (Z)"); - assert_equals(real(0), Z[2], real(0), "D decompose diagonal matrix (Z)"); - assert_equals(real(0), Z[3], real(0), "D decompose diagonal matrix (Z)"); - assert_equals(real(1), Z[4], real(0), "D decompose diagonal matrix (Z)"); - assert_equals(real(0), Z[5], real(0), "D decompose diagonal matrix (Z)"); - assert_equals(real(0), Z[6], real(0), "D decompose diagonal matrix (Z)"); - assert_equals(real(0), Z[7], real(0), "D decompose diagonal matrix (Z)"); - assert_equals(real(1), Z[8], real(0), "D decompose diagonal matrix (Z)"); - - assert_equals(real(1), w[0], real(0), "D decompose diagonal matrix (w)"); - assert_equals(real(2), w[1], real(0), "D decompose diagonal matrix (w)"); - assert_equals(real(3), w[2], real(0), "D decompose diagonal matrix (w)"); - } - - void test_decompose_diagonal_matrix_R() { - using especia::R_Decompose; - - const natural n = 3; - const R_Decompose decompose(n); - - const real A[n * n] = { real(1), real(0), real(0), - real(0), real(2), real(0), - real(0), real(0), real(3) }; - - real Z[n * n]; - real w[n]; - - decompose(A, Z, w); - - assert_equals(real(1), Z[0], real(0), "R decompose diagonal matrix (Z)"); - assert_equals(real(0), Z[1], real(0), "R decompose diagonal matrix (Z)"); - assert_equals(real(0), Z[2], real(0), "R decompose diagonal matrix (Z)"); - assert_equals(real(0), Z[3], real(0), "R decompose diagonal matrix (Z)"); - assert_equals(real(1), Z[4], real(0), "R decompose diagonal matrix (Z)"); - assert_equals(real(0), Z[5], real(0), "R decompose diagonal matrix (Z)"); - assert_equals(real(0), Z[6], real(0), "R decompose diagonal matrix (Z)"); - assert_equals(real(0), Z[7], real(0), "R decompose diagonal matrix (Z)"); - assert_equals(real(1), Z[8], real(0), "R decompose diagonal matrix (Z)"); - - assert_equals(real(1), w[0], real(0), "R decompose diagonal matrix (w)"); - assert_equals(real(2), w[1], real(0), "R decompose diagonal matrix (w)"); - assert_equals(real(3), w[2], real(0), "R decompose diagonal matrix (w)"); - } - - void test_decompose_diagonal_matrix_X() { - using especia::X_Decompose; - - const natural n = 3; - const X_Decompose decompose(n); - - const real A[n * n] = { real(1), real(0), real(0), - real(0), real(2), real(0), - real(0), real(0), real(3) }; - - real Z[n * n]; - real w[n]; - - decompose(A, Z, w); - - assert_equals(real(1), Z[0], real(0), "X decompose diagonal matrix (Z)"); - assert_equals(real(0), Z[1], real(0), "X decompose diagonal matrix (Z)"); - assert_equals(real(0), Z[2], real(0), "X decompose diagonal matrix (Z)"); - assert_equals(real(0), Z[3], real(0), "X decompose diagonal matrix (Z)"); - assert_equals(real(1), Z[4], real(0), "X decompose diagonal matrix (Z)"); - assert_equals(real(0), Z[5], real(0), "X decompose diagonal matrix (Z)"); - assert_equals(real(0), Z[6], real(0), "X decompose diagonal matrix (Z)"); - assert_equals(real(0), Z[7], real(0), "X decompose diagonal matrix (Z)"); - assert_equals(real(1), Z[8], real(0), "X decompose diagonal matrix (Z)"); - - assert_equals(real(1), w[0], real(0), "X decompose diagonal matrix (w)"); - assert_equals(real(2), w[1], real(0), "X decompose diagonal matrix (w)"); - assert_equals(real(3), w[2], real(0), "X decompose diagonal matrix (w)"); - } - - void test_decompose_symmetric_matrix_D() { - using especia::D_Decompose; - - const natural n = 3; - const D_Decompose decompose(n); - - const real A[n * n] = { real(1), real(2), real(3), - real(2), real(4), real(5), - real(3), real(5), real(6) }; - - real Z[n * n]; - real w[n]; - - decompose(A, Z, w); - - // the eigenvectors have different signs than those provided by Wolfram Alpha - assert_equals(real( 0.736976), Z[0], real(1.0E-06), "D decompose symmetric matrix (Z)"); - assert_equals(real( 0.327985), Z[1], real(1.0E-06), "D decompose symmetric matrix (Z)"); - assert_equals(real(-0.591009), Z[2], real(1.0E-06), "D decompose symmetric matrix (Z)"); - assert_equals(real( 0.591009), Z[3], real(1.0E-06), "D decompose symmetric matrix (Z)"); - assert_equals(real(-0.736976), Z[4], real(1.0E-06), "D decompose symmetric matrix (Z)"); - assert_equals(real( 0.327985), Z[5], real(1.0E-06), "D decompose symmetric matrix (Z)"); - assert_equals(real( 0.327985), Z[6], real(1.0E-06), "D decompose symmetric matrix (Z)"); - assert_equals(real( 0.591009), Z[7], real(1.0E-06), "D decompose symmetric matrix (Z)"); - assert_equals(real( 0.736976), Z[8], real(1.0E-06), "D decompose symmetric matrix (Z)"); - - assert_equals(real(-0.515729), w[0], real(1.0E-06), "D decompose symmetric matrix (w)"); - assert_equals(real( 0.170915), w[1], real(1.0E-06), "D decompose symmetric matrix (w)"); - assert_equals(real( 11.34480), w[2], real(1.0E-04), "D decompose symmetric matrix (w)"); - } - - void test_decompose_symmetric_matrix_R() { - using especia::R_Decompose; - - const natural n = 3; - const R_Decompose decompose(n); - - const real A[n * n] = { real(1), real(2), real(3), - real(2), real(4), real(5), - real(3), real(5), real(6) }; - - real Z[n * n]; - real w[n]; - - decompose(A, Z, w); - - // - assert_equals(real(-0.736976), Z[0], real(1.0E-06), "R decompose symmetric matrix (Z)"); - assert_equals(real(-0.327985), Z[1], real(1.0E-06), "R decompose symmetric matrix (Z)"); - assert_equals(real( 0.591009), Z[2], real(1.0E-06), "R decompose symmetric matrix (Z)"); - assert_equals(real( 0.591009), Z[3], real(1.0E-06), "R decompose symmetric matrix (Z)"); - assert_equals(real(-0.736976), Z[4], real(1.0E-06), "R decompose symmetric matrix (Z)"); - assert_equals(real( 0.327985), Z[5], real(1.0E-06), "R decompose symmetric matrix (Z)"); - assert_equals(real(-0.327985), Z[6], real(1.0E-06), "R decompose symmetric matrix (Z)"); - assert_equals(real(-0.591009), Z[7], real(1.0E-06), "R decompose symmetric matrix (Z)"); - assert_equals(real(-0.736976), Z[8], real(1.0E-06), "R decompose symmetric matrix (Z)"); - - assert_equals(real(-0.515729), w[0], real(1.0E-06), "R decompose symmetric matrix (w)"); - assert_equals(real( 0.170915), w[1], real(1.0E-06), "R decompose symmetric matrix (w)"); - assert_equals(real( 11.34480), w[2], real(1.0E-04), "R decompose symmetric matrix (w)"); - } - - void test_decompose_symmetric_matrix_X() { - using especia::X_Decompose; - - const natural n = 3; - const X_Decompose decompose(n); - - const real A[n * n] = { real(1), real(2), real(3), - real(2), real(4), real(5), - real(3), real(5), real(6) }; - - real Z[n * n]; - real w[n]; - - decompose(A, Z, w); - - // the eigenvectors have different signs than those provided by Wolfram Alpha - assert_equals(real(-0.736976), Z[0], real(1.0E-06), "X decompose symmetric matrix (Z)"); - assert_equals(real(-0.327985), Z[1], real(1.0E-06), "X decompose symmetric matrix (Z)"); - assert_equals(real( 0.591009), Z[2], real(1.0E-06), "X decompose symmetric matrix (Z)"); - assert_equals(real( 0.591009), Z[3], real(1.0E-06), "X decompose symmetric matrix (Z)"); - assert_equals(real(-0.736976), Z[4], real(1.0E-06), "X decompose symmetric matrix (Z)"); - assert_equals(real( 0.327985), Z[5], real(1.0E-06), "X decompose symmetric matrix (Z)"); - assert_equals(real( 0.327985), Z[6], real(1.0E-06), "X decompose symmetric matrix (Z)"); - assert_equals(real( 0.591009), Z[7], real(1.0E-06), "X decompose symmetric matrix (Z)"); - assert_equals(real( 0.736976), Z[8], real(1.0E-06), "X decompose symmetric matrix (Z)"); - - assert_equals(real(-0.515729), w[0], real(1.0E-06), "X decompose symmetric matrix (w)"); - assert_equals(real( 0.170915), w[1], real(1.0E-06), "X decompose symmetric matrix (w)"); - assert_equals(real( 11.34480), w[2], real(1.0E-04), "X decompose symmetric matrix (w)"); - } - - - void run_all() override { - run(this, &Decompose_Test::test_decompose_diagonal_matrix_D); - run(this, &Decompose_Test::test_decompose_diagonal_matrix_R); - run(this, &Decompose_Test::test_decompose_diagonal_matrix_X); - run(this, &Decompose_Test::test_decompose_symmetric_matrix_D); - run(this, &Decompose_Test::test_decompose_symmetric_matrix_R); - run(this, &Decompose_Test::test_decompose_symmetric_matrix_X); - } + void + test_decompose_diagonal_matrix_D () + { + using especia::D_Decompose; + + const natural n = 3; + const D_Decompose decompose (n); + + const real A[n * n] = { real (1), real (0), real (0), real (0), real (2), + real (0), real (0), real (0), real (3) }; + + real Z[n * n]; + real w[n]; + + decompose (A, Z, w); + + assert_equals (real (1), Z[0], real (0), + "D decompose diagonal matrix (Z)"); + assert_equals (real (0), Z[1], real (0), + "D decompose diagonal matrix (Z)"); + assert_equals (real (0), Z[2], real (0), + "D decompose diagonal matrix (Z)"); + assert_equals (real (0), Z[3], real (0), + "D decompose diagonal matrix (Z)"); + assert_equals (real (1), Z[4], real (0), + "D decompose diagonal matrix (Z)"); + assert_equals (real (0), Z[5], real (0), + "D decompose diagonal matrix (Z)"); + assert_equals (real (0), Z[6], real (0), + "D decompose diagonal matrix (Z)"); + assert_equals (real (0), Z[7], real (0), + "D decompose diagonal matrix (Z)"); + assert_equals (real (1), Z[8], real (0), + "D decompose diagonal matrix (Z)"); + + assert_equals (real (1), w[0], real (0), + "D decompose diagonal matrix (w)"); + assert_equals (real (2), w[1], real (0), + "D decompose diagonal matrix (w)"); + assert_equals (real (3), w[2], real (0), + "D decompose diagonal matrix (w)"); + } + + void + test_decompose_diagonal_matrix_R () + { + using especia::R_Decompose; + + const natural n = 3; + const R_Decompose decompose (n); + + const real A[n * n] = { real (1), real (0), real (0), real (0), real (2), + real (0), real (0), real (0), real (3) }; + + real Z[n * n]; + real w[n]; + + decompose (A, Z, w); + + assert_equals (real (1), Z[0], real (0), + "R decompose diagonal matrix (Z)"); + assert_equals (real (0), Z[1], real (0), + "R decompose diagonal matrix (Z)"); + assert_equals (real (0), Z[2], real (0), + "R decompose diagonal matrix (Z)"); + assert_equals (real (0), Z[3], real (0), + "R decompose diagonal matrix (Z)"); + assert_equals (real (1), Z[4], real (0), + "R decompose diagonal matrix (Z)"); + assert_equals (real (0), Z[5], real (0), + "R decompose diagonal matrix (Z)"); + assert_equals (real (0), Z[6], real (0), + "R decompose diagonal matrix (Z)"); + assert_equals (real (0), Z[7], real (0), + "R decompose diagonal matrix (Z)"); + assert_equals (real (1), Z[8], real (0), + "R decompose diagonal matrix (Z)"); + + assert_equals (real (1), w[0], real (0), + "R decompose diagonal matrix (w)"); + assert_equals (real (2), w[1], real (0), + "R decompose diagonal matrix (w)"); + assert_equals (real (3), w[2], real (0), + "R decompose diagonal matrix (w)"); + } + + void + test_decompose_diagonal_matrix_X () + { + using especia::X_Decompose; + + const natural n = 3; + const X_Decompose decompose (n); + + const real A[n * n] = { real (1), real (0), real (0), real (0), real (2), + real (0), real (0), real (0), real (3) }; + + real Z[n * n]; + real w[n]; + + decompose (A, Z, w); + + assert_equals (real (1), Z[0], real (0), + "X decompose diagonal matrix (Z)"); + assert_equals (real (0), Z[1], real (0), + "X decompose diagonal matrix (Z)"); + assert_equals (real (0), Z[2], real (0), + "X decompose diagonal matrix (Z)"); + assert_equals (real (0), Z[3], real (0), + "X decompose diagonal matrix (Z)"); + assert_equals (real (1), Z[4], real (0), + "X decompose diagonal matrix (Z)"); + assert_equals (real (0), Z[5], real (0), + "X decompose diagonal matrix (Z)"); + assert_equals (real (0), Z[6], real (0), + "X decompose diagonal matrix (Z)"); + assert_equals (real (0), Z[7], real (0), + "X decompose diagonal matrix (Z)"); + assert_equals (real (1), Z[8], real (0), + "X decompose diagonal matrix (Z)"); + + assert_equals (real (1), w[0], real (0), + "X decompose diagonal matrix (w)"); + assert_equals (real (2), w[1], real (0), + "X decompose diagonal matrix (w)"); + assert_equals (real (3), w[2], real (0), + "X decompose diagonal matrix (w)"); + } + + void + test_decompose_symmetric_matrix_D () + { + using especia::D_Decompose; + + const natural n = 3; + const D_Decompose decompose (n); + + const real A[n * n] = { real (1), real (2), real (3), real (2), real (4), + real (5), real (3), real (5), real (6) }; + + real Z[n * n]; + real w[n]; + + decompose (A, Z, w); + + // the eigenvectors have different signs than those provided by Wolfram + // Alpha + assert_equals (real (0.736976), Z[0], real (1.0E-06), + "D decompose symmetric matrix (Z)"); + assert_equals (real (0.327985), Z[1], real (1.0E-06), + "D decompose symmetric matrix (Z)"); + assert_equals (real (-0.591009), Z[2], real (1.0E-06), + "D decompose symmetric matrix (Z)"); + assert_equals (real (0.591009), Z[3], real (1.0E-06), + "D decompose symmetric matrix (Z)"); + assert_equals (real (-0.736976), Z[4], real (1.0E-06), + "D decompose symmetric matrix (Z)"); + assert_equals (real (0.327985), Z[5], real (1.0E-06), + "D decompose symmetric matrix (Z)"); + assert_equals (real (0.327985), Z[6], real (1.0E-06), + "D decompose symmetric matrix (Z)"); + assert_equals (real (0.591009), Z[7], real (1.0E-06), + "D decompose symmetric matrix (Z)"); + assert_equals (real (0.736976), Z[8], real (1.0E-06), + "D decompose symmetric matrix (Z)"); + + assert_equals (real (-0.515729), w[0], real (1.0E-06), + "D decompose symmetric matrix (w)"); + assert_equals (real (0.170915), w[1], real (1.0E-06), + "D decompose symmetric matrix (w)"); + assert_equals (real (11.34480), w[2], real (1.0E-04), + "D decompose symmetric matrix (w)"); + } + + void + test_decompose_symmetric_matrix_R () + { + using especia::R_Decompose; + + const natural n = 3; + const R_Decompose decompose (n); + + const real A[n * n] = { real (1), real (2), real (3), real (2), real (4), + real (5), real (3), real (5), real (6) }; + + real Z[n * n]; + real w[n]; + + decompose (A, Z, w); + + // + assert_equals (real (-0.736976), Z[0], real (1.0E-06), + "R decompose symmetric matrix (Z)"); + assert_equals (real (-0.327985), Z[1], real (1.0E-06), + "R decompose symmetric matrix (Z)"); + assert_equals (real (0.591009), Z[2], real (1.0E-06), + "R decompose symmetric matrix (Z)"); + assert_equals (real (0.591009), Z[3], real (1.0E-06), + "R decompose symmetric matrix (Z)"); + assert_equals (real (-0.736976), Z[4], real (1.0E-06), + "R decompose symmetric matrix (Z)"); + assert_equals (real (0.327985), Z[5], real (1.0E-06), + "R decompose symmetric matrix (Z)"); + assert_equals (real (-0.327985), Z[6], real (1.0E-06), + "R decompose symmetric matrix (Z)"); + assert_equals (real (-0.591009), Z[7], real (1.0E-06), + "R decompose symmetric matrix (Z)"); + assert_equals (real (-0.736976), Z[8], real (1.0E-06), + "R decompose symmetric matrix (Z)"); + + assert_equals (real (-0.515729), w[0], real (1.0E-06), + "R decompose symmetric matrix (w)"); + assert_equals (real (0.170915), w[1], real (1.0E-06), + "R decompose symmetric matrix (w)"); + assert_equals (real (11.34480), w[2], real (1.0E-04), + "R decompose symmetric matrix (w)"); + } + + void + test_decompose_symmetric_matrix_X () + { + using especia::X_Decompose; + + const natural n = 3; + const X_Decompose decompose (n); + + const real A[n * n] = { real (1), real (2), real (3), real (2), real (4), + real (5), real (3), real (5), real (6) }; + + real Z[n * n]; + real w[n]; + + decompose (A, Z, w); + + // the eigenvectors have different signs than those provided by Wolfram + // Alpha + assert_equals (real (-0.736976), Z[0], real (1.0E-06), + "X decompose symmetric matrix (Z)"); + assert_equals (real (-0.327985), Z[1], real (1.0E-06), + "X decompose symmetric matrix (Z)"); + assert_equals (real (0.591009), Z[2], real (1.0E-06), + "X decompose symmetric matrix (Z)"); + assert_equals (real (0.591009), Z[3], real (1.0E-06), + "X decompose symmetric matrix (Z)"); + assert_equals (real (-0.736976), Z[4], real (1.0E-06), + "X decompose symmetric matrix (Z)"); + assert_equals (real (0.327985), Z[5], real (1.0E-06), + "X decompose symmetric matrix (Z)"); + assert_equals (real (0.327985), Z[6], real (1.0E-06), + "X decompose symmetric matrix (Z)"); + assert_equals (real (0.591009), Z[7], real (1.0E-06), + "X decompose symmetric matrix (Z)"); + assert_equals (real (0.736976), Z[8], real (1.0E-06), + "X decompose symmetric matrix (Z)"); + + assert_equals (real (-0.515729), w[0], real (1.0E-06), + "X decompose symmetric matrix (w)"); + assert_equals (real (0.170915), w[1], real (1.0E-06), + "X decompose symmetric matrix (w)"); + assert_equals (real (11.34480), w[2], real (1.0E-04), + "X decompose symmetric matrix (w)"); + } + + void + run_all () override + { + run (this, &Decompose_Test::test_decompose_diagonal_matrix_D); + run (this, &Decompose_Test::test_decompose_diagonal_matrix_R); + run (this, &Decompose_Test::test_decompose_diagonal_matrix_X); + run (this, &Decompose_Test::test_decompose_symmetric_matrix_D); + run (this, &Decompose_Test::test_decompose_symmetric_matrix_R); + run (this, &Decompose_Test::test_decompose_symmetric_matrix_X); + } }; - -int main() { - return Decompose_Test().run_testsuite(); +int +main () +{ + return Decompose_Test ().run_testsuite (); } diff --git a/src/test/cxx/core/integrator_test.cxx b/src/test/cxx/core/integrator_test.cxx index dcb7d36..3c26da3 100644 --- a/src/test/cxx/core/integrator_test.cxx +++ b/src/test/cxx/core/integrator_test.cxx @@ -9,96 +9,119 @@ using especia::Integrator; - -class Integrator_Test : public Unit_Test { +class Integrator_Test : public Unit_Test +{ private: - - void test_integrate_cos() { - using std::cos; - using especia::pi; - - const double result = integrator.integrate([](double x) -> double { return cos(x); }, 0.0, pi); - - assert_equals(0.0, result, 0.5E-06, "integrate cosine"); - } - - void test_integrate_sin() { - using std::sin; - using especia::pi; - - const double result = integrator.integrate([](double x) -> double { return sin(x); }, 0.0, pi); - - assert_equals(2.0, result, 0.5E-06, "integrate sine"); - } - - void test_integrate_sin_sq() { - using std::sin; - using especia::pi; - using especia::sq; - - const double result = integrator.integrate([](double x) -> double { return sq(sin(x)); }, 0.0, 2.0 * pi); - - assert_equals(pi, result, 0.5E-06, "integrate sine squared"); - } - - void test_integrate_absorption() { - using std::exp; - using especia::sq; - - const double result = integrator.integrate( - [](double x) -> double { return 1.0 - exp(-exp(-sq(x))); }, 0.0, 4.0); - - // - assert_equals(0.642572, result, 0.5E-06, "integrate absorption"); - } - - void test_integrate_absorption_positive_infinite() { - using std::exp; - using especia::sq; - - const double result = integrator.integrate_positive_infinite( - [](double x) -> double { return 1.0 - exp(-exp(-sq(x))); }); - - // - assert_equals(0.642572, result, 0.5E-06, "integrate absorption (positive-infinite)"); - } - - void test_integrate_absorption_negative_infinite() { - using std::exp; - using especia::sq; - - const double result = integrator.integrate_negative_infinite( - [](double x) -> double { return 1.0 - exp(-exp(-sq(x))); }); - - // - assert_equals(0.642572, result, 0.5E-06, "integrate absorption (negative-infinite)"); - } - - void test_integrate_absorption_infinite() { - using std::exp; - using especia::sq; - - const double result = integrator.integrate_infinite( - [](double x) -> double { return 1.0 - exp(-exp(-sq(x))); }); - - // - assert_equals(1.285145, result, 0.5E-06, "integrate absorption (infinite)"); - } - - void run_all() override { - run(this, &Integrator_Test::test_integrate_cos); - run(this, &Integrator_Test::test_integrate_sin); - run(this, &Integrator_Test::test_integrate_sin_sq); - run(this, &Integrator_Test::test_integrate_absorption); - run(this, &Integrator_Test::test_integrate_absorption_positive_infinite); - run(this, &Integrator_Test::test_integrate_absorption_negative_infinite); - run(this, &Integrator_Test::test_integrate_absorption_infinite); - } - - Integrator integrator; + void + test_integrate_cos () + { + using especia::pi; + using std::cos; + + const double result = integrator.integrate ( + [] (double x) -> double { return cos (x); }, 0.0, pi); + + assert_equals (0.0, result, 0.5E-06, "integrate cosine"); + } + + void + test_integrate_sin () + { + using especia::pi; + using std::sin; + + const double result = integrator.integrate ( + [] (double x) -> double { return sin (x); }, 0.0, pi); + + assert_equals (2.0, result, 0.5E-06, "integrate sine"); + } + + void + test_integrate_sin_sq () + { + using especia::pi; + using especia::sq; + using std::sin; + + const double result = integrator.integrate ( + [] (double x) -> double { return sq (sin (x)); }, 0.0, 2.0 * pi); + + assert_equals (pi, result, 0.5E-06, "integrate sine squared"); + } + + void + test_integrate_absorption () + { + using especia::sq; + using std::exp; + + const double result = integrator.integrate ( + [] (double x) -> double { return 1.0 - exp (-exp (-sq (x))); }, 0.0, + 4.0); + + // + assert_equals (0.642572, result, 0.5E-06, "integrate absorption"); + } + + void + test_integrate_absorption_positive_infinite () + { + using especia::sq; + using std::exp; + + const double result = integrator.integrate_positive_infinite ( + [] (double x) -> double { return 1.0 - exp (-exp (-sq (x))); }); + + // + assert_equals (0.642572, result, 0.5E-06, + "integrate absorption (positive-infinite)"); + } + + void + test_integrate_absorption_negative_infinite () + { + using especia::sq; + using std::exp; + + const double result = integrator.integrate_negative_infinite ( + [] (double x) -> double { return 1.0 - exp (-exp (-sq (x))); }); + + // + assert_equals (0.642572, result, 0.5E-06, + "integrate absorption (negative-infinite)"); + } + + void + test_integrate_absorption_infinite () + { + using especia::sq; + using std::exp; + + const double result = integrator.integrate_infinite ( + [] (double x) -> double { return 1.0 - exp (-exp (-sq (x))); }); + + // + assert_equals (1.285145, result, 0.5E-06, + "integrate absorption (infinite)"); + } + + void + run_all () override + { + run (this, &Integrator_Test::test_integrate_cos); + run (this, &Integrator_Test::test_integrate_sin); + run (this, &Integrator_Test::test_integrate_sin_sq); + run (this, &Integrator_Test::test_integrate_absorption); + run (this, &Integrator_Test::test_integrate_absorption_positive_infinite); + run (this, &Integrator_Test::test_integrate_absorption_negative_infinite); + run (this, &Integrator_Test::test_integrate_absorption_infinite); + } + + Integrator integrator; }; - -int main() { - return Integrator_Test().run_testsuite(); +int +main () +{ + return Integrator_Test ().run_testsuite (); } diff --git a/src/test/cxx/core/optimizer_test.cxx b/src/test/cxx/core/optimizer_test.cxx index 88e2b7d..397b87b 100644 --- a/src/test/cxx/core/optimizer_test.cxx +++ b/src/test/cxx/core/optimizer_test.cxx @@ -8,236 +8,335 @@ #include "../unittest.h" using especia::natural; -using especia::real; using especia::Optimizer; +using especia::real; -class Optimizer_Test : public Unit_Test { +class Optimizer_Test : public Unit_Test +{ private: - - static real sphere(const real x[], natural n) { - using especia::sq; - - auto y = real(0); - - for (natural i = 0; i < n; ++i) { - y += sq(x[i]); - } - - return y; - } - - static real ellipsoid(const real x[], natural n) { - using especia::sq; - - auto y = real(0); - - for (natural i = 0; i < n; ++i) { - y += std::pow(real(1.0E+06), real(i) / real(n - 1)) * sq(x[i]); - } - - return y; - } - - static real cigar(const real x[], natural n) { - using especia::sq; - - auto y = real(0); - - for (natural i = 1; i < n; ++i) { - y += sq(x[i]); - } - - return real(1.0E+06) * y + sq(x[0]); - } - - static real tablet(const real x[], natural n) { - using especia::sq; - - auto y = real(0); - - for (natural i = 1; i < n; ++i) { - y += sq(x[i]); - } - - return real(1.0E+06) * sq(x[0]) + y; - } - - /// [The Rosenbrock function](https://en.wikipedia.org/wiki/Rosenbrock_function) - static real rosenbrock(const real x[], natural n) { - using especia::sq; - - auto y = real(0); - - for (natural i = 0; i < n - 1; ++i) { - y += real(100) * sq(x[i + 1] - sq(x[i])) + sq(real(1) - x[i]); - } - - return y; - } - - static real different_powers(const real x[], natural n) { - using std::abs; - using std::pow; - - auto y = real(0); - - for (natural i = 0; i < n - 1; ++i) { - y += pow(abs(x[i]), real(2) + real(10 * i) / real(n - 1)); - } - - return y; - } - - void before() override { - builder.with_problem_dimension(10). - with_stop_generation(400). - with_accuracy_goal(real(1.0E-06)). - with_random_seed(31415); - } - - void after() override { - builder.with_defaults(); - } - - void test_minimize_sphere() { - const valarray x(real(1), 10); - const valarray d(real(1), 10); - const auto s = real(1); - - const Optimizer optimizer = builder.build(); - const Optimizer::Result result = optimizer.minimize(sphere, x, d, s); - - assert_true(result.is_optimized(), "test minimize sphere (optimized)"); - assert_false(result.is_underflow(), "test minimize sphere (underflow)"); - assert_equals(real(0), result.get_fitness(), real(1.0E-10), "test minimize sphere (fitness)"); - assert_equals(real(0), result.get_parameter_values()[0], real(1.0E-06), "test minimize sphere (0)"); - assert_equals(real(0), result.get_parameter_values()[1], real(1.0E-06), "test minimize sphere (1)"); - assert_equals(real(0), result.get_parameter_values()[2], real(1.0E-06), "test minimize sphere (2)"); - assert_equals(real(0), result.get_parameter_values()[3], real(1.0E-06), "test minimize sphere (3)"); - assert_equals(real(0), result.get_parameter_values()[4], real(1.0E-06), "test minimize sphere (4)"); - assert_equals(real(0), result.get_parameter_values()[5], real(1.0E-06), "test minimize sphere (5)"); - assert_equals(real(0), result.get_parameter_values()[6], real(1.0E-06), "test minimize sphere (6)"); - assert_equals(real(0), result.get_parameter_values()[7], real(1.0E-06), "test minimize sphere (7)"); - assert_equals(real(0), result.get_parameter_values()[8], real(1.0E-06), "test minimize sphere (8)"); - assert_equals(real(0), result.get_parameter_values()[9], real(1.0E-06), "test minimize sphere (9)"); - } - - void test_minimize_ellipsoid() { - const valarray x(real(1), 10); - const valarray d(real(1), 10); - const auto s = real(1); - - const Optimizer optimizer = builder.build(); - const Optimizer::Result result = optimizer.minimize(ellipsoid, x, d, s); - - assert_true(result.is_optimized(), "test minimize ellipsoid (optimized)"); - assert_false(result.is_underflow(), "test minimize ellipsoid (underflow)"); - assert_equals(real(0), result.get_fitness(), real(1.0E-10), "test minimize ellipsoid (fitness)"); - assert_equals(real(0), result.get_parameter_values()[0], real(1.0E-06), "test minimize ellipsoid (0)"); - assert_equals(real(0), result.get_parameter_values()[1], real(1.0E-06), "test minimize ellipsoid (1)"); - assert_equals(real(0), result.get_parameter_values()[2], real(1.0E-06), "test minimize ellipsoid (2)"); - assert_equals(real(0), result.get_parameter_values()[3], real(1.0E-06), "test minimize ellipsoid (3)"); - assert_equals(real(0), result.get_parameter_values()[4], real(1.0E-06), "test minimize ellipsoid (4)"); - assert_equals(real(0), result.get_parameter_values()[5], real(1.0E-06), "test minimize ellipsoid (5)"); - assert_equals(real(0), result.get_parameter_values()[6], real(1.0E-06), "test minimize ellipsoid (6)"); - assert_equals(real(0), result.get_parameter_values()[7], real(1.0E-06), "test minimize ellipsoid (7)"); - assert_equals(real(0), result.get_parameter_values()[8], real(1.0E-06), "test minimize ellipsoid (8)"); - assert_equals(real(0), result.get_parameter_values()[9], real(1.0E-06), "test minimize ellipsoid (9)"); - } - - void test_minimize_cigar() { - const valarray x(real(1), 10); - const valarray d(real(1), 10); - const auto s = real(1); - - const Optimizer optimizer = builder.build(); - const Optimizer::Result result = optimizer.minimize(cigar, x, d, s); - - assert_true(result.is_optimized(), "test minimize cigar (optimized)"); - assert_false(result.is_underflow(), "test minimize cigar (underflow)"); - assert_equals(real(0), result.get_fitness(), real(1.0E-10), "test minimize cigar (fitness)"); - assert_equals(real(0), result.get_parameter_values()[0], real(1.0E-06), "test minimize cigar (0)"); - assert_equals(real(0), result.get_parameter_values()[1], real(1.0E-06), "test minimize cigar (1)"); - assert_equals(real(0), result.get_parameter_values()[2], real(1.0E-06), "test minimize cigar (2)"); - assert_equals(real(0), result.get_parameter_values()[3], real(1.0E-06), "test minimize cigar (3)"); - assert_equals(real(0), result.get_parameter_values()[4], real(1.0E-06), "test minimize cigar (4)"); - assert_equals(real(0), result.get_parameter_values()[5], real(1.0E-06), "test minimize cigar (5)"); - assert_equals(real(0), result.get_parameter_values()[6], real(1.0E-06), "test minimize cigar (6)"); - assert_equals(real(0), result.get_parameter_values()[7], real(1.0E-06), "test minimize cigar (7)"); - assert_equals(real(0), result.get_parameter_values()[8], real(1.0E-06), "test minimize cigar (8)"); - assert_equals(real(0), result.get_parameter_values()[9], real(1.0E-06), "test minimize cigar (9)"); - } - - void test_minimize_tablet() { - const valarray x(real(1), 10); - const valarray d(real(1), 10); - const auto s = real(1); - - const Optimizer optimizer = builder.build(); - const Optimizer::Result result = optimizer.minimize(tablet, x, d, s); - - assert_true(result.is_optimized(), "test minimize tablet (optimized)"); - assert_false(result.is_underflow(), "test minimize tablet (underflow)"); - assert_equals(real(0), result.get_fitness(), real(1.0E-10), "test minimize tablet (fitness)"); - assert_equals(real(0), result.get_parameter_values()[0], real(1.0E-06), "test minimize tablet (0)"); - assert_equals(real(0), result.get_parameter_values()[1], real(1.0E-06), "test minimize tablet (1)"); - assert_equals(real(0), result.get_parameter_values()[2], real(1.0E-06), "test minimize tablet (2)"); - assert_equals(real(0), result.get_parameter_values()[3], real(1.0E-06), "test minimize tablet (3)"); - assert_equals(real(0), result.get_parameter_values()[4], real(1.0E-06), "test minimize tablet (4)"); - assert_equals(real(0), result.get_parameter_values()[5], real(1.0E-06), "test minimize tablet (5)"); - assert_equals(real(0), result.get_parameter_values()[6], real(1.0E-06), "test minimize tablet (6)"); - assert_equals(real(0), result.get_parameter_values()[7], real(1.0E-06), "test minimize tablet (7)"); - assert_equals(real(0), result.get_parameter_values()[8], real(1.0E-06), "test minimize tablet (8)"); - assert_equals(real(0), result.get_parameter_values()[9], real(1.0E-06), "test minimize tablet (9)"); - } - - void test_minimize_rosenbrock() { - const valarray x(real(0), 10); - const valarray d(real(1), 10); - const auto s = real(0.1); - - const Optimizer optimizer = builder.build(); - const Optimizer::Result result = optimizer.minimize(rosenbrock, x, d, s); - - assert_true(result.is_optimized(), "test minimize Rosenbrock (optimized)"); - assert_false(result.is_underflow(), "test minimize Rosenbrock (underflow)"); - assert_equals(real(0), result.get_fitness(), real(1.0E-10), "test minimize Rosenbrock (fitness)"); - assert_equals(real(1), result.get_parameter_values()[0], real(1.0E-06), "test minimize Rosenbrock (0)"); - assert_equals(real(1), result.get_parameter_values()[1], real(1.0E-06), "test minimize Rosenbrock (1)"); - assert_equals(real(1), result.get_parameter_values()[2], real(1.0E-06), "test minimize Rosenbrock (2)"); - assert_equals(real(1), result.get_parameter_values()[3], real(1.0E-06), "test minimize Rosenbrock (3)"); - assert_equals(real(1), result.get_parameter_values()[4], real(1.0E-06), "test minimize Rosenbrock (4)"); - assert_equals(real(1), result.get_parameter_values()[5], real(1.0E-06), "test minimize Rosenbrock (5)"); - assert_equals(real(1), result.get_parameter_values()[6], real(1.0E-06), "test minimize Rosenbrock (6)"); - assert_equals(real(1), result.get_parameter_values()[7], real(1.0E-06), "test minimize Rosenbrock (7)"); - assert_equals(real(1), result.get_parameter_values()[8], real(1.0E-06), "test minimize Rosenbrock (8)"); - assert_equals(real(1), result.get_parameter_values()[9], real(1.0E-06), "test minimize Rosenbrock (9)"); - } - - void test_minimize_different_powers() { - const valarray x(real(1), 10); - const valarray d(real(1), 10); - const auto s = real(1); - - const Optimizer optimizer = builder.build(); - const Optimizer::Result result = optimizer.minimize(different_powers, x, d, s); - - assert_equals(real(0), result.get_fitness(), real(1.0E-16), "test minimize different powers"); - } - - void run_all() override { - run(this, &Optimizer_Test::test_minimize_sphere); - run(this, &Optimizer_Test::test_minimize_ellipsoid); - run(this, &Optimizer_Test::test_minimize_cigar); - run(this, &Optimizer_Test::test_minimize_tablet); - run(this, &Optimizer_Test::test_minimize_rosenbrock); - run(this, &Optimizer_Test::test_minimize_different_powers); - } - - Optimizer::Builder builder; + static real + sphere (const real x[], natural n) + { + using especia::sq; + + auto y = real (0); + + for (natural i = 0; i < n; ++i) + { + y += sq (x[i]); + } + + return y; + } + + static real + ellipsoid (const real x[], natural n) + { + using especia::sq; + + auto y = real (0); + + for (natural i = 0; i < n; ++i) + { + y += std::pow (real (1.0E+06), real (i) / real (n - 1)) * sq (x[i]); + } + + return y; + } + + static real + cigar (const real x[], natural n) + { + using especia::sq; + + auto y = real (0); + + for (natural i = 1; i < n; ++i) + { + y += sq (x[i]); + } + + return real (1.0E+06) * y + sq (x[0]); + } + + static real + tablet (const real x[], natural n) + { + using especia::sq; + + auto y = real (0); + + for (natural i = 1; i < n; ++i) + { + y += sq (x[i]); + } + + return real (1.0E+06) * sq (x[0]) + y; + } + + /// [The Rosenbrock + /// function](https://en.wikipedia.org/wiki/Rosenbrock_function) + static real + rosenbrock (const real x[], natural n) + { + using especia::sq; + + auto y = real (0); + + for (natural i = 0; i < n - 1; ++i) + { + y += real (100) * sq (x[i + 1] - sq (x[i])) + sq (real (1) - x[i]); + } + + return y; + } + + static real + different_powers (const real x[], natural n) + { + using std::abs; + using std::pow; + + auto y = real (0); + + for (natural i = 0; i < n - 1; ++i) + { + y += pow (abs (x[i]), real (2) + real (10 * i) / real (n - 1)); + } + + return y; + } + + void + before () override + { + builder.with_problem_dimension (10) + .with_stop_generation (400) + .with_accuracy_goal (real (1.0E-06)) + .with_random_seed (31415); + } + + void + after () override + { + builder.with_defaults (); + } + + void + test_minimize_sphere () + { + const valarray x (real (1), 10); + const valarray d (real (1), 10); + const auto s = real (1); + + const Optimizer optimizer = builder.build (); + const Optimizer::Result result = optimizer.minimize (sphere, x, d, s); + + assert_true (result.is_optimized (), "test minimize sphere (optimized)"); + assert_false (result.is_underflow (), "test minimize sphere (underflow)"); + assert_equals (real (0), result.get_fitness (), real (1.0E-10), + "test minimize sphere (fitness)"); + assert_equals (real (0), result.get_parameter_values ()[0], real (1.0E-06), + "test minimize sphere (0)"); + assert_equals (real (0), result.get_parameter_values ()[1], real (1.0E-06), + "test minimize sphere (1)"); + assert_equals (real (0), result.get_parameter_values ()[2], real (1.0E-06), + "test minimize sphere (2)"); + assert_equals (real (0), result.get_parameter_values ()[3], real (1.0E-06), + "test minimize sphere (3)"); + assert_equals (real (0), result.get_parameter_values ()[4], real (1.0E-06), + "test minimize sphere (4)"); + assert_equals (real (0), result.get_parameter_values ()[5], real (1.0E-06), + "test minimize sphere (5)"); + assert_equals (real (0), result.get_parameter_values ()[6], real (1.0E-06), + "test minimize sphere (6)"); + assert_equals (real (0), result.get_parameter_values ()[7], real (1.0E-06), + "test minimize sphere (7)"); + assert_equals (real (0), result.get_parameter_values ()[8], real (1.0E-06), + "test minimize sphere (8)"); + assert_equals (real (0), result.get_parameter_values ()[9], real (1.0E-06), + "test minimize sphere (9)"); + } + + void + test_minimize_ellipsoid () + { + const valarray x (real (1), 10); + const valarray d (real (1), 10); + const auto s = real (1); + + const Optimizer optimizer = builder.build (); + const Optimizer::Result result = optimizer.minimize (ellipsoid, x, d, s); + + assert_true (result.is_optimized (), + "test minimize ellipsoid (optimized)"); + assert_false (result.is_underflow (), + "test minimize ellipsoid (underflow)"); + assert_equals (real (0), result.get_fitness (), real (1.0E-10), + "test minimize ellipsoid (fitness)"); + assert_equals (real (0), result.get_parameter_values ()[0], real (1.0E-06), + "test minimize ellipsoid (0)"); + assert_equals (real (0), result.get_parameter_values ()[1], real (1.0E-06), + "test minimize ellipsoid (1)"); + assert_equals (real (0), result.get_parameter_values ()[2], real (1.0E-06), + "test minimize ellipsoid (2)"); + assert_equals (real (0), result.get_parameter_values ()[3], real (1.0E-06), + "test minimize ellipsoid (3)"); + assert_equals (real (0), result.get_parameter_values ()[4], real (1.0E-06), + "test minimize ellipsoid (4)"); + assert_equals (real (0), result.get_parameter_values ()[5], real (1.0E-06), + "test minimize ellipsoid (5)"); + assert_equals (real (0), result.get_parameter_values ()[6], real (1.0E-06), + "test minimize ellipsoid (6)"); + assert_equals (real (0), result.get_parameter_values ()[7], real (1.0E-06), + "test minimize ellipsoid (7)"); + assert_equals (real (0), result.get_parameter_values ()[8], real (1.0E-06), + "test minimize ellipsoid (8)"); + assert_equals (real (0), result.get_parameter_values ()[9], real (1.0E-06), + "test minimize ellipsoid (9)"); + } + + void + test_minimize_cigar () + { + const valarray x (real (1), 10); + const valarray d (real (1), 10); + const auto s = real (1); + + const Optimizer optimizer = builder.build (); + const Optimizer::Result result = optimizer.minimize (cigar, x, d, s); + + assert_true (result.is_optimized (), "test minimize cigar (optimized)"); + assert_false (result.is_underflow (), "test minimize cigar (underflow)"); + assert_equals (real (0), result.get_fitness (), real (1.0E-10), + "test minimize cigar (fitness)"); + assert_equals (real (0), result.get_parameter_values ()[0], real (1.0E-06), + "test minimize cigar (0)"); + assert_equals (real (0), result.get_parameter_values ()[1], real (1.0E-06), + "test minimize cigar (1)"); + assert_equals (real (0), result.get_parameter_values ()[2], real (1.0E-06), + "test minimize cigar (2)"); + assert_equals (real (0), result.get_parameter_values ()[3], real (1.0E-06), + "test minimize cigar (3)"); + assert_equals (real (0), result.get_parameter_values ()[4], real (1.0E-06), + "test minimize cigar (4)"); + assert_equals (real (0), result.get_parameter_values ()[5], real (1.0E-06), + "test minimize cigar (5)"); + assert_equals (real (0), result.get_parameter_values ()[6], real (1.0E-06), + "test minimize cigar (6)"); + assert_equals (real (0), result.get_parameter_values ()[7], real (1.0E-06), + "test minimize cigar (7)"); + assert_equals (real (0), result.get_parameter_values ()[8], real (1.0E-06), + "test minimize cigar (8)"); + assert_equals (real (0), result.get_parameter_values ()[9], real (1.0E-06), + "test minimize cigar (9)"); + } + + void + test_minimize_tablet () + { + const valarray x (real (1), 10); + const valarray d (real (1), 10); + const auto s = real (1); + + const Optimizer optimizer = builder.build (); + const Optimizer::Result result = optimizer.minimize (tablet, x, d, s); + + assert_true (result.is_optimized (), "test minimize tablet (optimized)"); + assert_false (result.is_underflow (), "test minimize tablet (underflow)"); + assert_equals (real (0), result.get_fitness (), real (1.0E-10), + "test minimize tablet (fitness)"); + assert_equals (real (0), result.get_parameter_values ()[0], real (1.0E-06), + "test minimize tablet (0)"); + assert_equals (real (0), result.get_parameter_values ()[1], real (1.0E-06), + "test minimize tablet (1)"); + assert_equals (real (0), result.get_parameter_values ()[2], real (1.0E-06), + "test minimize tablet (2)"); + assert_equals (real (0), result.get_parameter_values ()[3], real (1.0E-06), + "test minimize tablet (3)"); + assert_equals (real (0), result.get_parameter_values ()[4], real (1.0E-06), + "test minimize tablet (4)"); + assert_equals (real (0), result.get_parameter_values ()[5], real (1.0E-06), + "test minimize tablet (5)"); + assert_equals (real (0), result.get_parameter_values ()[6], real (1.0E-06), + "test minimize tablet (6)"); + assert_equals (real (0), result.get_parameter_values ()[7], real (1.0E-06), + "test minimize tablet (7)"); + assert_equals (real (0), result.get_parameter_values ()[8], real (1.0E-06), + "test minimize tablet (8)"); + assert_equals (real (0), result.get_parameter_values ()[9], real (1.0E-06), + "test minimize tablet (9)"); + } + + void + test_minimize_rosenbrock () + { + const valarray x (real (0), 10); + const valarray d (real (1), 10); + const auto s = real (0.1); + + const Optimizer optimizer = builder.build (); + const Optimizer::Result result = optimizer.minimize (rosenbrock, x, d, s); + + assert_true (result.is_optimized (), + "test minimize Rosenbrock (optimized)"); + assert_false (result.is_underflow (), + "test minimize Rosenbrock (underflow)"); + assert_equals (real (0), result.get_fitness (), real (1.0E-10), + "test minimize Rosenbrock (fitness)"); + assert_equals (real (1), result.get_parameter_values ()[0], real (1.0E-06), + "test minimize Rosenbrock (0)"); + assert_equals (real (1), result.get_parameter_values ()[1], real (1.0E-06), + "test minimize Rosenbrock (1)"); + assert_equals (real (1), result.get_parameter_values ()[2], real (1.0E-06), + "test minimize Rosenbrock (2)"); + assert_equals (real (1), result.get_parameter_values ()[3], real (1.0E-06), + "test minimize Rosenbrock (3)"); + assert_equals (real (1), result.get_parameter_values ()[4], real (1.0E-06), + "test minimize Rosenbrock (4)"); + assert_equals (real (1), result.get_parameter_values ()[5], real (1.0E-06), + "test minimize Rosenbrock (5)"); + assert_equals (real (1), result.get_parameter_values ()[6], real (1.0E-06), + "test minimize Rosenbrock (6)"); + assert_equals (real (1), result.get_parameter_values ()[7], real (1.0E-06), + "test minimize Rosenbrock (7)"); + assert_equals (real (1), result.get_parameter_values ()[8], real (1.0E-06), + "test minimize Rosenbrock (8)"); + assert_equals (real (1), result.get_parameter_values ()[9], real (1.0E-06), + "test minimize Rosenbrock (9)"); + } + + void + test_minimize_different_powers () + { + const valarray x (real (1), 10); + const valarray d (real (1), 10); + const auto s = real (1); + + const Optimizer optimizer = builder.build (); + const Optimizer::Result result + = optimizer.minimize (different_powers, x, d, s); + + assert_equals (real (0), result.get_fitness (), real (1.0E-16), + "test minimize different powers"); + } + + void + run_all () override + { + run (this, &Optimizer_Test::test_minimize_sphere); + run (this, &Optimizer_Test::test_minimize_ellipsoid); + run (this, &Optimizer_Test::test_minimize_cigar); + run (this, &Optimizer_Test::test_minimize_tablet); + run (this, &Optimizer_Test::test_minimize_rosenbrock); + run (this, &Optimizer_Test::test_minimize_different_powers); + } + + Optimizer::Builder builder; }; - -int main() { - return Optimizer_Test().run_testsuite(); +int +main () +{ + return Optimizer_Test ().run_testsuite (); } diff --git a/src/test/cxx/core/profiles_test.cxx b/src/test/cxx/core/profiles_test.cxx index 043a17f..970e069 100644 --- a/src/test/cxx/core/profiles_test.cxx +++ b/src/test/cxx/core/profiles_test.cxx @@ -8,89 +8,115 @@ #include "../../../main/cxx/core/profiles.h" #include "../unittest.h" -using especia::real; using especia::Equivalent_Width_Calculator; using especia::Integrator; +using especia::real; - -class Profiles_Test : public Unit_Test { +class Profiles_Test : public Unit_Test +{ private: - - void test_equivalent_width_intergalactic_doppler() { - using especia::Intergalactic_Doppler; - - const real w = calculator.calculate(Intergalactic_Doppler()); - - // - assert_equals(real(0.698785), w, real(1.0E-06), "equivalent width (intergalactic Doppler)"); - } - - void test_equivalent_width_many_multiplet() { - using especia::Many_Multiplet; - - const real w = calculator.calculate(Many_Multiplet()); - - // - assert_equals(real(0.698785), w, real(1.0E-06), "equivalent width (many-multiplet)"); - } - - void test_equivalent_width_intergalactic_voigt() { - using especia::Intergalactic_Voigt; - using especia::Pseudo_Voigt; - - const real w = calculator.calculate(Intergalactic_Voigt()); - - // - assert_equals(real(0.881143), w, real(5.0E-03), "equivalent width (intergalactic Voigt)"); - } - - void test_equivalent_width_intergalactic_voigt_extended() { - using especia::Intergalactic_Voigt; - using especia::Extended_Pseudo_Voigt; - - const real w = calculator.calculate(Intergalactic_Voigt()); - - // - assert_equals(real(0.881143), w, real(5.0E-03), "equivalent width (intergalactic Voigt, extended)"); - } - - void test_maximum_pseudo_voigt() { - using especia::Pseudo_Voigt; - - // - assert_equals(real(0.482476), Pseudo_Voigt(0.5, 0.5)(0.0), real(1.0E-03), - "Voigt function maximum (pseudo-Voigt approximation)"); - - // - assert_equals(real(0.241238), Pseudo_Voigt(1.0, 1.0)(0.0), real(1.0E-03), - "Voigt function maximum (pseudo-Voigt approximation)"); - } - - void test_maximum_pseudo_voigt_extended() { - using especia::Extended_Pseudo_Voigt; - - // - assert_equals(real(0.482476), Extended_Pseudo_Voigt(0.5, 0.5)(0.0), real(5.0E-04), - "Voigt function maximum (extended pseudo-Voigt approximation)"); - - // - assert_equals(real(0.241238), Extended_Pseudo_Voigt(1.0, 1.0)(0.0), real(5.0E-04), - "Voigt function maximum (extended pseudo-Voigt approximation)"); - } - - void run_all() override { - run(this, &Profiles_Test::test_equivalent_width_intergalactic_doppler); - run(this, &Profiles_Test::test_equivalent_width_many_multiplet); - run(this, &Profiles_Test::test_equivalent_width_intergalactic_voigt); - run(this, &Profiles_Test::test_equivalent_width_intergalactic_voigt_extended); - run(this, &Profiles_Test::test_maximum_pseudo_voigt); - run(this, &Profiles_Test::test_maximum_pseudo_voigt_extended); - } - - Equivalent_Width_Calculator> calculator; + void + test_equivalent_width_intergalactic_doppler () + { + using especia::Intergalactic_Doppler; + + const real w = calculator.calculate (Intergalactic_Doppler ()); + + // + assert_equals (real (0.698785), w, real (1.0E-06), + "equivalent width (intergalactic Doppler)"); + } + + void + test_equivalent_width_many_multiplet () + { + using especia::Many_Multiplet; + + const real w = calculator.calculate (Many_Multiplet ()); + + // + assert_equals (real (0.698785), w, real (1.0E-06), + "equivalent width (many-multiplet)"); + } + + void + test_equivalent_width_intergalactic_voigt () + { + using especia::Intergalactic_Voigt; + using especia::Pseudo_Voigt; + + const real w = calculator.calculate (Intergalactic_Voigt ()); + + // + assert_equals (real (0.881143), w, real (5.0E-03), + "equivalent width (intergalactic Voigt)"); + } + + void + test_equivalent_width_intergalactic_voigt_extended () + { + using especia::Extended_Pseudo_Voigt; + using especia::Intergalactic_Voigt; + + const real w + = calculator.calculate (Intergalactic_Voigt ()); + + // + assert_equals (real (0.881143), w, real (5.0E-03), + "equivalent width (intergalactic Voigt, extended)"); + } + + void + test_maximum_pseudo_voigt () + { + using especia::Pseudo_Voigt; + + // + assert_equals (real (0.482476), Pseudo_Voigt (0.5, 0.5) (0.0), + real (1.0E-03), + "Voigt function maximum (pseudo-Voigt approximation)"); + + // + assert_equals (real (0.241238), Pseudo_Voigt (1.0, 1.0) (0.0), + real (1.0E-03), + "Voigt function maximum (pseudo-Voigt approximation)"); + } + + void + test_maximum_pseudo_voigt_extended () + { + using especia::Extended_Pseudo_Voigt; + + // + assert_equals ( + real (0.482476), Extended_Pseudo_Voigt (0.5, 0.5) (0.0), + real (5.0E-04), + "Voigt function maximum (extended pseudo-Voigt approximation)"); + + // + assert_equals ( + real (0.241238), Extended_Pseudo_Voigt (1.0, 1.0) (0.0), + real (5.0E-04), + "Voigt function maximum (extended pseudo-Voigt approximation)"); + } + + void + run_all () override + { + run (this, &Profiles_Test::test_equivalent_width_intergalactic_doppler); + run (this, &Profiles_Test::test_equivalent_width_many_multiplet); + run (this, &Profiles_Test::test_equivalent_width_intergalactic_voigt); + run (this, + &Profiles_Test::test_equivalent_width_intergalactic_voigt_extended); + run (this, &Profiles_Test::test_maximum_pseudo_voigt); + run (this, &Profiles_Test::test_maximum_pseudo_voigt_extended); + } + + Equivalent_Width_Calculator > calculator; }; - -int main() { - return Profiles_Test().run_testsuite(); +int +main () +{ + return Profiles_Test ().run_testsuite (); } diff --git a/src/test/cxx/core/random_test.cxx b/src/test/cxx/core/random_test.cxx index cbc84f5..4db7f62 100644 --- a/src/test/cxx/core/random_test.cxx +++ b/src/test/cxx/core/random_test.cxx @@ -11,79 +11,100 @@ using especia::Mt19937_32; using especia::Mt19937_64; using especia::Pcg_32; -class Rng_Test : public Unit_Test { +class Rng_Test : public Unit_Test +{ private: - - void test_melg19937_64() { - using especia::natural; - using especia::word64; - - const word64 seeds[] = {0x12345ULL, 0x23456ULL, 0x34567ULL, 0x45678ULL}; - const Melg19937_64 melg(4, seeds); - - assert_equals(16675511042081433281ULL, melg.rand(), "test MELG-19937-64 (0)"); - assert_equals(8489326016911908102ULL, melg.rand(), "test MELG-19937-64 (1)"); - assert_equals(16071362722047509693ULL, melg.rand(), "test MELG-19937-64 (2)"); - assert_equals(11631833934008589069ULL, melg.rand(), "test MELG-19937-64 (3)"); - assert_equals(3308423691540511443ULL, melg.rand(), "test MELG-19937-64 (4)"); - assert_equals(12463994900921303743ULL, melg.rand(), "test MELG-19937-64 (5)"); - - for (especia::natural i = 6; i < 999; i++) { - melg.rand(); - } - - assert_equals(13711744326396256691ULL, melg.rand(), "test MELG-19937-64 (6)"); - } - - void test_mt19937_32() { - using especia::word64; - - const word64 seeds[] = {0x123ULL, 0x234ULL, 0x345ULL, 0x456ULL}; - const Mt19937_32 mt(4, seeds); - - assert_equals(1067595299ULL, mt.rand(), "test MT-19937-32 (0)"); - assert_equals(955945823ULL, mt.rand(), "test MT-19937-32 (1)"); - assert_equals(477289528ULL, mt.rand(), "test MT-19937-32 (2)"); - assert_equals(4107218783ULL, mt.rand(), "test MT-19937-32 (3)"); - assert_equals(4228976476ULL, mt.rand(), "test MT-19937-32 (4)"); - assert_equals(3344332714ULL, mt.rand(), "test MT-19937-32 (5)"); - } - - void test_mt19937_64() { - using especia::word64; - - const word64 seeds[] = {0x12345ULL, 0x23456ULL, 0x34567ULL, 0x45678ULL}; - const Mt19937_64 mt(4, seeds); - - assert_equals(7266447313870364031ULL, mt.rand(), "test MT-19937-64 (0)"); - assert_equals(4946485549665804864ULL, mt.rand(), "test MT-19937-64 (1)"); - assert_equals(16945909448695747420ULL, mt.rand(), "test MT-19937-64 (2)"); - assert_equals(16394063075524226720ULL, mt.rand(), "test MT-19937-64 (3)"); - assert_equals(4873882236456199058ULL, mt.rand(), "test MT-19937-64 (4)"); - assert_equals(14877448043947020171ULL, mt.rand(), "test MT-19937-64 (5)"); - } - - void test_pcg() { - const Pcg_32 pcg(42ULL, 54ULL); - - assert_equals(0xa15c02b7ul, pcg.rand(), "test PCG-XSH-RR-64-32 (0)"); - assert_equals(0x7b47f409ul, pcg.rand(), "test PCG-XSH-RR-64-32 (1)"); - assert_equals(0xba1d3330ul, pcg.rand(), "test PCG-XSH-RR-64-32 (2)"); - assert_equals(0x83d2f293ul, pcg.rand(), "test PCG-XSH-RR-64-32 (3)"); - assert_equals(0xbfa4784bul, pcg.rand(), "test PCG-XSH-RR-64-32 (4)"); - assert_equals(0xcbed606eul, pcg.rand(), "test PCG-XSH-RR-64-32 (5)"); - } - - - void run_all() override { - run(this, &Rng_Test::test_melg19937_64); - run(this, &Rng_Test::test_mt19937_32); - run(this, &Rng_Test::test_mt19937_64); - run(this, &Rng_Test::test_pcg); - } + void + test_melg19937_64 () + { + using especia::natural; + using especia::word64; + + const word64 seeds[] = { 0x12345ULL, 0x23456ULL, 0x34567ULL, 0x45678ULL }; + const Melg19937_64 melg (4, seeds); + + assert_equals (16675511042081433281ULL, melg.rand (), + "test MELG-19937-64 (0)"); + assert_equals (8489326016911908102ULL, melg.rand (), + "test MELG-19937-64 (1)"); + assert_equals (16071362722047509693ULL, melg.rand (), + "test MELG-19937-64 (2)"); + assert_equals (11631833934008589069ULL, melg.rand (), + "test MELG-19937-64 (3)"); + assert_equals (3308423691540511443ULL, melg.rand (), + "test MELG-19937-64 (4)"); + assert_equals (12463994900921303743ULL, melg.rand (), + "test MELG-19937-64 (5)"); + + for (especia::natural i = 6; i < 999; i++) + { + melg.rand (); + } + + assert_equals (13711744326396256691ULL, melg.rand (), + "test MELG-19937-64 (6)"); + } + + void + test_mt19937_32 () + { + using especia::word64; + + const word64 seeds[] = { 0x123ULL, 0x234ULL, 0x345ULL, 0x456ULL }; + const Mt19937_32 mt (4, seeds); + + assert_equals (1067595299ULL, mt.rand (), "test MT-19937-32 (0)"); + assert_equals (955945823ULL, mt.rand (), "test MT-19937-32 (1)"); + assert_equals (477289528ULL, mt.rand (), "test MT-19937-32 (2)"); + assert_equals (4107218783ULL, mt.rand (), "test MT-19937-32 (3)"); + assert_equals (4228976476ULL, mt.rand (), "test MT-19937-32 (4)"); + assert_equals (3344332714ULL, mt.rand (), "test MT-19937-32 (5)"); + } + + void + test_mt19937_64 () + { + using especia::word64; + + const word64 seeds[] = { 0x12345ULL, 0x23456ULL, 0x34567ULL, 0x45678ULL }; + const Mt19937_64 mt (4, seeds); + + assert_equals (7266447313870364031ULL, mt.rand (), "test MT-19937-64 (0)"); + assert_equals (4946485549665804864ULL, mt.rand (), "test MT-19937-64 (1)"); + assert_equals (16945909448695747420ULL, mt.rand (), + "test MT-19937-64 (2)"); + assert_equals (16394063075524226720ULL, mt.rand (), + "test MT-19937-64 (3)"); + assert_equals (4873882236456199058ULL, mt.rand (), "test MT-19937-64 (4)"); + assert_equals (14877448043947020171ULL, mt.rand (), + "test MT-19937-64 (5)"); + } + + void + test_pcg () + { + const Pcg_32 pcg (42ULL, 54ULL); + + assert_equals (0xa15c02b7ul, pcg.rand (), "test PCG-XSH-RR-64-32 (0)"); + assert_equals (0x7b47f409ul, pcg.rand (), "test PCG-XSH-RR-64-32 (1)"); + assert_equals (0xba1d3330ul, pcg.rand (), "test PCG-XSH-RR-64-32 (2)"); + assert_equals (0x83d2f293ul, pcg.rand (), "test PCG-XSH-RR-64-32 (3)"); + assert_equals (0xbfa4784bul, pcg.rand (), "test PCG-XSH-RR-64-32 (4)"); + assert_equals (0xcbed606eul, pcg.rand (), "test PCG-XSH-RR-64-32 (5)"); + } + + void + run_all () override + { + run (this, &Rng_Test::test_melg19937_64); + run (this, &Rng_Test::test_mt19937_32); + run (this, &Rng_Test::test_mt19937_64); + run (this, &Rng_Test::test_pcg); + } }; - -int main() { - return Rng_Test().run_testsuite(); +int +main () +{ + return Rng_Test ().run_testsuite (); } diff --git a/src/test/cxx/unittest.h b/src/test/cxx/unittest.h index 21efd38..1490b73 100644 --- a/src/test/cxx/unittest.h +++ b/src/test/cxx/unittest.h @@ -13,212 +13,254 @@ #include #include - /// The base class to be inherited by all unit-level tests. -class Unit_Test { +class Unit_Test +{ public: - /// The destructor. - virtual ~Unit_Test() = default; - - /// Runs the testsuite. - /// - /// @return an exit code. - int run_testsuite() { - using std::endl; - using std::exception; - - try { - before_all(); - run_all(); - after_all(); - return 0; - } catch (Assertion_Error &e) { - err << e.what() << endl; - return 1; - } catch (exception &e) { - err << e.what() << endl; - return 2; - } - } + /// The destructor. + virtual ~Unit_Test () = default; + + /// Runs the testsuite. + /// + /// @return an exit code. + int + run_testsuite () + { + using std::endl; + using std::exception; + + try + { + before_all (); + run_all (); + after_all (); + return 0; + } + catch (Assertion_Error &e) + { + err << e.what () << endl; + return 1; + } + catch (exception &e) + { + err << e.what () << endl; + return 2; + } + } protected: - /// The type of exception thrown when an assertion fails. - class Assertion_Error : public std::exception { - public: - /// The constructor. - /// - /// @param what A description of the failed assertion. - explicit Assertion_Error(std::string what) : exception(), what_happened(std::move(what)) { - } - - /// The destructor. - ~Assertion_Error() override = default; - - /// Returns a description of the failed assertion. - /// - /// @return the description of the failed assertion. - const char* what() const noexcept override { - return what_happened.c_str(); - } - - private: - /// The description of the failed assertion. - std::string what_happened; - }; - + /// The type of exception thrown when an assertion fails. + class Assertion_Error : public std::exception + { + public: /// The constructor. - Unit_Test() = default; - - /// Method called before any test case will be executed. - virtual void before_all() { - - } - - /// Method called after all test cases have been executed. - virtual void after_all() { - - } - - /// Method called before each test case. - virtual void before() { - - } - - /// Method called after each test case. - virtual void after() { - - } - - /// Runs all test cases. - virtual void run_all() = 0; - - /// Runs a test case. - /// - /// @tparam C The test class. - /// @tparam T The test case type. - /// - /// @param c A pointer to the test class. - /// @param t A pointer to the test case. - template - void run(C c, T t) { - before(); - (c->*t)(); - after(); - } - - /// Asserts equality of two values. - /// - /// @tparam E The expected type. - /// @tparam A The actual type. /// - /// @param expected The expected value. - /// @param actual The actual value. - /// @param name The assertion name. - /// @throw an @c Assertion_Error if requested. - template - void assert_equals(const E &expected, const A &actual, const std::string &name = "unnamed assertion") const { - using std::cerr; - using std::endl; - using std::stringstream; - - if (actual == expected) { // NaN safe - handle_passed(name); - } else { - stringstream what; - what << "Failed: " << name << "\n"; - what << " Expected result: " << expected << "\n"; - what << " Actual result: " << actual; - handle_failed(what.str()); - } + /// @param what A description of the failed assertion. + explicit Assertion_Error (std::string what) + : exception (), what_happened (std::move (what)) + { } - /// Asserts equality of two values with some (absolute) tolerance. - /// - /// @tparam E The expected type. - /// @tparam A The actual type. - /// - /// @param expected The expected value. - /// @param actual The actual value. - /// @param tolerance The (absolute) tolerance. - /// @param name The assertion name. - /// @throw an @c Assertion_Error if requested. - template - void assert_equals(const E &expected, const A &actual, const E &tolerance, - const std::string &name = "unnamed assertion") const { - using std::abs; - using std::stringstream; - - if (abs(actual - expected) <= tolerance) { // NaN safe - handle_passed(name); - } else { - stringstream what; - what << "Failed: " << name << "\n"; - what << " Expected result: " << expected << "\n"; - what << " Actual result: " << actual << "\n"; - what << " Expected tolerance: " << tolerance; - handle_failed(what.str()); - } - } + /// The destructor. + ~Assertion_Error () override = default; - /// Asserts @c false. + /// Returns a description of the failed assertion. /// - /// @param actual The actual value. - /// @param name The assertion name. - /// @throw an @c Assertion_Error if requested. - void assert_false(const bool actual, const std::string &name = "unnamed assertion") const { - assert_equals(false, actual, name); + /// @return the description of the failed assertion. + const char * + what () const noexcept override + { + return what_happened.c_str (); } - /// Asserts @c true. - /// - /// @param actual The actual value. - /// @param name The assertion name. - /// @throw an @c Assertion_Error if requested. - void assert_true(const bool actual, const std::string &name = "unnamed assertion") const { - assert_equals(true, actual, name); - } + private: + /// The description of the failed assertion. + std::string what_happened; + }; + + /// The constructor. + Unit_Test () = default; + + /// Method called before any test case will be executed. + virtual void + before_all () + { + } + + /// Method called after all test cases have been executed. + virtual void + after_all () + { + } + + /// Method called before each test case. + virtual void + before () + { + } + + /// Method called after each test case. + virtual void + after () + { + } + + /// Runs all test cases. + virtual void run_all () = 0; + + /// Runs a test case. + /// + /// @tparam C The test class. + /// @tparam T The test case type. + /// + /// @param c A pointer to the test class. + /// @param t A pointer to the test case. + template + void + run (C c, T t) + { + before (); + (c->*t) (); + after (); + } + + /// Asserts equality of two values. + /// + /// @tparam E The expected type. + /// @tparam A The actual type. + /// + /// @param expected The expected value. + /// @param actual The actual value. + /// @param name The assertion name. + /// @throw an @c Assertion_Error if requested. + template + void + assert_equals (const E &expected, const A &actual, + const std::string &name = "unnamed assertion") const + { + using std::cerr; + using std::endl; + using std::stringstream; + + if (actual == expected) + { // NaN safe + handle_passed (name); + } + else + { + stringstream what; + what << "Failed: " << name << "\n"; + what << " Expected result: " << expected << "\n"; + what << " Actual result: " << actual; + handle_failed (what.str ()); + } + } + + /// Asserts equality of two values with some (absolute) tolerance. + /// + /// @tparam E The expected type. + /// @tparam A The actual type. + /// + /// @param expected The expected value. + /// @param actual The actual value. + /// @param tolerance The (absolute) tolerance. + /// @param name The assertion name. + /// @throw an @c Assertion_Error if requested. + template + void + assert_equals (const E &expected, const A &actual, const E &tolerance, + const std::string &name = "unnamed assertion") const + { + using std::abs; + using std::stringstream; + + if (abs (actual - expected) <= tolerance) + { // NaN safe + handle_passed (name); + } + else + { + stringstream what; + what << "Failed: " << name << "\n"; + what << " Expected result: " << expected << "\n"; + what << " Actual result: " << actual << "\n"; + what << " Expected tolerance: " << tolerance; + handle_failed (what.str ()); + } + } + + /// Asserts @c false. + /// + /// @param actual The actual value. + /// @param name The assertion name. + /// @throw an @c Assertion_Error if requested. + void + assert_false (const bool actual, + const std::string &name = "unnamed assertion") const + { + assert_equals (false, actual, name); + } + + /// Asserts @c true. + /// + /// @param actual The actual value. + /// @param name The assertion name. + /// @throw an @c Assertion_Error if requested. + void + assert_true (const bool actual, + const std::string &name = "unnamed assertion") const + { + assert_equals (true, actual, name); + } private: - /// Handles a failed assertion. - /// - /// @param what A description of the failed assertion. - /// @throw an @c Assertion_Error if requested. - void handle_failed(const std::string &what) const { - using std::endl; - - if (message_on_failed) { - err << what << endl; - } - if (throw_on_failed) { - throw Assertion_Error(what); // NOLINT - } - } - - /// Handles a passed assertion. - /// - /// @param name The name of the passed assertion. - void handle_passed(const std::string &name) const { - using std::endl; - - if (message_on_passed) { - out << "Passed: " << name << endl; - } - } - - /// Issue a message when an assertion failed? - bool message_on_failed = false; - - /// Issue a message when an assertion passed? - bool message_on_passed = true; - - /// Throw an exception when an assertion failed? - bool throw_on_failed = true; - - /// The output stream for error messages. - std::ostream &err = std::cerr; - - /// The output stream for other messages. - std::ostream &out = std::cout; + /// Handles a failed assertion. + /// + /// @param what A description of the failed assertion. + /// @throw an @c Assertion_Error if requested. + void + handle_failed (const std::string &what) const + { + using std::endl; + + if (message_on_failed) + { + err << what << endl; + } + if (throw_on_failed) + { + throw Assertion_Error (what); // NOLINT + } + } + + /// Handles a passed assertion. + /// + /// @param name The name of the passed assertion. + void + handle_passed (const std::string &name) const + { + using std::endl; + + if (message_on_passed) + { + out << "Passed: " << name << endl; + } + } + + /// Issue a message when an assertion failed? + bool message_on_failed = false; + + /// Issue a message when an assertion passed? + bool message_on_passed = true; + + /// Throw an exception when an assertion failed? + bool throw_on_failed = true; + + /// The output stream for error messages. + std::ostream &err = std::cerr; + + /// The output stream for other messages. + std::ostream &out = std::cout; }; #endif // ESPECIA_UNITTEST_H_H