Replication: Adämmer and Weiß (2026)

This page reproduces Table B.20 in the appendix of Adämmer and Weiß (2026): rejection rates of the seven Box-Ljung type ordinal pattern (BL-OP) tests with \(m = 3\) and of the Box-Ljung test on the sample autocorrelations (BL-ACF) at level \(\alpha = 0.05\), for maximal delays \(w = 1, \ldots, 5\) and sample sizes \(T\).

Libraries

using StatsOrdinalPatterns, Random, Statistics, Printf, DataFrames
import StatsOrdinalPatterns: init_dgp_op!

Statistics of the paper and their types in the package

Table B.20 column chart_choice Summand for delay \(d\)
\(\widehat{H}_{\text{BL}}\) Shannon(base=exp(1)) \(\tfrac{2}{6}\, n_d \big(\ln 6 - \widehat{H}^{(d)}\big)\)
\(\widehat{H}^{\text{ex}}_{\text{BL}}\) ShannonExtropy(base=exp(1)) \(\tfrac{10}{6}\, n_d \big(5 \ln \tfrac{6}{5} - \widehat{H}_{\text{ex}}^{(d)}\big)\)
\(\widehat{\Delta}^2_{\text{BL}}\) DistanceToWhiteNoise() \(n_d\, \widehat{\Delta}^{2\,(d)}\)
\(\widehat{\beta}_{\text{BL}}\) UpDownBalance() \(n_d\, \big(\hat{\beta}^{(d)}\big)^2\)
\(\widehat{\tau}_{\text{BL}}\) Persistence() \(n_d\, \big(\hat{\tau}^{(d)}\big)^2\)
\(\widehat{\gamma}_{\text{BL}}\) RotationalAsymmetry() \(n_d\, \big(\hat{\gamma}^{(d)}\big)^2\)
\(\widehat{\delta}_{\text{BL}}\) UpDownScaling() \(n_d\, \big(\hat{\delta}^{(d)}\big)^2\)
\(\widehat{\rho}_{\text{BL}}\) stat_acf \(T(T+2)\, \hat{\rho}(d)^2 / (T - d)\)

Here \(n_d = T - (m-1)d\). stat_op_bp(data, w; chart_choice, m, ljung_box=true) sums the summands over \(d = 1, \ldots, w\), and crit_val_op_bp(w; chart_choice, m) returns the critical value. The BL-ACF statistic is compared with the \(\chi^2(w)\) quantile.

Setup

The DGP is \(Y_t = X_t + O_t\) with \(X_t = 0.5\, X_{t-2} + \epsilon_t\), \(\epsilon_t \sim \text{i.i.d. } N(0,1)\), and additive outliers \(O_t \sim \text{i.i.d. } 10 \cdot \text{Bernoulli}(0.1)\). As \(\alpha_1 = 0\), the values at odd and at even times are two independent AR1(0.5, Normal(0, 1)) processes, which init_dgp_op! fills in place.

dgp = AR1(0.5, Normal(0, 1))
dist_ao = 10 * Bernoulli(0.1)               # additive outliers
m = 3                                       # pattern length
level = 0.05                                # nominal significance level
ws = 1:5                                    # maximal delays
Ts = (100, 250, 500, 1000)                  # sample sizes
reps = 10_000                               # Monte Carlo replications

function simulate!(x)
    init_dgp_op!(dgp, view(x, 1:2:length(x)), dgp.dist, 1)   # odd times
    init_dgp_op!(dgp, view(x, 2:2:length(x)), dgp.dist, 1)   # even times
    for t in eachindex(x)
        x[t] += rand(dist_ao)
    end
    return x
end

charts = [
    ("H", Shannon(base=exp(1))),
    ("Hex", ShannonExtropy(base=exp(1))),
    ("Δ²", DistanceToWhiteNoise()),
    ("β", UpDownBalance()),
    ("τ", Persistence()),
    ("γ", RotationalAsymmetry()),
    ("δ", UpDownScaling()),
]

A single test

Random.seed!(1)
x = simulate!(zeros(500))
test_op_bp(x, 3; chart_choice=Persistence(), m=m, ljung_box=true)
OPBPTestResult
  Chart:            Persistence()
  Statistic:        4.1447
  ─────────────────────────────
  Asymptotic test
    Critical value: 1.4059
    p-value:        not available (use test_op_bp_bootstrap)
    Reject H₀:      true

Running the power study

The BL-OP statistic for \(w\) is the one for \(w - 1\) plus the summand of delay \(w\), and all summands of delay \(d\) are functions of the pattern frequencies returned by stat_op. One pass over \(d = 1, \ldots, 5\) therefore yields all charts and all \(w\).

# summand of delay d, as in the table above, without the weight n_d
bl_term(::Shannon, s) = (log(6) - s) / 3
bl_term(::ShannonExtropy, s) = 5 / 3 * (5 * log(6 / 5) - s)
bl_term(::DistanceToWhiteNoise, s) = s
bl_term(_, s) = s^2

