Files
stonks-oracle/tests/test_v3_contradiction.py

177 lines
6.4 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Unit tests for v3 LLR entropy contradiction score.
Tests compute_v3_contradiction from services.aggregation.contradiction.
Requirements validated: 7.17.7
"""
from __future__ import annotations
import math
import pytest
from services.aggregation.contradiction import compute_v3_contradiction
from services.aggregation.worker import EvidenceCluster
def _make_cluster(cluster_llr: float) -> EvidenceCluster:
"""Helper to create a minimal EvidenceCluster with a given cluster_llr."""
return EvidenceCluster(
cluster_id="test", units=[], llrs=[], n_eff=1.0, cluster_llr=cluster_llr
)
# ---------------------------------------------------------------------------
# 1. All bullish (unidirectional positive) → contradiction = 0.0
# ---------------------------------------------------------------------------
class TestUnidirectionalPositive:
"""When all cluster LLRs are positive, there is no contradiction."""
def test_all_bullish(self):
clusters = [_make_cluster(1.0), _make_cluster(0.5), _make_cluster(0.8)]
assert compute_v3_contradiction(clusters) == 0.0
def test_single_bullish(self):
clusters = [_make_cluster(2.0)]
assert compute_v3_contradiction(clusters) == 0.0
# ---------------------------------------------------------------------------
# 2. All bearish (unidirectional negative) → contradiction = 0.0
# ---------------------------------------------------------------------------
class TestUnidirectionalNegative:
"""When all cluster LLRs are negative, there is no contradiction."""
def test_all_bearish(self):
clusters = [_make_cluster(-1.0), _make_cluster(-0.5)]
assert compute_v3_contradiction(clusters) == 0.0
def test_single_bearish(self):
clusters = [_make_cluster(-2.5)]
assert compute_v3_contradiction(clusters) == 0.0
# ---------------------------------------------------------------------------
# 3. Equal split → high contradiction near 1.0
# ---------------------------------------------------------------------------
class TestEqualSplit:
"""Equal positive and negative evidence produces maximum entropy (H=1.0),
modulated by the volume factor."""
def test_equal_split_moderate_mass(self):
"""LLR +2.0 and -2.0: E_pos=2, E_neg=2, E_total=4.
H_conflict = 1.0 (50/50 split).
volume_factor = 1 - exp(-4/3) ≈ 0.7364.
contradiction ≈ 0.7364.
"""
clusters = [_make_cluster(2.0), _make_cluster(-2.0)]
result = compute_v3_contradiction(clusters)
expected_volume = 1.0 - math.exp(-4.0 / 3.0)
expected = 1.0 * expected_volume # H_conflict = 1.0 for equal split
assert result == pytest.approx(expected, abs=1e-4)
def test_equal_split_large_mass(self):
"""LLR +5.0 and -5.0: E_total=10, volume_factor → ~0.964.
contradiction near 1.0.
"""
clusters = [_make_cluster(5.0), _make_cluster(-5.0)]
result = compute_v3_contradiction(clusters)
expected_volume = 1.0 - math.exp(-10.0 / 3.0)
expected = 1.0 * expected_volume
assert result == pytest.approx(expected, abs=1e-4)
assert result > 0.9 # Near 1.0 due to large evidence mass
# ---------------------------------------------------------------------------
# 4. E_total = 0 → contradiction = 0.0
# ---------------------------------------------------------------------------
class TestZeroEvidence:
"""When all cluster LLRs are zero, E_total=0, contradiction is 0."""
def test_all_zero_llrs(self):
clusters = [_make_cluster(0.0), _make_cluster(0.0), _make_cluster(0.0)]
assert compute_v3_contradiction(clusters) == 0.0
def test_single_zero_llr(self):
clusters = [_make_cluster(0.0)]
assert compute_v3_contradiction(clusters) == 0.0
# ---------------------------------------------------------------------------
# 5. Empty clusters → contradiction = 0.0
# ---------------------------------------------------------------------------
class TestEmptyClusters:
"""Empty cluster list returns 0.0."""
def test_empty_list(self):
assert compute_v3_contradiction([]) == 0.0
# ---------------------------------------------------------------------------
# 6. Small evidence mass → suppressed score (volume_factor effect)
# ---------------------------------------------------------------------------
class TestSmallEvidenceMass:
"""Small E_total leads to a small volume_factor that suppresses the score."""
def test_tiny_equal_split(self):
"""LLR +0.1 and -0.1: E_total=0.2.
H_conflict = 1.0 (equal split).
volume_factor = 1 - exp(-0.2/3) ≈ 0.0645.
contradiction ≈ 0.0645 (suppressed).
"""
clusters = [_make_cluster(0.1), _make_cluster(-0.1)]
result = compute_v3_contradiction(clusters)
expected_volume = 1.0 - math.exp(-0.2 / 3.0)
expected = 1.0 * expected_volume
assert result == pytest.approx(expected, abs=1e-4)
# Confirm suppression: score well below 0.1
assert result < 0.1
# ---------------------------------------------------------------------------
# 7. Large evidence mass → volume_factor approaches 1.0
# ---------------------------------------------------------------------------
class TestLargeEvidenceMass:
"""Large E_total pushes volume_factor near 1.0, so score ≈ H_conflict."""
def test_large_equal_split(self):
"""LLR +5.0 and -5.0: E_total=10, volume_factor ≈ 0.964.
contradiction ≈ 0.964 (near maximum).
"""
clusters = [_make_cluster(5.0), _make_cluster(-5.0)]
result = compute_v3_contradiction(clusters)
# Volume factor should be very close to 1.0
volume_factor = 1.0 - math.exp(-10.0 / 3.0)
assert volume_factor > 0.95
assert result > 0.95
def test_asymmetric_large_mass(self):
"""LLR +4.0 and -1.0: E_pos=4, E_neg=1, E_total=5.
f_pos=0.8, f_neg=0.2.
H_conflict = -0.8×log2(0.8) - 0.2×log2(0.2) ≈ 0.7219.
volume_factor = 1 - exp(-5/3) ≈ 0.8111.
contradiction ≈ 0.586.
"""
clusters = [_make_cluster(4.0), _make_cluster(-1.0)]
result = compute_v3_contradiction(clusters)
f_pos = 4.0 / 5.0
f_neg = 1.0 / 5.0
h_conflict = -f_pos * math.log2(f_pos) - f_neg * math.log2(f_neg)
volume_factor = 1.0 - math.exp(-5.0 / 3.0)
expected = h_conflict * volume_factor
assert result == pytest.approx(expected, abs=1e-4)