This appendix provides a step‑by‑step protocol for testing the STC’s prediction of log‑periodic oscillations in the cosmic microwave background (CMB) angular power spectrum. The prediction is:
$$ \ell(\ell+1)C_\ell \propto \left[ 1 + B \cos\!\left( \frac{2\pi}{\ln q} \ln \ell + \varphi \right) \right], $$
where $B \lesssim 0.01$, $q$ is the discrete scale factor (likely $q=2$), and $\varphi$ is a phase.
The protocol uses publicly available CMB data from the Planck, ACT, and SPT collaborations. All steps can be implemented in Python with standard scientific libraries (NumPy, SciPy, astropy).
COMPowerSpectCMB‑full‑R3.01.txt from the Planck Legacy Archive. Contains $C_\ell$ for $\ell = 2–2508$ with covariance matrix.ACTdr4cls.dat from the ACT Data Release 4 website. Covers $\ell \approx 300–9000$.SPT3G_bandpowers.txt from the SPT‑3G public data release. Covers $\ell \approx 300–8000$.Pre‑processing: Combine the datasets into a single array $(\elli, C{\elli}, \sigma{\elli})$, where $\sigma{\ell_i}$ is the standard error. Use the unbinned (bandpower‑averaged) estimates to preserve high‑$\ell$ information.
Result: A cleaned dataset $\{\elli, C{\elli}\}{i=1}^{N}$ with uncertainties $\sigma_i$.
The log‑periodic signal is a small modulation on top of the smooth $\Lambda$CDM power spectrum. Therefore we need to subtract the best‑fit theoretical spectrum.
Compute the theoretical $C_\ell^{\text{theory}}$ using CAMB or CLASS with the Planck 2018 best‑fit parameters:
$$ \Omegab h^2 = 0.02237,\quad \Omegac h^2 = 0.1200,\quad H0 = 67.36,\quad \tau = 0.0544,\quad As = 2.10\times10^{-9},\quad n_s = 0.9649. $$
Run the Boltzmann code to obtain $C_\ell^{\text{theory}}$ for the same $\ell$ range as the data.
Define the residuals
$$ r\ell = \frac{\ell(\ell+1)}{2\pi}\bigl(C\ell^{\text{obs}} - C_\ell^{\text{theory}}\bigr). $$
The prefactor $\ell(\ell+1)/(2\pi)$ converts to the conventional dimensionless power. The residuals $r_\ell$ should have mean zero and contain the log‑periodic signal plus noise.
Visual check: Plot $r_\ell$ vs $\ell$ (linear scale) to confirm no large‑scale systematic drift remains.
The signal is periodic in $\ln\ell$, not in $\ell$. Therefore we interpolate the residuals onto a uniform grid in $x = \ln\ell$.
Let $x{\min} = \ln(30)$, $x{\max} = \ln(5000)$. Choose a grid spacing $\Delta x$ smaller than the expected period $\ln q$. For $q=2$, $\ln 2 \approx 0.693$. A safe choice is $\Delta x = 0.01$ (about 70 points per period).
Number of grid points: $Nx = \lfloor (x{\max}-x_{\min})/\Delta x \rfloor + 1$.
Grid points: $xj = x{\min} + j\Delta x,\; j=0,\dots,N_x-1$.
Use cubic spline interpolation (or linear if the data are dense enough) to obtain $r(xj)$ from the original $r{\ell_i}$. Propagate errors: if the original errors are Gaussian, use error propagation for the interpolation.
Output: Equally‑spaced samples $rj = r(xj)$ with uncertainties $\epsilon_j$.
Compute the DFT of the interpolated residuals:
$$ \tilde{r}k = \sum{j=0}^{Nx-1} rj \, e^{-2\pi i j k / Nx}, \qquad k = 0,\dots,Nx-1. $$
The corresponding physical frequencies are
$$ fk = \frac{k}{Nx \Delta x} \quad \text{[cycles per unit $\ln\ell$]}. $$
Compute the power spectrum
$$ Pk = |\tilde{r}k|^2. $$
The predicted frequency of the log‑periodic signal is
$$ f_{\text{pred}} = \frac{1}{\ln q}. $$
For $q=2$, $f_{\text{pred}} \approx 1.4427$ cycles per unit $\ln\ell$.
Search window: Look for a peak in $P_k$ within $f \in [1.0, 2.0]$ (allowing for small deviations from $q=2$).
The finite $\ell$ range and masking introduce a window function that distorts the power spectrum. Estimate the window function by applying the same analysis to a pure noise simulation with the same mask and $\ell$ coverage. Divide the observed $P_k$ by the window‑function power to deconvolve.
The null hypothesis $H_0$: the residuals contain only Gaussian noise (no log‑periodic signal).
Alternative: Use a permutation test by randomly shuffling the residuals $r_\ell$ (destroying any periodic signal) and repeating the analysis.
If analyzing multiple experiments (Planck, ACT, SPT) or multiple frequency bands (TT, EE, TE), apply the Benjamini‑Hochberg procedure to control the false‑discovery rate at 5%.
If a significant peak is found, fit the model
$$ r(x) = A \cos(2\pi f x + \varphi) + \text{noise}, \qquad x = \ln\ell, $$
using least‑squares or MCMC (emcee package). Parameters: amplitude $A$, frequency $f$, phase $\varphi$, plus possibly a linear tilt (allowing for residual smooth mismatch).
Run an MCMC with flat priors:
Use the Planck likelihood code (plik) to account for the full covariance matrix of the $C_\ell$ data.
From the posterior of $f$, compute
$$ q = e^{1/f}, $$
the discrete scale factor. Also compute the signal‑to‑noise ratio $\text{SNR} = A/\sigma_A$.
Repeat the entire analysis independently for:
The oscillation parameters $(f, A, \varphi)$ should be consistent across experiments if the signal is cosmological. Inconsistencies would indicate instrumental systematics.
Combine datasets: Perform a joint fit to all experiments, allowing for independent amplitudes (due to different calibration) but common frequency and phase.
If a log‑periodic signal is detected:
If no signal is found, set an upper limit on $B$ (e.g., $B < 0.005$ at 95% CL). This would constrain the discrete scale invariance to be very weak or the scale factor $q$ to be far from an integer.
import numpy as np
from scipy.interpolate import interp1d
from scipy.fft import fft
# Step 1: load data
ell, C_obs, sigma = load_data('planck2018.dat')
C_theory = get_camb_spectrum(ell)
# Step 2: residuals
res = ell*(ell+1)/(2*np.pi) * (C_obs - C_theory)
# Step 3: logarithmic resampling
x = np.log(ell)
x_grid = np.arange(np.log(30), np.log(5000), 0.01)
f_interp = interp1d(x, res, kind='cubic')
res_grid = f_interp(x_grid)
# Step 4: Fourier transform
N = len(res_grid)
dft = fft(res_grid)
freq = np.fft.fftfreq(N, d=0.01)
power = np.abs(dft)**2
# Step 5: find peak near f_pred = 1.4427
mask = (freq > 1.0) & (freq < 2.0)
idx_peak = np.argmax(power[mask])
f_peak = freq[mask][idx_peak]
q_est = np.exp(1/f_peak)
# Step 6: bootstrap significance
# ... (implementation omitted for brevity)
# Step 7: parameter estimation with emcee
# ... (see full code in repository)