function power(T, reps)
    crits = [crit_val_op_bp(w; chart_choice=cc, m=m) for (_, cc) in charts, w in ws]
    crits_acf = [quantile(Chisq(w), 1 - level) for w in ws]

    tasks = map(Iterators.partition(1:reps, cld(reps, Threads.nthreads()))) do idx
        Threads.@spawn begin
            x = Vector{Float64}(undef, T)
            bl = zeros(length(charts))
            hits = zeros(Int, length(charts) + 1, length(ws))
            for _ in idx
                simulate!(x)
                fill!(bl, 0.0)
                q = 0.0
                for d in ws
                    n = T - (m - 1) * d
                    p̂ = stat_op(x; chart_choice=Shannon(base=exp(1)), m=m, d=d)[2]
                    for (j, (_, cc)) in enumerate(charts)
                        bl[j] += n * bl_term(cc, chart_stat_op(p̂, cc))
                        hits[j, d] += bl[j] > crits[j, d]
                    end
                    q += T * (T + 2) * stat_acf(x, d)^2 / (T - d)
                    hits[end, d] += q > crits_acf[d]
                end
            end
            hits
        end
    end
    return sum(fetch.(tasks)) ./ reps
end

Check that the shortcut equals stat_op_bp:

let T = 400, x = simulate!(zeros(T))
    ok = all(
        begin
            s = sum((T - (m - 1) * d) *
                    bl_term(cc, chart_stat_op(stat_op(x; chart_choice=cc, m=m, d=d)[2], cc))
                    for d in 1:w)
            s ≈ stat_op_bp(x, w; chart_choice=cc, m=m, ljung_box=true)
        end for (_, cc) in charts, w in ws
    )
    (shortcut_matches_stat_op_bp=ok,)
end
(shortcut_matches_stat_op_bp = true,)

Table B.20: AR(2) with \(\alpha_2 = 0.5\) and additive outliers

Random.seed!(2026)
res = Dict(T => power(T, reps) for T in Ts)
names = vcat([n for (n, _) in charts], "ACF")
20×10 DataFrame
Row w T H Hex Δ² β τ γ δ ACF
Int64 Int64 Float64 Float64 Float64 Float64 Float64 Float64 Float64 Float64
1 1 100 0.214 0.179 0.178 0.02 0.44 0.05 0.048 0.059
2 1 250 0.523 0.496 0.498 0.027 0.777 0.058 0.04 0.066
3 1 500 0.849 0.837 0.839 0.028 0.964 0.062 0.045 0.069
4 1 1000 0.993 0.992 0.992 0.027 1.0 0.057 0.046 0.063
5 2 100 0.224 0.209 0.208 0.046 0.517 0.049 0.036 0.09
6 2 250 0.59 0.579 0.579 0.043 0.881 0.052 0.044 0.146
7 2 500 0.913 0.91 0.91 0.043 0.99 0.056 0.04 0.237
8 2 1000 0.999 0.999 0.999 0.046 1.0 0.055 0.042 0.429
9 3 100 0.215 0.198 0.198 0.049 0.477 0.054 0.043 0.085
10 3 250 0.54 0.526 0.526 0.049 0.851 0.054 0.048 0.134
11 3 500 0.882 0.877 0.878 0.044 0.988 0.056 0.041 0.204
12 3 1000 0.998 0.998 0.998 0.049 1.0 0.054 0.048 0.375
13 4 100 0.201 0.186 0.184 0.058 0.469 0.056 0.042 0.084
14 4 250 0.517 0.507 0.506 0.055 0.858 0.053 0.044 0.136
15 4 500 0.868 0.863 0.864 0.048 0.99 0.054 0.041 0.224
16 4 1000 0.999 0.999 0.999 0.052 1.0 0.054 0.044 0.406
17 5 100 0.206 0.19 0.188 0.065 0.443 0.058 0.05 0.081
18 5 250 0.488 0.477 0.476 0.058 0.834 0.056 0.049 0.129
19 5 500 0.845 0.84 0.84 0.05 0.987 0.055 0.046 0.208
20 5 1000 0.997 0.997 0.997 0.055 1.0 0.055 0.048 0.378

Comparison with the published rates

