/ proof-of-concept / timeseries-entropy
Sign in
proof-of-concept / timeseries-entropy
timeseries-entropy / tests / test_estimator.py
57 lines · 1.9 KBBlameHistoryRaw
1import math
3import numpy as np
4import pytest
6from timeseries_entropy import plugin_entropy, unbiased_entropy, level_corrections
9def test_plugin_entropy_exact():
10 assert plugin_entropy([0, 1]) == pytest.approx(1.0)
11 assert plugin_entropy([3, 3, 3]) == pytest.approx(0.0)
12 assert plugin_entropy([0, 0, 0, 1]) == pytest.approx(
13 -(0.75 * math.log2(0.75) + 0.25 * math.log2(0.25)))
16def test_unbiased_on_iid_categorical():
17 p = np.array([0.5, 0.3, 0.2])
18 true_h = -np.sum(p * np.log2(p))
19 rng = np.random.default_rng(1)
20 draw = lambda k: rng.choice(3, size=k, p=p)
21 vals = np.array([unbiased_entropy(draw, n0=32, r=1.5, rng=rng)
22 for _ in range(4000)])
23 se = vals.std(ddof=1) / math.sqrt(len(vals))
24 assert abs(vals.mean() - true_h) < 5 * se
25 assert se < 0.01
28def test_unbiased_on_autocorrelated_chain():
29 # Stationary two-state Markov chain with sticky transitions; the marginal
30 # is uniform, so H = 1 bit despite strong autocorrelation.
31 rng = np.random.default_rng(2)
32 state = [int(rng.random() < 0.5)]
34 def draw(k):
35 out = np.empty(k, dtype=int)
36 for i in range(k):
37 if rng.random() < 0.1:
38 state[0] = 1 - state[0]
39 out[i] = state[0]
40 return out
42 vals = np.array([unbiased_entropy(draw, n0=64, r=1.5, rng=rng)
43 for _ in range(3000)])
44 se = vals.std(ddof=1) / math.sqrt(len(vals))
45 assert abs(vals.mean() - 1.0) < 5 * se
46 assert se < 0.02
49def test_level_corrections_decay():
50 rng = np.random.default_rng(3)
51 p = np.array([0.5, 0.3, 0.2])
52 deltas = np.array([
53 level_corrections(lambda k: rng.choice(3, size=k, p=p), n0=64, levels=5)
54 for _ in range(200)])
55 rms = np.sqrt((deltas ** 2).mean(axis=0))
56 # E[Delta_m^2] should decay roughly like 2^(-2m); demand clear decay.
57 assert rms[-1] < rms[0] / 4
↑↓ move↵openescclose