Files
stonks-oracle/tests/test_pbt_v3_layers.py
T

173 lines
5.7 KiB
Python

"""Property-based tests for v3 macro and competitive layers.
Validates:
- Property 13: Noisy-OR normalized exposure is bounded in [0, 1]
- Property 14: Competitive LLR is clamped to [-1.25, 1.25]
- Property 15: Graph attenuation is zero beyond max distance
Requirements: 9.1, 9.2, 10.3, 10.4, 10.5
"""
from __future__ import annotations
from hypothesis import given, settings
from hypothesis import strategies as st
from services.aggregation.interpolation import (
compute_normalized_macro_exposure,
)
from services.aggregation.signal_propagation import (
compute_competitive_llr,
compute_shrunk_correlation,
)
# ---------------------------------------------------------------------------
# Strategies
# ---------------------------------------------------------------------------
unit_floats = st.floats(min_value=0.0, max_value=1.0, allow_nan=False, allow_infinity=False)
llr_source_values = st.floats(min_value=-5.0, max_value=5.0, allow_nan=False, allow_infinity=False)
distances_valid = st.integers(min_value=1, max_value=3)
distances_beyond = st.integers(min_value=4, max_value=10)
# ---------------------------------------------------------------------------
# Feature: math-core-v3-engine, Property 13: Noisy-OR normalized exposure
# is bounded in [0, 1]
# ---------------------------------------------------------------------------
# **Validates: Requirements 9.1, 9.2**
@settings(max_examples=100)
@given(
geo=unit_floats,
supply=unit_floats,
commodity=unit_floats,
sector=unit_floats,
)
def test_property_13_noisy_or_exposure_bounded(
geo: float,
supply: float,
commodity: float,
sector: float,
) -> None:
"""Property 13: Noisy-OR normalized exposure is bounded in [0, 1].
For any overlap values O_k in [0, 1] for each dimension (geo, supply,
commodity, sector) with fixed positive weights, the normalized macro
exposure E_macro SHALL be in [0.0, 1.0], reaching exactly 1.0 when
all O_k = 1.0. (Use tier="regional" with dampener=1.0 for this
property test)
"""
overlaps = {
"geo": geo,
"supply": supply,
"commodity": commodity,
"sector": sector,
}
e_macro = compute_normalized_macro_exposure(overlaps, tier="regional")
# Must be bounded in [0, 1] with regional dampener = 1.0
assert 0.0 <= e_macro <= 1.0, (
f"E_macro={e_macro} out of bounds [0, 1] for overlaps={overlaps}"
)
@settings(max_examples=1)
@given(st.just(None))
def test_property_13_max_exposure_is_one(_: None) -> None:
"""Property 13 (edge): E_macro reaches exactly 1.0 when all O_k = 1.0."""
overlaps = {"geo": 1.0, "supply": 1.0, "commodity": 1.0, "sector": 1.0}
e_macro = compute_normalized_macro_exposure(overlaps, tier="regional")
assert e_macro == 1.0, f"Expected 1.0 when all overlaps=1.0, got {e_macro}"
# ---------------------------------------------------------------------------
# Feature: math-core-v3-engine, Property 14: Competitive LLR is clamped
# to [-1.25, 1.25]
# ---------------------------------------------------------------------------
# **Validates: Requirements 10.3, 10.4, 10.5**
@settings(max_examples=100)
@given(
llr_source=llr_source_values,
rho_rolling=unit_floats,
n_observations=st.integers(min_value=1, max_value=500),
same_sector=st.booleans(),
d_network=distances_valid,
pattern_confidence=unit_floats,
)
def test_property_14_competitive_llr_clamped(
llr_source: float,
rho_rolling: float,
n_observations: int,
same_sector: bool,
d_network: int,
pattern_confidence: float,
) -> None:
"""Property 14: Competitive LLR is clamped to [-1.25, 1.25].
For any source LLR, shrunk correlation (non-negative), graph distance
(1-3), and pattern confidence in [0,1], the computed competitive LLR
SHALL be in [-1.25, 1.25].
"""
rho_effective = compute_shrunk_correlation(
rho_rolling=rho_rolling,
n_observations=n_observations,
same_sector=same_sector,
)
# rho_effective should be non-negative per definition
assert rho_effective >= 0.0, f"rho_effective={rho_effective} is negative"
llr_competitive = compute_competitive_llr(
llr_source=llr_source,
rho_effective=rho_effective,
d_network=d_network,
pattern_confidence=pattern_confidence,
)
assert -1.25 <= llr_competitive <= 1.25, (
f"Competitive LLR={llr_competitive} out of bounds [-1.25, 1.25] "
f"for llr_source={llr_source}, rho_effective={rho_effective}, "
f"d_network={d_network}, pattern_confidence={pattern_confidence}"
)
# ---------------------------------------------------------------------------
# Feature: math-core-v3-engine, Property 15: Graph attenuation is zero
# beyond max distance
# ---------------------------------------------------------------------------
# **Validates: Requirements 10.4, 10.5**
@settings(max_examples=100)
@given(
llr_source=llr_source_values,
rho_effective=unit_floats,
d_network=distances_beyond,
pattern_confidence=unit_floats,
)
def test_property_15_zero_attenuation_beyond_max_distance(
llr_source: float,
rho_effective: float,
d_network: int,
pattern_confidence: float,
) -> None:
"""Property 15: Graph attenuation is zero beyond max distance.
For any inputs where graph distance > 3, the computed attenuation
SHALL be 0.0, producing zero competitive LLR regardless of other
parameters.
"""
llr_competitive = compute_competitive_llr(
llr_source=llr_source,
rho_effective=rho_effective,
d_network=d_network,
pattern_confidence=pattern_confidence,
)
assert llr_competitive == 0.0, (
f"Expected 0.0 for d_network={d_network} > 3, got {llr_competitive}"
)