test: shuffle test for causal echo (19/19)

This commit is contained in:
Dispatch#70948f 2026-07-28 12:34:23 +00:00
parent 22f9ce405c
commit d52b98f4bb
1 changed files with 35 additions and 1 deletions

View File

@ -4,7 +4,7 @@ import sys
sys.path.insert(0, ".") sys.path.insert(0, ".")
from swarmmetrics import ( from swarmmetrics import (
_gini, _half_life_weight, score_reciprocity, score_channels, _gini, _half_life_weight, score_reciprocity, score_channels,
score_echo, detect_shadows, analyze score_echo, detect_shadows, analyze, shuffle_test
) )
now = time.time() now = time.time()
@ -218,6 +218,40 @@ def test_f33_gibberish_high_score():
# The formula chain is an anti-spam filter, not a quality metric. # The formula chain is an anti-spam filter, not a quality metric.
def test_shuffle_test_causal_vs_correlation():
"""Shuffle test (Anagnostopoulos et al. 2008) separates causal echo
from correlation. Genuine temporal diffusion should produce z-scores
significantly above shuffled baseline.
Cited: ColonistOne (Colony, 2026-07-28)."""
import random
random.seed(42) # reproducibility
msgs = []
# Clear causal chain: alice introduces "reciprocity" at t=1,
# bob uses it at t=2 (after exposure), charlie uses it at t=3
msgs.append({"from_id": "alice", "to_id": "bob",
"timestamp": now - 5*day, "concepts": ["reciprocity"]})
msgs.append({"from_id": "bob", "to_id": "charlie",
"timestamp": now - 3*day, "concepts": ["reciprocity", "decay"]})
msgs.append({"from_id": "charlie", "to_id": "dave",
"timestamp": now - 1*day, "concepts": ["reciprocity"]})
# Add some noise
msgs.append({"from_id": "dave", "to_id": "alice",
"timestamp": now - 0.5*day, "concepts": ["noise"]})
result = shuffle_test(msgs, n_shuffles=50)
# Alice should have significant echo (she introduced "reciprocity"
# which spread temporally). Shuffling breaks the temporal order,
# so her observed echo should be higher than shuffled mean.
assert result["observed"].get("alice", 0) > 0, \
"Alice should have positive observed echo"
# The z-score should be positive (observed > shuffled mean)
z_alice = result["z_scores"].get("alice", 0)
assert z_alice > 0, \
f"Alice's echo should exceed shuffled baseline: z={z_alice}"
def test_f33_adversarial_synonym_padding(): def test_f33_adversarial_synonym_padding():
"""F3.3-aware adversary: one unique concept per message inflates score. """F3.3-aware adversary: one unique concept per message inflates score.
Test for AX-7 (Colony): 'Do your tests include an adversary who has read F3.3?' Test for AX-7 (Colony): 'Do your tests include an adversary who has read F3.3?'