Files
Jyotisha/tests/test_rect_scoring_perf_20261013.py
T
jesse-uxandGrok (agent loop) 8e0503f9c0 Speed up rectification scoring without changing results.
Cache work that depends only on the candidate minute, drop unread response fields, and reject windows above 241 candidates before any chart is built. Eleven canonical scenario hashes match 77057a819. Sealed and reported-offset replays match the 2026-10-08 records (changed_trial_count 8 and 581) and are stored as scoring_perf_2026_10_13.

Co-Authored-By: Grok (agent loop)
2026-10-14 02:06:14 +08:00

362 lines
14 KiB
Python

"""Output-identical speedups for rectification scoring (BUG-1323, 1324, 1325, 1345)."""
from __future__ import annotations
import copy
import inspect
import time
from datetime import date, datetime, timedelta
from unittest.mock import patch
import pytest
from scripts.active_rectification_event_engine import (
_active_narayana,
build_candidate_static_context,
compute_event_candidate_rows,
)
from scripts.rectification.api_service import (
MAX_CANDIDATES_PER_REQUEST,
TooManyCandidates,
_candidate_count,
_reject_if_too_many_candidates,
block_scan,
diagnostics,
score_candidates,
)
from scripts.rectification.contracts import normalize_rectification_request
from scripts.rectification.dasha_transition_proximity import merge_transition_proximity
from scripts.rectification.event_probes import guided_collect_windows
from scripts.rectification.scoring_service import build_event_contribution_matrix, sha256
import scripts.active_rectification_event_engine as event_engine
from scripts.rectification import dasha_transition_proximity as proximity
from scripts.rectification import event_probes
from scripts.rectification import scoring_service
from tests._baseline_rect_scoring_20261013 import (
guided_collect_windows as baseline_guided_collect_windows,
merge_transition_proximity as baseline_merge_transition_proximity,
)
FROZEN_TODAY = date(2026, 9, 16)
EVENT_SPECS = (
("education", "education_start", "2008-01-01", "2008-12-31", "year"),
("career", "career_entry", "2012-06-01", "2012-06-30", "month"),
("relocation", "relocation", "2014-03-01", "2014-03-31", "month"),
("relationship", "relationship_commitment", "2016-10-01", "2016-10-31", "month"),
("career", "promotion", "2018-01-01", "2018-12-31", "year"),
("finance", "asset_change", "2019-05-01", "2019-05-31", "month"),
("family", "family_event", "2020-08-01", "2020-08-31", "month"),
("health", "self_health_event", "2022-01-01", "2022-12-31", "year"),
)
class _FrozenDate(date):
@classmethod
def today(cls) -> date:
return FROZEN_TODAY
def _events() -> list[dict]:
rows = []
for index, (domain, kind, start, end, precision) in enumerate(EVENT_SPECS, start=1):
rows.append({
"id": f"00000000-0000-4000-8000-{index:012d}",
"domain": domain,
"event_kind": kind,
"date_start": start,
"date_end": end,
"precision": precision,
"summary": "虚构",
})
return rows
def _request(start: str, end: str, **extra) -> dict:
body = {
"birth_date": "1990-01-01",
"start_time": start,
"end_time": end,
"lat": 39.9,
"lon": 116.4,
"tz": 8,
"events": _events(),
**extra,
}
return normalize_rectification_request(body, today=FROZEN_TODAY)
def _count(monkeypatch, owner, name: str) -> list[int]:
original = getattr(owner, name)
counter = [0]
def wrapped(*args, **kwargs):
counter[0] += 1
return original(*args, **kwargs)
monkeypatch.setattr(owner, name, wrapped)
return counter
@pytest.fixture(scope="module")
def wide_case():
request = _request("11:00", "13:00")
captured: dict = {}
original = scoring_service.merge_transition_proximity
def grab(matrix, events, contexts, birth_date, *, public_technique_layers):
captured["matrix"] = copy.deepcopy(matrix)
captured["events"] = list(events)
captured["contexts"] = contexts
captured["birth_date"] = birth_date
captured["layers"] = public_technique_layers
return original(
matrix, events, contexts, birth_date,
public_technique_layers=public_technique_layers,
)
with patch.object(event_probes, "date", _FrozenDate), patch.object(
scoring_service, "merge_transition_proximity", grab,
):
built = build_event_contribution_matrix(request)
return request, built, captured
def test_guided_collect_windows_matches_baseline_and_is_linear(wide_case, monkeypatch) -> None:
request, built, _captured = wide_case
times = list(built["candidate_times"])
assert len(times) == 121
variants = [
(request, times),
(request, times[::2]),
(request, times[:3]),
({**request, "declined_domains": ["relationship"]}, times),
]
with patch.object(event_probes, "date", _FrozenDate):
for variant_request, variant_times in variants:
assert guided_collect_windows(
variant_request, built, candidate_times=variant_times, today=FROZEN_TODAY,
) == baseline_guided_collect_windows(
variant_request, built, candidate_times=variant_times, today=FROZEN_TODAY,
)
short_built = {
**built,
"static_contexts": list(built["static_contexts"])[:21],
"candidate_times": times[:21],
}
full_calls = _count(monkeypatch, event_probes, "_context_time")
guided_collect_windows(request, built, candidate_times=times, today=FROZEN_TODAY)
full = full_calls[0]
short_calls = _count(monkeypatch, event_probes, "_context_time")
guided_collect_windows(request, short_built, candidate_times=times[:21], today=FROZEN_TODAY)
short = short_calls[0]
assert full <= 16 * 121
assert short <= 16 * 21
assert full / short <= (121 / 21) * 1.2
def test_merge_transition_proximity_matches_baseline(wide_case, monkeypatch) -> None:
_request_wide, _built, captured = wide_case
vim_calls = _count(monkeypatch, proximity, "_vim_start_dates")
nara_calls = _count(monkeypatch, proximity, "_narayana_start_dates")
fresh = copy.deepcopy(captured["matrix"])
base = copy.deepcopy(captured["matrix"])
merge_transition_proximity(
fresh, captured["events"], captured["contexts"], captured["birth_date"],
public_technique_layers=captured["layers"],
)
baseline_merge_transition_proximity(
base, captured["events"], captured["contexts"], captured["birth_date"],
public_technique_layers=captured["layers"],
)
assert fresh == base
assert vim_calls[0] <= 2 * len(captured["contexts"])
assert nara_calls[0] <= len(captured["contexts"])
midnight = _request("23:20", "00:40")
midnight_captured: dict = {}
original = scoring_service.merge_transition_proximity
def grab(matrix, events, contexts, birth_date, *, public_technique_layers):
midnight_captured["matrix"] = copy.deepcopy(matrix)
midnight_captured["events"] = list(events)
midnight_captured["contexts"] = contexts
midnight_captured["birth_date"] = birth_date
midnight_captured["layers"] = public_technique_layers
return original(matrix, events, contexts, birth_date, public_technique_layers=public_technique_layers)
with patch.object(event_probes, "date", _FrozenDate), patch.object(
scoring_service, "merge_transition_proximity", grab,
):
build_event_contribution_matrix(midnight)
fresh_night = copy.deepcopy(midnight_captured["matrix"])
base_night = copy.deepcopy(midnight_captured["matrix"])
merge_transition_proximity(
fresh_night, midnight_captured["events"], midnight_captured["contexts"], midnight_captured["birth_date"],
public_technique_layers=midnight_captured["layers"],
)
baseline_merge_transition_proximity(
base_night, midnight_captured["events"], midnight_captured["contexts"], midnight_captured["birth_date"],
public_technique_layers=midnight_captured["layers"],
)
assert fresh_night == base_night
def test_static_context_caches_match_fallback_and_stay_out_of_features(wide_case, monkeypatch) -> None:
request, built, _captured = wide_case
contexts = list(built["static_contexts"])
assert len(contexts) == 121
for context in contexts:
assert "functional_profile" not in context["feature"]
assert "narayana_antardasha_by_md" not in context["feature"]
assert context.get("functional_profile")
assert context.get("narayana_antardasha_by_md")
features = [context["feature"] for context in contexts]
cleared = []
for context in contexts:
clone = dict(context)
clone["functional_profile"] = None
clone["narayana_antardasha_by_md"] = None
cleared.append(clone)
engine_request = {
"birth_date": "1990-01-01",
"start_time": "11:00",
"end_time": "13:00",
"lat": 39.9,
"lon": 116.4,
"tz": 8,
"events": [{
"id": "00000000-0000-4000-8000-000000000002",
"domain": "career",
"event_kind": "career_entry",
"date": "2012-06-15",
"precision": "day",
"summary": "虚构",
}],
}
assert compute_event_candidate_rows(engine_request, static_contexts=contexts) == compute_event_candidate_rows(
engine_request, static_contexts=cleared,
)
assert sha256(features) == sha256([dict(feature) for feature in features])
functional_calls = _count(
monkeypatch, event_engine.functional_benefics, "derive_functional_benefic_malefic",
)
narayana_calls = [0]
original = event_engine.narayana_dasha.get_current_narayana_dasha
def wrapped(*args, **kwargs):
caller = inspect.stack()[1].filename.replace("\\", "/")
names = [frame.function for frame in inspect.stack()]
if caller.endswith("/active_rectification_event_engine.py") and "_candidate_row" in names:
narayana_calls[0] += 1
return original(*args, **kwargs)
monkeypatch.setattr(event_engine.narayana_dasha, "get_current_narayana_dasha", wrapped)
with patch.object(event_probes, "date", _FrozenDate):
scored = score_candidates(request, include_internal=True)
functional_during_score = functional_calls[0]
compact = score_candidates(request)
assert functional_during_score == 121
# Probe path (_score_year) is out of scope and still calls get_current.
# The scoring loop (_candidate_row) must not.
assert narayana_calls[0] == 0
assert "event_contribution_matrix" in scored
assert "candidate_decision_receipt" in scored
assert "event_contribution_matrix" not in compact
assert "candidate_decision_receipt" not in compact
def without_timing(value):
if isinstance(value, dict):
return {
key: without_timing(item)
for key, item in value.items()
if key not in {"column_compare_ms", "event_contribution_matrix", "candidate_decision_receipt"}
}
if isinstance(value, list):
return [without_timing(item) for item in value]
return value
assert without_timing(compact) == without_timing(scored)
assert len(scored["candidate_feature_snapshot"]["features"]) == 121
assert scored["candidate_feature_snapshot"]["feature_hash"] == compact["candidate_feature_snapshot"]["feature_hash"]
def test_cached_narayana_matches_live_lookup_across_ages(wide_case) -> None:
_request, built, _captured = wide_case
contexts = list(built["static_contexts"])[:3]
assert len(contexts) == 3
for context in contexts:
periods = context["narayana_periods"]
table = context["narayana_antardasha_by_md"]
birth = context["candidate_at"]
planets = context["planet_longitudes"]
asc = context["ascendant_index"]
def at_age(age: float) -> datetime:
return birth + timedelta(seconds=age * 365.2425 * 86400)
ages = [index / 100 for index in range(0, 12001)]
total = sum(period["years"] for period in periods)
cycle_start = min(float(period.get("start_age", 0)) for period in periods)
edges = [cycle_start, cycle_start + total]
edges.extend(float(period["start_age"]) for period in periods)
edges.extend(float(period["end_age"]) for period in periods)
for edge in edges:
for delta in (-1e-9, 0.0, 1e-9):
if edge + delta >= 0:
ages.append(edge + delta)
for age in ages:
event_at = at_age(age)
cached = _active_narayana(asc, planets, birth, event_at, periods, table)
live = _active_narayana(asc, planets, birth, event_at, periods, None)
assert cached == live
def test_candidate_cap_rejects_before_charts_and_allows_product_windows() -> None:
assert _candidate_count(_request("00:00", "03:59")) == 240
assert _candidate_count(_request("00:00", "04:00")) == 241
assert _candidate_count(_request("00:00", "04:01")) == 242
_reject_if_too_many_candidates(_request("00:00", "03:59"))
_reject_if_too_many_candidates(_request("00:00", "04:00"))
with pytest.raises(TooManyCandidates) as rejected:
_reject_if_too_many_candidates(_request("00:00", "04:01"))
assert rejected.value.code == "too_many_candidates"
assert rejected.value.limit == MAX_CANDIDATES_PER_REQUEST == 241
assert "count=242" in str(rejected.value)
full_day = _request("00:00", "23:59")
assert _candidate_count(full_day) == 1440
started = time.perf_counter()
with pytest.raises(TooManyCandidates) as full:
score_candidates(full_day)
assert time.perf_counter() - started < 0.05
assert "count=1440" in str(full.value)
with pytest.raises(TooManyCandidates):
diagnostics(full_day)
step_10 = _request("00:00", "23:59", minute_step=10)
assert _candidate_count(step_10) == 144
_reject_if_too_many_candidates(step_10)
step_2 = _request("00:00", "03:00", minute_step=2)
step_5 = _request("00:00", "06:00", minute_step=5)
assert _candidate_count(step_2) <= 91
assert _candidate_count(step_5) <= 121
_reject_if_too_many_candidates(step_2)
_reject_if_too_many_candidates(step_5)
with patch(
"scripts.rectification.api_service.score_candidates",
return_value={"candidate_scores": [], "decision_receipt": {}},
) as scored:
block_scan(step_10)
block_scan(step_2)
block_scan(step_5)
assert scored.call_count == 3
def test_one_candidate_context_exposes_top_level_caches() -> None:
request = _request("12:00", "12:00")
context = build_candidate_static_context(request, datetime(1990, 1, 1, 12, 0))
assert "functional_profile" not in context["feature"]
assert "narayana_antardasha_by_md" not in context["feature"]
assert context["functional_profile"]["functional_benefics"] or context["functional_profile"]["functional_malefics"]
assert set(context["narayana_antardasha_by_md"]) == {period["sign_idx"] for period in context["narayana_periods"]}