Skip to content

Commit 9d816fe

Browse files
committed
test/adjointness denom now almost no random fluct; improves test stability
1 parent 7bd8adc commit 9d816fe

File tree

1 file changed

+29
-15
lines changed

1 file changed

+29
-15
lines changed

test/adjointness.cpp

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,45 @@
1+
#include "utils/norms.hpp"
12
#include <finufft/test_defs.h>
23

34
/* Test that execute_adjoint applies the adjoint of execute, in the
45
guru interface, for all types and dimensions.
56
We use the test_defs macros, as with other C-interface tests.
6-
Barnett 7/22/25 - 7/23/25.
7+
Barnett 7/23/25. More stable relative error denom, discussion 7/29/25.
78
89
Test tolerances are hard-wired in this code, not via cmd line.
910
Subtlety is that adjointness is subject to round-off, which is amplified
10-
by the r_dyn dynamic range, per dimension. That can get bad for USF=1.25.
11-
It also appears to vary with platform (macos-13 CI forced us to abandon
12-
allowederr = 1e-13 in favor of 1e-12).
11+
by the r_dyn dynamic range, per dimension. That can get bad for USF=1.25,
12+
esp in type 3 (eg by default if eps~1e-8 in double prec).
1313
See discussions "Repeatability" and "Adjointness" in docs/trouble.rst
14+
15+
Discussion of the "correct" denominator to claim "relative" err in (f,F)-(C,c):
16+
||Ferr||_2/||F||_2 is our NUFFT metric, as per our SISC 2019 FINUFFT paper.
17+
Expect err in (f,F) ~ ||f||_2.(error in one entry of F), by rotational
18+
invariance in C^N (where N is really Nused the number of modes).
19+
And (err in one entry of F) ~ ||Ferr||_2 / sqrt(N). Combining with the metric
20+
above, explains our choice denom = ||f||.||F||/sqrt(N) to give a rel measure
21+
of adjointness error. Has much less fluctuation than old denom=(f,F) which it
22+
turned out had zero-mean Gaussian pdf (=> fat-tailed pdf of reciprocal, bad!)
1423
*/
1524

1625
using namespace std;
1726

1827
int main() {
1928

20-
BIGINT Ns[3] = {300, 40, 6}; // modes per dim, smallish probs (~1e5 max)
21-
BIGINT M = 100000; // NU pts, smallish, but enough so rand err small
29+
BIGINT Ns[3] = {200, 40, 6}; // modes per dim, smallish probs (~1e5 max)
30+
BIGINT M = 50000; // NU pts, smallish, but enough so rand err small
2231
int isign = +1;
2332
int ntr = 1; // how many transforms (one for now)
2433
finufft_opts opts;
2534
FINUFFT_DEFAULT_OPTS(&opts);
2635
// opts.upsampfac = 1.25; // experts use to override default USF
2736
#ifdef SINGLE
28-
FLT tol = 1e-5; // requested transform tol (adj err worse near epsmach)
29-
FLT allowederr = 1e-4; // 1e3*epsmach due to USF=1.25 larger r_dyn
37+
FLT tol = 1e-6; // requested transform tol (small enough to force USF=2)
38+
FLT allowederr = 1e-4; // ~1e3*epsmach (allow USF=1.25 larger r_dyn)
3039
string name = "adjointnessf";
3140
#else
32-
FLT tol = 1e-12; // requested transform tol (USF=2 guaranteed)
33-
FLT allowederr = 1e-12; // 1e4*epsmach (USF=2 r_dyn^3<1e3, but macos-13 fails)
41+
FLT tol = 1e-12; // requested transform tol (eps<=1e-9 => USF=2 guaranteed)
42+
FLT allowederr = 1e-10; // ~1e6*epsmach (USF=2 r_dyn^3<1e3, but allow USF=1.25)
3443
string name = "adjointness";
3544
#endif
3645

@@ -90,19 +99,24 @@ int main() {
9099
FINUFFT_DESTROY(plan);
91100

92101
// measure scalar error (f,F) - (C,c), should vanish by adjointness...
93-
CPX ipc = 0.0, ipf = 0.0; // results for (C,c) and (f,F)
102+
CPX ipc = 0.0, ipf = 0.0; // inner-prod results for (C,c) and (f,F)
94103
for (int i = 0; i < M; i++) ipc += conj(C[i]) * c[i];
95-
int Nused = (type == 3) ? Nmax : N; // how many modes or freqs used
104+
int Nused = (type == 3) ? Nmax : N; // how many modes or freqs used
96105
for (int j = 0; j < Nused; j++) ipf += conj(f[j]) * F[j];
97-
FLT err = abs(ipc - ipf) / abs(ipc); // scale rel to innerprod size
98-
cout << " adj rel err " << err << endl;
106+
107+
// denominator for rel error (twonorm in utils.hpp), see discussion at top:
108+
// FLT denom = twonorm(M,C.data()) * twonorm(M,c.data()) / sqrt(M); // v sim
109+
FLT denom = twonorm(Nused, F.data()) * twonorm(Nused, f.data()) / sqrt(Nused);
110+
FLT err = abs(ipc - ipf) / denom; // scale rel to norms of vectors in ipc
111+
cout << " adj rel err " << err << endl; // "\t denom=" << denom << endl;
99112
errmax = max(err, errmax);
100113
}
101114
}
102115

103116
// report and exit code
104117
if (errmax > allowederr || iermax > 0) {
105-
cout << name << " failed! (allowederr=" << allowederr << ")" << endl;
118+
cout << name << " failed! (allowederr=" << allowederr << ", iermax=" << iermax << ")"
119+
<< endl;
106120
return 1;
107121
} else {
108122
cout << name << " passed" << endl;

0 commit comments

Comments
 (0)