It may be possible to use Bayesian statistics here.
Let's set testing aside for a moment and instead focus on estimation. If we can construct a model and assess the estimates of $A$, maybe we can say something substantive about the decay term.
A simple model in Stan would be
data {
int n;
array[n] int r;
vector[n] t;
real c;
real N;
real f;
}
parameters {
real<lower=0> inv_tau;
real<lower=-1, upper = 1> phi_scaled;
real<lower=0, upper=1> A;
}
transformed parameters {
vector[n] s = A .* sin(2 * pi() * f * t + pi() * phi_scaled) .* exp(-t * inv_tau);
vector[n] cts = N .*(c-2-2*c*s)./(c-2);
}
model{
A ~ uniform(0, 1);
phi_scaled ~ uniform(-1, 1);
inv_tau ~ cauchy(0, 1);
r ~ poisson(cts);
}
generated quantities {
real estimated_tau = 1.0 / inv_tau;
real estimated_phu = pi() * phi;
}
Here, I am modelling scaled_phi
to be on the interval $[-1, 1]$ and simply multiplying it by $\pi$ in the signal part. Additionally, I am modelling the inverse of $\tau$ rather than tau because I typically find that small parameters are often modelled more easily than large ones.
When I run the model, I get good diagnostics (note, you will need to install cmdstan and cmdstanpy if you want to replicate this).
import numpy as np
import cmdstanpy
import matplotlib.pyplot as plt
import arviz as az
def simulate_data():
rng = np.random.RandomState(123)
# Signal parameters
f = 0.25 # Known
tau = 50 # Unknown
phi = 0 # Unknown
A = 0.07 # Unknown A<1
# Signal modeling
t = np.arange(25)
s = A*np.sin(2*np.pi*f*t+phi)*np.exp(-t/tau)
# Noise parameters:
N = 20e3 # Mean number of counts (known)
c = 0.3 # Contrast (known)
# Noise modeling
cts = N *(c-2-2*c*s)/(c-2)
r = rng.poisson(cts)
return t, r, cts
def fit_model(*args, **kwargs):
t, r, cts = simulate_data()
model = cmdstanpy.CmdStanModel(stan_file="signal.stan")
data = dict(t=t.tolist(), r=r.tolist(), n=r.size, f = 0.25, N = 20000, c = 0.3)
fit = model.sample(data, **kwargs)
return fit
fit = fit_model()
print(fit.diagnose())
Checking sampler transitions treedepth.
Treedepth satisfactory for all transitions.
Checking sampler transitions for divergences.
No divergent transitions found.
Checking E-BFMI - sampler transitions HMC potential energy.
E-BFMI satisfactory.
Effective sample size satisfactory.
Split R-hat values satisfactory all parameters.
Processing complete, no problems detected.
Now, we can look at some plots of the estimated parameters
var_names = ['A', 'estimated_tau', 'estimated_phi']
fig, ax = plt.subplots(dpi = 240, ncols=3, nrows=1, figsize = (15, 5))
for vn, a in zip(var_names, ax):
x = fit.stan_variable(vn)
a.hist(x, edgecolor='white')
a.set_title(vn)
$A$ and $\phi$ are very well estimated. Tau remains uncertain (95% credible interval: 21 - 389), perhaps that could be combatted with some better priors.
From these plots, we could conclude that $A>0$ and hence the first term is likely present.