AHD-CMA / tests /unit /test_phase_switch.py
Akiyue's picture
Add files using upload-large-folder tool
b2077dd verified
Raw
History Blame Contribute Delete
3.07 kB
"""Tests for the adaptive phase controller."""
from __future__ import annotations
import pytest
from ahdcma.controller.phase_switch import AdaptivePhaseController
def _config(**overrides: float | int) -> dict:
cfg: dict = {
"entropy_bins": 10,
"ruggedness_walk_length": 10,
"threshold_update_interval": 5,
"tau_high_offset": 0.5,
"tau_low_offset": -0.5,
"ruggedness_explore_thresh": 0.3,
"ruggedness_exploit_thresh": 0.6,
}
cfg.update(overrides)
return cfg
def test_pre_threshold_uses_ruggedness_only() -> None:
c = AdaptivePhaseController(_config())
# Before update_thresholds is called, tau bounds are infinite, so
# ruggedness alone decides.
assert c.select_mode(entropy=5.0, ruggedness=0.05, t=0) == "exploit"
assert c.select_mode(entropy=5.0, ruggedness=0.95, t=0) == "explore"
assert c.select_mode(entropy=5.0, ruggedness=0.45, t=0) == "hybrid"
def test_threshold_update_after_interval() -> None:
c = AdaptivePhaseController(_config(threshold_update_interval=5))
# Feed 20 generations of entropy ~ N(10, 2)
rng_vals = [
12.0,
8.0,
11.0,
9.0,
10.5,
9.5,
10.0,
11.5,
8.5,
10.0,
12.5,
7.5,
10.0,
9.0,
11.0,
9.5,
10.5,
10.0,
9.0,
11.0,
]
for t, h in enumerate(rng_vals):
# ruggedness fixed in the "hybrid" band so mode is dictated by entropy
c.select_mode(entropy=h, ruggedness=0.45, t=t)
# Now thresholds are real numbers
assert c.tau_high > c.tau_low
# A spike above tau_high → explore
very_high = c.tau_high + 5.0
assert c.select_mode(entropy=very_high, ruggedness=0.45, t=21) == "explore"
# A drop below tau_low → exploit
very_low = c.tau_low - 5.0
assert c.select_mode(entropy=very_low, ruggedness=0.45, t=22) == "exploit"
def test_high_ruggedness_overrides_hybrid_band() -> None:
c = AdaptivePhaseController(_config())
# 6 entropy values inside tight band → tau_high ≈ tau_low ≈ mean
for t, h in enumerate([5.0] * 6):
c.select_mode(entropy=h, ruggedness=0.45, t=t)
# Same entropy as the band, but very rugged → still explore
assert c.select_mode(entropy=5.0, ruggedness=0.95, t=7) in {"explore", "exploit"}
def test_invalid_offsets_rejected() -> None:
with pytest.raises(ValueError):
AdaptivePhaseController(_config(tau_high_offset=-1.0, tau_low_offset=0.5))
with pytest.raises(ValueError):
AdaptivePhaseController(
_config(ruggedness_explore_thresh=0.7, ruggedness_exploit_thresh=0.3)
)
def test_entropy_history_view() -> None:
c = AdaptivePhaseController(_config())
for t, h in enumerate([1.0, 2.0, 3.0]):
c.select_mode(entropy=h, ruggedness=0.45, t=t)
assert c.entropy_history == [1.0, 2.0, 3.0]
# The view should be a copy
c.entropy_history.append(99.0)
assert c.entropy_history == [1.0, 2.0, 3.0]