Skip to content

Commit 3483509

Browse files
committed
TST: lqmn tests based on scipy's (python) tests
1 parent 3fc840f commit 3483509

File tree

1 file changed

+91
-0
lines changed

1 file changed

+91
-0
lines changed
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
#include "../testing_utils.h"
2+
3+
#include <xsf/legendre.h>
4+
5+
// backport of std::mdspan (since C++23)
6+
#define MDSPAN_USE_PAREN_OPERATOR 1
7+
#include <xsf/third_party/kokkos/mdspan.hpp>
8+
9+
// From https://github.com/scipy/scipy/blob/bdd3b0e/scipy/special/tests/test_legendre.py#L693-L697
10+
TEST_CASE("lqmn TestLegendreFunctions.test_lqmn", "[lqmn]") {
11+
constexpr double atol = 1.5e-4;
12+
13+
constexpr int m = 0;
14+
constexpr int n = 2;
15+
constexpr double x = 0.5;
16+
17+
constexpr int m1p = m + 1;
18+
constexpr int n1p = n + 1;
19+
20+
// lqmnf = special.lqmn(0, 2, .5)
21+
double lqmnf0_data[m1p * n1p], lqmnf1_data[m1p * n1p];
22+
auto lqmnf0 = std::mdspan(lqmnf0_data, m1p, n1p);
23+
auto lqmnf1 = std::mdspan(lqmnf1_data, m1p, n1p);
24+
xsf::lqmn(x, lqmnf0, lqmnf1);
25+
26+
// lqf = special.lqn(2, .5)
27+
double lqf0_data[n1p], lqf1_data[n1p];
28+
auto lqf0 = std::mdspan(lqf0_data, n1p);
29+
auto lqf1 = std::mdspan(lqf1_data, n1p);
30+
xsf::lqn(x, lqf0, lqf1);
31+
32+
// assert_allclose(lqmnf[0][0], lqf[0], atol=1.5e-4, rtol=0)
33+
for (int n = 0; n < n1p; ++n) {
34+
auto error0 = xsf::extended_absolute_error(lqmnf0(0, n), lqf0(n));
35+
CAPTURE(0, 0, n, lqmnf0(0, n), lqf0(n), error0, atol);
36+
REQUIRE(error0 <= atol);
37+
}
38+
39+
// assert_allclose(lqmnf[1][0], lqf[1], atol=1.5e-4, rtol=0)
40+
for (int n = 0; n < n1p; ++n) {
41+
auto error1 = xsf::extended_absolute_error(lqmnf1(0, n), lqf1(n));
42+
CAPTURE(1, 0, n, lqmnf1(0, n), lqf1(n), error1, atol);
43+
REQUIRE(error1 <= atol);
44+
}
45+
46+
// some additional smoke checks
47+
for (int n = 0; n < n1p; ++n) {
48+
CAPTURE(0, 0, n, lqmnf0(0, n));
49+
REQUIRE(lqmnf0(0, n) != 0.0);
50+
REQUIRE(std::isfinite(lqmnf0(0, n)));
51+
}
52+
for (int n = 0; n < n1p; ++n) {
53+
CAPTURE(1, 0, n, lqmnf1(0, n));
54+
REQUIRE(lqmnf1(0, n) != 0.0);
55+
REQUIRE(std::isfinite(lqmnf1(0, n)));
56+
}
57+
}
58+
59+
// From https://github.com/scipy/scipy/blob/bdd3b0e/scipy/special/tests/test_legendre.py#L699-L708
60+
TEST_CASE("lqmn TestLegendreFunctions.test_lqmn_gt1", "[lqmn]") {
61+
constexpr double atol = 1.5e-7;
62+
63+
constexpr int m = 2;
64+
constexpr int n = 1;
65+
constexpr int m1p = m + 1;
66+
constexpr int n1p = n + 1;
67+
68+
double lqmnf0_data[m1p * n1p], lqmnf1_data[m1p * n1p];
69+
auto lqmnf0 = std::mdspan(lqmnf0_data, m1p, n1p);
70+
auto lqmnf1 = std::mdspan(lqmnf1_data, m1p, n1p);
71+
72+
// algorithm for real arguments changes at 1.0001
73+
// test against analytical result for m=2, n=1
74+
auto x0 = 1.0001;
75+
auto delta = 0.00002;
76+
77+
// for x in (x0-delta, x0+delta):
78+
for (double x : {x0 - delta, x0 + delta}) {
79+
// lq = special.lqmn(2, 1, x)[0][-1, -1]
80+
xsf::lqmn(x, lqmnf0, lqmnf1);
81+
double lq = lqmnf0(m, n); // _[-1, -1] corresponds to _[m, n]
82+
83+
// expected = 2/(x*x-1)
84+
double expected = 2.0 / (x * x - 1.0);
85+
86+
// assert_allclose(lq, expected, atol=1.5e-7, rtol=0)
87+
auto error = xsf::extended_absolute_error(lq, expected);
88+
CAPTURE(x, lq, expected, error, atol);
89+
REQUIRE(error <= atol);
90+
}
91+
}

0 commit comments

Comments
 (0)