"""D12 parent-probe research helpers. Public charts only; no production edits.""" from __future__ import annotations from datetime import datetime from decimal import Decimal import pytest from scripts.research import d12_parent_probes_lib as lib BODIES = ("Sun", "Moon", "Mercury", "Venus", "Mars", "Jupiter", "Saturn", "Rahu", "Ketu") def signs(**updates: int) -> dict[str, int]: base = {name: 0 for name in BODIES} base.update(updates) return base def test_preregistration_locks_the_gate_numbers() -> None: prereg = lib.load_prereg() lib.assert_prereg_locked(prereg) births = lib.json.loads(lib.HOLDOUT_PATH.read_text(encoding="utf-8"))["cases"] digest = lib.case_id_digest([case["case_id"] for case in births]) assert digest == prereg["birth_records"]["case_id_sha256"] assert len(births) == 77 def test_house_lord_career_and_separation_follow_the_preregistration() -> None: assert lib.house_of(0, 0) == 1 assert lib.house_of(8, 0) == 9 assert lib.lord_of(0, 9) == "Jupiter" assert lib.lord_of(0, 4) == "Moon" assert lib.career_house(9) == 6 assert lib.career_house(10) == 7 assert lib.separated(0, 5) assert lib.separated(0, 1) assert not lib.separated(0, 0) assert not lib.separated(0, 2) def test_care_abstains_only_on_a_kendra_tie_and_financial_uses_house_2() -> None: abstain = lib.read_probes(signs(**{name: 1 for name in BODIES}), 0, 9) assert abstain["mother_more_daily_care"] == "abstain" assert abstain["father_financial_lead"] == "yes" study_no = lib.read_probes( signs(Sun=0, Moon=1, Jupiter=3, Mercury=4, Venus=4, Mars=4, Saturn=2, Rahu=4), 1, 9, ) assert study_no["father_guided_study"] == "no" institutional = lib.read_probes(signs(Saturn=5), 0, 9) assert institutional["father_work_institutional"] == "yes" def test_missing_rahu_does_not_invent_a_node() -> None: bare = {name: 0 for name in BODIES if name != "Rahu"} labels = lib.read_probes(bare, 0, 9) assert labels["parents_long_apart"] in {"yes", "no"} def test_nearest_cut_tie_break_and_true_side() -> None: signs_at = {-2: 1, -1: 1, 0: 1, 1: 2, 2: 2} assert lib.nearest_cut(lib.cuts(signs_at)) == (0, 1) tied = {-3: 0, -2: 1, -1: 1, 0: 1, 1: 1, 2: 2, 3: 2} assert lib.nearest_cut(lib.cuts(tied)) == (1, 2) mirror = {-3: 0, -2: 0, -1: 1, 0: 1, 1: 1, 2: 1, 3: 2} assert lib.nearest_cut(lib.cuts(mirror)) == (-2, -1) both = {-1: 0, 0: 1, 1: 2} assert lib.nearest_cut(lib.cuts(both)) == (0, 1) assert lib.true_side(signs_at, (0, 1)) == (0, 1) with pytest.raises(RuntimeError): lib.true_side({0: 1, 1: 1}, (0, 1)) def test_segment_representative_prefers_the_recorded_minute() -> None: assert lib.segment_representative([-2, -1, 0, 1]) == 0 assert lib.segment_representative([-4, -3]) == -3 assert lib.segment_representative([2, 3, 4]) == 2 def test_tie_is_not_a_selection_and_unclear_is_not_counted() -> None: answers = {probe: "yes" for probe in lib.PROBES} same = {probe: "yes" for probe in lib.PROBES} assert lib.side_score(same, answers) == 6 mixed = dict(same) mixed["parents_long_apart"] = "abstain" unclear = dict(answers) unclear["father_guided_study"] = "unclear" assert lib.side_score(mixed, unclear) == 4 assert lib.selection_count( ["a"], {"a": answers}, {"a": same}, {"a": same}, ) == 0 def test_fisher_yates_is_repeatable_and_shuffles_whole_vectors() -> None: first = lib.permutation_maps(["b", "a", "c"], seed=20261012, count=3) second = lib.permutation_maps(["b", "a", "c"], seed=20261012, count=3) assert first == second assert all(sorted(row) == ["a", "b", "c"] for row in first) assigned = lib.assign_answers( ["a", "b", "c"], ["c", "a", "b"], {"a": {"k": "yes"}, "b": {"k": "no"}, "c": {"k": "unclear"}}, ) assert assigned["a"]["k"] == "unclear" assert assigned["b"]["k"] == "yes" def test_window_selects_the_matching_side_and_the_head_segment() -> None: offsets = [-1, 0, 1] sign_at = {offset: 0 if offset < 1 else 1 for offset in offsets} true_labels = {probe: "yes" for probe in lib.PROBES} other_labels = {probe: "no" for probe in lib.PROBES} predictions = {-1: true_labels, 0: true_labels, 1: other_labels} answers = {probe: "yes" for probe in lib.PROBES} row = lib.case_window(sign_at, predictions, answers, 1) assert row["boundary"] is True assert row["true_offset"] == 0 assert row["other_offset"] == 1 assert row["selected"] is True assert row["head_hit"] is True assert row["retained"] is True planted = lib.plant_answers(true_labels) assert planted["mother_more_daily_care"] == "yes" assert lib.plant_answers({"mother_more_daily_care": "abstain", **{probe: "no" for probe in lib.PROBES if probe != "mother_more_daily_care"}})[ "mother_more_daily_care" ] == "unclear" def test_permutation_summary_uses_the_exact_ratio() -> None: summary = lib.permutation_summary(5, 10, [0, 1, 5, 6]) assert summary["extreme"] == 2 assert summary["p_numer"] == 3 assert summary["p_denom"] == 5 assert summary["above_null_mean"] is True assert summary["percentile_numer"] == 2 assert lib.ratio_below(12, 1000, lib.ALPHA) is True assert lib.ratio_below(13, 1000, lib.ALPHA) is False assert lib.ratio_below(199, 1000, lib.ROBUST_MAX) is True assert lib.ratio_below(200, 1000, lib.ROBUST_MAX) is False assert lib.cell_is_significant( {"above_null_mean": True, "p_numer": 12, "p_denom": 1000} ) is True assert lib.cell_is_significant( {"above_null_mean": True, "p_numer": 13, "p_denom": 1000} ) is False def test_positive_control_detects_a_planted_difference() -> None: sign_at = {offset: 0 if offset <= 0 else 1 for offset in range(-1, 2)} yes_labels = {probe: "yes" for probe in lib.PROBES} no_labels = {probe: "no" for probe in lib.PROBES} case_ids = [f"c{index:02d}" for index in range(20)] signs = {case_id: sign_at for case_id in case_ids} predicted = {} for index, case_id in enumerate(case_ids): if index % 2 == 0: predicted[case_id] = {-1: yes_labels, 0: yes_labels, 1: no_labels} else: predicted[case_id] = {-1: no_labels, 0: no_labels, 1: yes_labels} control = lib.positive_control_cell(case_ids, signs, predicted, 1) assert control["pipeline_exact"] is True assert control["differing"] == 20 assert control["summary"]["observed_numer"] == 20 assert control["p_below"] is True def test_decision_status_does_not_pass_a_small_or_blind_sample() -> None: assert lib.decide_status( included=77, control_exact=True, differing=30, control_p_below=True, any_robust_significant=True, ) == "pass" assert lib.decide_status( included=59, control_exact=True, differing=30, control_p_below=True, any_robust_significant=True, ) == "underpowered_sample" assert lib.decide_status( included=77, control_exact=True, differing=30, control_p_below=True, any_robust_significant=False, ) == "fail_closed" assert lib.decide_status( included=77, control_exact=True, differing=19, control_p_below=True, any_robust_significant=True, ) == "control_underpowered" assert lib.decide_status( included=77, control_exact=False, differing=30, control_p_below=True, any_robust_significant=True, ) == "control_invalid" def test_fact_row_requires_an_opened_https_source() -> None: row = { "answer": "unclear", "source_grade": "encyclopedia", "url": "https://en.wikipedia.org/wiki/Example", "excerpt": "The page names the parents and does not say who did the daily care.", } case = {"case_id": "example", "probes": {probe: dict(row) for probe in lib.PROBES}} answers = lib.validate_fact_case(case) assert lib.decisive_count(answers) == 0 broken = {"case_id": "example", "probes": {probe: dict(row) for probe in lib.PROBES}} broken["probes"]["parents_long_apart"]["url"] = "http://insecure.example" with pytest.raises(SystemExit): lib.validate_fact_case(broken) def test_change_count_and_canonical_bytes_are_stable() -> None: labels = {probe: "yes" for probe in lib.PROBES} flipped = dict(labels) flipped["parents_long_apart"] = "no" baseline = {"a": {0: labels, 1: labels}} alternate = {"a": {0: labels, 1: flipped}} assert lib.change_count(baseline, alternate, {"a": (0, 1)}) == 1 payload = {"b": 1, "a": {"d": 2, "c": 3}} assert lib.canonical_bytes(payload) == lib.canonical_bytes(payload) assert lib.canonical_bytes(payload).endswith(b"\n") assert Decimal("0.0125") == lib.ALPHA def test_production_chart_feeds_d12_without_a_second_ephemeris() -> None: from scripts.research.d12_parent_probes_run import placement_at birth = { "date": "2000-01-01", "time": "12:00", "latitude": 0.0, "longitude": 0.0, "timezone_offset": 0.0, } placement = placement_at(datetime(2000, 1, 1, 12, 0), birth, "raman", "mean") assert 0 <= placement.d12_lagna <= 11 assert set(lib.predict(placement, "D12", 9)) == set(lib.PROBES) assert set(lib.predict(placement, "D1", 9)) == set(lib.PROBES)