Commit 69219b67 authored by David Frank's avatar David Frank
Browse files

Add modified Bessel function of the first kind for integer order

parent bbbe9fb8
......@@ -5,6 +5,7 @@ set(MODULE_HEADERS
Cloneable.h
Utilities/Assertions.h
Utilities/Badge.hpp
Utilities/Bessel.h
Utilities/CartesianIndices.h
Utilities/DataContainerFormatter.hpp
Utilities/FormatConfig.h
......@@ -37,6 +38,7 @@ set(MODULE_HEADERS
set(MODULE_SOURCES
elsaDefines.cpp
Backtrace.cpp
Utilities/Bessel.cpp
Utilities/CartesianIndices.cpp
Utilities/Assertions.cpp
Descriptors/DataDescriptor.cpp
......
#include "Bessel.h"
#include "elsaDefines.h"
#include <unsupported/Eigen/SpecialFunctions>
namespace elsa::math
{
double bessi0(double x)
{
double ax = std::abs(x);
// polynomial fit, different polynoms for different ranges
if (ax < 3.75) {
const auto y = std::pow(x / 3.75, 2);
// different terms
const auto p0 = 0.45813e-2;
const auto p1 = 0.360768e-1 + y * p0;
const auto p2 = 0.2659732 + y * p1;
const auto p3 = 1.2067492 + y * p2;
const auto p4 = 3.0899424 + y * p3;
const auto p5 = 3.5156229 + y * p4;
return 1.0 + y * p5;
} else {
const auto y = 3.75 / ax;
// different terms
const auto p0 = 0.392377e-2;
const auto p1 = -0.1647633e-1 + y * p0;
const auto p2 = 0.2635537e-1 + y * p1;
const auto p3 = -0.2057706e-1 + y * p2;
const auto p4 = 0.916281e-2 + y * p3;
const auto p5 = -0.157565e-2 + y * p4;
const auto p6 = 0.225319e-2 + y * p5;
const auto p7 = 0.1328592e-1 + y * p6;
const auto p8 = 0.39894228 + y * p7;
return (std::exp(ax) / std::sqrt(ax)) * p8;
}
}
double bessi1(double x)
{
double result = 0;
double ax = std::abs(x);
// polynomial fit, different polynoms for different ranges
if (ax < 3.75) {
const auto y = std::pow(x / 3.75, 2);
const auto p0 = 0.32411e-3;
const auto p1 = 0.301532e-2 + y * p0;
const auto p2 = 0.2658733e-1 + y * p1;
const auto p3 = 0.15084934 + y * p2;
const auto p4 = 0.51498869 + y * p3;
const auto p5 = 0.87890594 + y * p4;
const auto p6 = 0.5 + y * p5;
result = ax * p6;
} else {
const auto y = 3.75 / ax;
const auto p0 = 0.420059e-2;
const auto p1 = 0.1787654e-1 - y * p0;
const auto p2 = -0.2895312e-1 + y * p1;
const auto p3 = 0.2282967e-1 + y * p2;
const auto p4 = -0.1031555e-1 + y * p3;
const auto p5 = 0.163801e-2 + y * p4;
const auto p6 = -0.362018e-2 + y * p5;
const auto p7 = -0.3988024e-1 + y * p6;
const auto p8 = 0.39894228 + y * p7;
result = p8 * (exp(ax) / sqrt(ax));
}
return x < 0.0 ? -result : result;
}
double bessi2(double x) { return (x == 0) ? 0 : bessi0(x) - ((2 * 1) / x) * bessi1(x); }
double bessi3(double x) { return (x == 0) ? 0 : bessi1(x) - ((2 * 2) / x) * bessi2(x); }
double bessi4(double x) { return (x == 0) ? 0 : bessi2(x) - ((2 * 3) / x) * bessi3(x); }
double bessi(int m, double x)
{
if (m == 0) {
return bessi0(x);
} else if (m == 1) {
return bessi1(x);
} else if (m == 2) {
return bessi2(x);
} else if (m == 3) {
return bessi3(x);
} else if (m == 4) {
return bessi4(x);
}
constexpr double ACC = 40.0;
constexpr double BIGNO = 1.0e10;
constexpr double BIGNI = 1.0e-10;
if (x == 0.0) {
return 0.0;
} else {
double tox = 2.0 / std::abs(x);
double result = 0.0;
double bip = 0.0;
double bi = 1.0;
for (int j = 2 * (m + (int) std::sqrt(ACC * m)); j > 0; --j) {
double bim = bip + j * tox * bi;
bip = bi;
bi = bim;
if (std::abs(bi) > BIGNO) {
result *= BIGNI;
bi *= BIGNI;
bip *= BIGNI;
}
if (j == m) {
result = bip;
}
}
result *= bessi0(x) / bi;
return (((x < 0.0) && ((m % 2) == 0)) ? -result : result);
}
}
} // namespace elsa::math
#pragma once
#include <cmath>
namespace elsa::math
{
/// Modified Bessel Function of the First Kind \f$ I_n(x) \f$, with \f$n = 0\f$
/// See:
/// - Chapter 9 of Handbook of Mathematical Functions: with Formulas, Graphs, and Mathematical
/// Tables, by Milton Abramowitz and Irene A. Stegun
/// - Chapter 6.6 of Numerical recipes in C: The art of scientific computing, second edition,
/// by Jaob Winkler (1993)
/// - https://www.astro.rug.nl/~gipsy/sub/bessel.c
/// - https://stackoverflow.com/questions/8797722/modified-bessel-functions-of-order-n
double bessi0(double x);
/// Modified Bessel Function of the First Kind \f$ I_n(x) \f$, with \f$n = 1\f$
/// See:
/// - Chapter 9 of Handbook of Mathematical Functions: with Formulas, Graphs, and Mathematical
/// Tables, by Milton Abramowitz and Irene A. Stegun
/// - Chapter 6.6 of Numerical recipes in C: The art of scientific computing, second edition,
/// by Jaob Winkler (1993)
double bessi1(double x);
/// Modified Bessel Function of the First Kind \f$ I_n(x) \f$, with \f$n = 2\f$, using the
/// recurrence relations, i.e. \f$ I_{n+1}(x) = I_{n-1}(x) - (2 * n / x) I_{n}(x)\f$
///
/// See:
/// - Chapter 9.6.26(i) of Handbook of Mathematical Functions: with Formulas, Graphs, and
/// Mathematical Tables, by Milton Abramowitz and Irene A. Stegun
/// - Equation 6.6.4 of Numerical recipes in C: The art of scientific computing, second edition,
/// by Jaob Winkler (1993)
double bessi2(double x);
/// Modified Bessel Function of the First Kind \f$ I_n(x) \f$, with \f$n = 3\f$, using the
/// recurrence relations, i.e. \f$ I_{n+1}(x) = I_{n-1}(x) - (2 * n / x) I_{n}(x)\f$
///
/// See:
/// - Chapter 9.6.26(i) of Handbook of Mathematical Functions: with Formulas, Graphs, and
/// Mathematical Tables, by Milton Abramowitz and Irene A. Stegun
/// - Equation 6.6.4 of Numerical recipes in C: The art of scientific computing, second edition,
/// by Jaob Winkler (1993)
double bessi3(double x);
/// Modified Bessel Function of the First Kind \f$ I_n(x) \f$, with \f$n = 4\f$, using the
/// recurrence relations, i.e. \f$ I_{n+1}(x) = I_{n-1}(x) - (2 * n / x) I_{n}(x)\f$
///
/// See:
/// - Chapter 9.6.26(i) of Handbook of Mathematical Functions: with Formulas, Graphs, and
/// Mathematical Tables, by Milton Abramowitz and Irene A. Stegun
/// - Equation 6.6.4 of Numerical recipes in C: The art of scientific computing, second edition,
/// by Jaob Winkler (1993)
double bessi4(double x);
/// See: https://stackoverflow.com/questions/8797722/modified-bessel-functions-of-order-n
double bessi(int m, double x);
} // namespace elsa::math
......@@ -28,3 +28,4 @@ ELSA_DOCTEST(DataHandlerMap)
ELSA_DOCTEST(DataContainer)
ELSA_DOCTEST(DataContainerFormatter)
ELSA_DOCTEST(CartesianIndices)
ELSA_DOCTEST(Bessel)
#include "doctest/doctest.h"
#include "Bessel.h"
#include "elsaDefines.h"
#include "spdlog/fmt/fmt.h"
#include "spdlog/fmt/bundled/ranges.h"
TEST_SUITE_BEGIN("Bessel");
using namespace elsa;
template <typename T>
std::vector<T> get_bessi0();
template <typename T>
std::vector<T> get_bessi1();
template <typename T>
std::vector<T> get_bessi2();
template <typename T>
std::vector<T> get_bessi3();
template <typename T>
std::vector<T> get_bessi4();
TEST_CASE_TEMPLATE("bessel0: compare against libstdc++ std::cyl_bessel_i", T, float, double)
{
auto expected = get_bessi0<T>();
const auto size = expected.size();
auto linspace = Vector_t<T>::LinSpaced(size, 0, 4);
for (std::size_t i = 0; i < size; ++i) {
CHECK_EQ(math::bessi0(linspace[i]), doctest::Approx(expected[i]));
}
}
TEST_CASE_TEMPLATE("bessel1: compare against libstdc++ std::cyl_bessel_i", T, float, double)
{
std::vector<T> expected = get_bessi1<T>();
const auto size = expected.size();
auto linspace = Vector_t<T>::LinSpaced(size, 0, 4);
for (std::size_t i = 0; i < size; ++i) {
CHECK_EQ(math::bessi1(linspace[i]), doctest::Approx(expected[i]));
}
}
TEST_CASE_TEMPLATE("bessel2: compare against libstdc++ std::cyl_bessel_i", T, float, double)
{
std::vector<T> expected = get_bessi2<T>();
const auto size = expected.size();
auto linspace = Vector_t<T>::LinSpaced(size, 0, 4);
for (std::size_t i = 0; i < size; ++i) {
CHECK_EQ(math::bessi2(linspace[i]), doctest::Approx(expected[i]));
}
}
TEST_CASE_TEMPLATE("bessel3: compare against libstdc++ std::cyl_bessel_i", T, float, double)
{
std::vector<T> expected = get_bessi3<T>();
const auto size = expected.size();
auto linspace = Vector_t<T>::LinSpaced(size, 0, 4);
for (std::size_t i = 0; i < size; ++i) {
CHECK_EQ(math::bessi3(linspace[i]), doctest::Approx(expected[i]));
}
}
TEST_CASE_TEMPLATE("bessel4: compare against libstdc++ std::cyl_bessel_i", T, float, double)
{
std::vector<T> expected = get_bessi4<T>();
const auto size = expected.size();
auto linspace = Vector_t<T>::LinSpaced(size, 0, 4);
for (std::size_t i = 0; i < size; ++i) {
CHECK_EQ(math::bessi4(linspace[i]), doctest::Approx(expected[i]));
}
}
template <typename T>
std::vector<T> get_bessi0()
{
return {1,
1.0001010101009742,
1.0004040710131905,
1.0009092745748345,
1.0016167738839077,
1.0025267833497755,
1.0036395787653625,
1.0049554974000179,
1.0064749381130846,
1.0081983614882122,
1.0101262899884598,
1.012259308132248,
1.0145980626902165,
1.0171432629030606,
1.019895680720423,
1.0228561510609224,
1.02602557209342,
1.029404905539608,
1.032995176998044,
1.0367974762897314,
1.0408129578253802,
1.0450428409944676,
1.0494884105762434,
1.054151017172824,
1.059032077664524,
1.064133075687591,
1.0694555621345143,
1.0750011556770782,
1.080771543312348,
1.086768480931786,
1.0929937939136918,
1.0994493777391823,
1.1061371986319262,
1.1130592942218605,
1.1202177742331243,
1.1276148211964545,
1.1352526911862921,
1.143133714582864,
1.1512602968595107,
1.1596349193955309,
1.1682601403148447,
1.177138595350759,
1.1862729987371505,
1.1956661441263778,
1.2053209055342506,
1.2152402383123868,
1.2254271801483088,
1.2358848520936279,
1.246616459620684,
1.257625293708014,
1.2689147319550371,
1.280488239726348,
1.2923493713260272,
1.3045017712023848,
1.3169491751835656,
1.3296954117444486,
1.3427444033053038,
1.3561001675626532,
1.3697668188528151,
1.3837485695486196,
1.3980497314897826,
1.4126747174474543,
1.427628042623464,
1.4429143261847786,
1.4585382928337465,
1.474504774414658,
1.490818711557214,
1.5074851553574764,
1.5245092690969024,
1.541896330000072,
1.5596517310317402,
1.5777809827338407,
1.5962897151031123,
1.6151836795100045,
1.6344687506595441,
1.6541509285948752,
1.674236340744168,
1.694731244011643,
1.7156420269134414,
1.736975211759113,
1.7587374568794965,
1.7809355589017835,
1.803576455072581,
1.8266672256298022,
1.8502150962242276,
1.8742274403916046,
1.8987117820761645,
1.923675798206464,
1.9491273213244606,
1.9750743422687718,
2.001525012913072,
2.0284876489606,
2.05597073279579,
2.0839829163940258,
2.1125330242905807,
2.141630056609784,
2.1712831921555122,
2.2015017915641,
2.2322954005208158,
2.2636737530410236,
2.2956467748172367,
2.328224586633251,
2.361417507846559,
2.3952360599403355,
2.4296909701462237,
2.464793175139245,
2.5005538248061545,
2.536984286088587,
2.5740961469023644,
2.611901220134393,
2.6504115477185626,
2.6896394047921204,
2.729597303934006,
2.770297999486674,
2.811754491962946,
2.853980032539495,
2.896988127638546,
2.9407925435994704,
2.9854073114419246,
3.030846731722257,
3.077125379484949,
3.1242581093108184,
3.1722600604638638,
3.221146662138552,
3.270933638809447,
3.3216370156851416,
3.3732731242683904,
3.425858608024511,
3.479410428160046,
3.5339458695137815,
3.5894825465622664,
3.646038409541965,
3.7036317506902616,
3.7622812106075663,
3.8220057847428186,
3.8828248300047266,
3.944758071501113,
4.007825609408828,
4.072047925976672,
4.137445892663884,
4.204040777416756,
4.271854252086014,
4.34090839998763,
4.411225723609797,
4.482829152468875,
4.5557420511170985,
4.629988227305008,
4.70559194030148,
4.782577909374421,
4.860971322435166,
4.940797844849728,
5.0220836284200425,
5.104855320538522,
5.189140073519174,
5.274965554108702,
5.362359953181001,
5.451351995618587,
5.5419709503845205,
5.634246640788485,
5.728209454950738,
5.823890356467696,
5.921320895283087,
6.020533218768536,
6.121560083017699,
6.2244348643579,
6.329191571083614,
6.435864855415881,
6.544490025692144,
6.655103058790918,
6.767740612795618,
6.882440039902386,
6.999239399576497,
7.118177471962079,
7.239293771550163,
7.362628561109785,
7.488222865887558,
7.616118488080491,
7.746358021587595,
7.878984867045601,
8.014043247154069,
8.151578222295688,
8.291635706457441,
8.434262483458198,
8.579506223488936,
8.727415499971412,
8.878039806741516,
9.031429575563577,
9.18763619398187,
9.346712023516085,
9.508710418207086,
9.673685743519929,
9.841693395611049,
10.012789820966388,
10.187032536418023,
10.364480149546196,
10.545192379474479,
10.729230078065527,
10.916655251525206,
11.107531082422915,
11.301921952136313};
}
template <typename T>
std::vector<T> get_bessi1()
{
return {0,
0.01005075884045603,
0.020104563391044293,
0.03016446038739731,
0.040233498616510634,
0.05031472994333094,
0.060411210338432324,
0.07052600090714392,
0.08066216892049329,
0.09082278884832931,
0.10101094339499074,
0.11122972453788607,
0.12148223456935212,
0.13177158714216047,
0.14210090831903993,
0.15247333762658902,
0.16289202911394865,
0.17336015241661085,
0.18388089382574038,
0.19445745736338693,
0.2050930658639692,
0.21579096206241288,
0.22655440968932924,
0.2373866945736206,
0.24829112575290474,
0.25927103659215084,
0.2703297859109233,
0.2814707591196341,
0.29269736936520485,
0.30401305868654604,
0.31542129918026324,
0.3269255941770035,
0.33852947942885864,
0.35023652430824875,
0.3620503330187103,
0.37397454581801776,
0.38601284025407606,
0.3981689324140204,
0.41044657818696956,
0.42284957454087707,
0.43538176081394164,
0.4480470200210296,
0.4608492801755771,
0.473792515627442,
0.48688074841718176,
0.500118049647239,
0.5135085408705254,
0.5270563954968935,
0.5407658402180044,
0.5546411564510945,
0.5686866818021555,
0.582906811549057,
0.5973060001451282,
0.6118887627437495,
0.626659676744486,
0.6416233833613249,
0.6567845892135676,
0.6721480679399557,
0.687718661836597,
0.7035012835192848,
0.719500917610802,
0.7357226224538141,
0.7521715318499643,
0.768852856825791,
0.7857718874261065,
0.8029339945354687,
0.8203446317284104,
0.8380093371490798,
0.8559337354209696,
0.8741235395874237,
0.8925845530836068,
0.9113226717406571,
0.9303438858227328,
0.9496542820976865,
0.9692600459421125,
0.989167463481518,
1.0093829237663967,
1.0299129209849767,
1.050764056713445,
1.0719430422044538,
1.093456700714736,
1.1153119698726648,
1.1375159040866067,
1.1600756769949445,
1.1829985839586397,
1.2062920445972445,
1.2299636053692673,
1.2540209421978297,
1.278471863142555,
1.3033243111186563,
1.328586366664206,
1.3542662507565746,
1.3803723276790743,
1.4069131079388215,
1.4338972512368886,
1.4613335694918075,
1.48923102991752,
1.5175987581568913,
1.5464460414719126,
1.5757823319917503,
1.6056172500198194,
1.6359605874010676,
1.666822310950698,
1.698212565945569,
1.7301416796795324,
1.7626201650839974,
1.7956587244150402,
1.829268253008377,
1.8634598431035827,
1.8982447877389228,
1.9336345847182241,
1.9696409406512119,
2.0062757750687927,
2.043551224614752,
2.0814796473154176,
2.1200736269288165,
2.15