Published rates and comparison table
# published values, ordered (H, Hex, Δ², β, τ, γ, δ, ACF)
published = Dict(
    (1, 100) => (0.222, 0.190, 0.189, 0.024, 0.436, 0.052, 0.050, 0.057),
    (1, 250) => (0.515, 0.484, 0.487, 0.029, 0.774, 0.055, 0.041, 0.063),
    (1, 500) => (0.845, 0.833, 0.835, 0.024, 0.966, 0.057, 0.045, 0.064),
    (1, 1000) => (0.993, 0.992, 0.992, 0.027, 1.000, 0.053, 0.044, 0.065),
    (2, 100) => (0.235, 0.218, 0.217, 0.049, 0.516, 0.052, 0.042, 0.091),
    (2, 250) => (0.578, 0.565, 0.566, 0.042, 0.874, 0.052, 0.041, 0.146),
    (2, 500) => (0.912, 0.909, 0.909, 0.044, 0.992, 0.053, 0.041, 0.236),
    (2, 1000) => (0.999, 0.999, 0.999, 0.044, 1.000, 0.054, 0.041, 0.426),
    (3, 100) => (0.223, 0.204, 0.203, 0.050, 0.480, 0.057, 0.046, 0.087),
    (3, 250) => (0.528, 0.513, 0.513, 0.048, 0.846, 0.056, 0.044, 0.130),
    (3, 500) => (0.879, 0.874, 0.874, 0.046, 0.988, 0.053, 0.044, 0.209),
    (3, 1000) => (0.998, 0.998, 0.998, 0.046, 1.000, 0.055, 0.045, 0.375),
    (4, 100) => (0.210, 0.195, 0.193, 0.057, 0.472, 0.057, 0.043, 0.086),
    (4, 250) => (0.507, 0.496, 0.495, 0.053, 0.854, 0.052, 0.041, 0.135),
    (4, 500) => (0.869, 0.865, 0.865, 0.051, 0.990, 0.053, 0.042, 0.222),
    (4, 1000) => (0.998, 0.998, 0.998, 0.051, 1.000, 0.052, 0.043, 0.402),
    (5, 100) => (0.210, 0.194, 0.192, 0.064, 0.449, 0.059, 0.049, 0.081),
    (5, 250) => (0.477, 0.465, 0.465, 0.058, 0.831, 0.056, 0.048, 0.126),
    (5, 500) => (0.838, 0.834, 0.834, 0.056, 0.988, 0.054, 0.046, 0.204),
    (5, 1000) => (0.996, 0.996, 0.996, 0.054, 1.000, 0.052, 0.046, 0.372),
)

cmp = DataFrame(w=Int[], T=Int[], statistic=String[], paper=Float64[],
    replicated=Float64[], dev=Float64[])
for w in ws, T in Ts, (j, nm) in enumerate(names)
    r = round(res[T][j, w], digits=3)
    push!(cmp, (w, T, nm, published[(w, T)][j], r, round(r - published[(w, T)][j], digits=3)))
end
cmp
160×6 DataFrame
135 rows omitted
Row w T statistic paper replicated dev
Int64 Int64 String Float64 Float64 Float64
1 1 100 H 0.222 0.214 -0.008
2 1 100 Hex 0.19 0.179 -0.011
3 1 100 Δ² 0.189 0.178 -0.011
4 1 100 β 0.024 0.02 -0.004
5 1 100 τ 0.436 0.44 0.004
6 1 100 γ 0.052 0.05 -0.002
7 1 100 δ 0.05 0.048 -0.002
8 1 100 ACF 0.057 0.059 0.002
9 1 250 H 0.515 0.523 0.008
10 1 250 Hex 0.484 0.496 0.012
11 1 250 Δ² 0.487 0.498 0.011
12 1 250 β 0.029 0.027 -0.002
13 1 250 τ 0.774 0.777 0.003
⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮
149 5 500 τ 0.988 0.987 -0.001
150 5 500 γ 0.054 0.055 0.001
151 5 500 δ 0.046 0.046 0.0
152 5 500 ACF 0.204 0.208 0.004
153 5 1000 H 0.996 0.997 0.001
154 5 1000 Hex 0.996 0.997 0.001
155 5 1000 Δ² 0.996 0.997 0.001
156 5 1000 β 0.054 0.055 0.001
157 5 1000 τ 1.0 1.0 0.0
158 5 1000 γ 0.052 0.055 0.003
159 5 1000 δ 0.046 0.048 0.002
160 5 1000 ACF 0.372 0.378 0.006
largest absolute deviation: 0.014
mean absolute deviation:    0.003
Monte Carlo standard error: at most 0.005

References

Adämmer, Philipp, and Christian H Weiß. 2026. “Nonparametric Portmanteau Tests for Serial and Spatial Dependence.” Computational Statistics & Data Analysis. https://authors.elsevier.com/sd/article/S0167-9473(26)00136-2.
Weiß, Christian H. 2022. “Non-Parametric Tests for Serial Dependence in Time Series Based on Asymptotic Implementations of Ordinal-Pattern Statistics.” Chaos: An Interdisciplinary Journal of Nonlinear Science 32 (9). https://pubs.aip.org/aip/cha/article-abstract/32/9/093107/2835852/Non-parametric-tests-for-serial-dependence-in-time?redirectedFrom=fulltext.