From a4b59852006bda928f4226ef5570b80f452828d7 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 4 Nov 2026 17:02:37 +0800 Subject: [PATCH] fix(benchmark): pass the ayanamsa to the Arudha and Chara Dasha compares run_arudha_compare.py and run_chara_dasha_compare.py still set PyJHora to Lahiri while the engine default is Raman (pre_work_error_ledger 2026-11-04). Both now take --ayanamsa / --node-mode, set both on PyJHora, and refuse a local baseline built with other settings (baseline_settings_error, as in run_pyjhora_compare.py). run_chara_dasha_compare.py imported jaimini from benchmarks/scripts (parents[2]); it now uses the repo scripts/. Raman rerun on the 19 fixed samples: A10 vs independent formula 114/114 fields, vs PyJHora 18/19 (one Scorpio/Aquarius co-lord case); Chara Dasha KN Rao sign sequence 228/228, durations 212/228. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01DbBXoyeZrYn6ryNcSe5Y2Y --- .../jyotish/scripts/run_arudha_compare.py | 24 +++++++++++++---- .../scripts/run_chara_dasha_compare.py | 25 ++++++++++++----- tests/test_pyjhora_compare_cli.py | 27 +++++++++++++++++++ 3 files changed, 65 insertions(+), 11 deletions(-) diff --git a/benchmarks/jyotish/scripts/run_arudha_compare.py b/benchmarks/jyotish/scripts/run_arudha_compare.py index d5968a5a..7fce60a1 100644 --- a/benchmarks/jyotish/scripts/run_arudha_compare.py +++ b/benchmarks/jyotish/scripts/run_arudha_compare.py @@ -9,6 +9,7 @@ Compares local Jyotish skill A10 canonical output against an independent local f and PyJHora's bhava_arudhas_from_planet_positions() sign output. Samples are fictional/public smoke cases only. """ +import argparse import csv import json import sys @@ -86,7 +87,7 @@ def patch_swisseph_for_pyjhora(): swe.houses_ex = houses_ex -def pyjhora_a10_sign(sample): +def pyjhora_a10_sign(sample, *, ayanamsa='lahiri', node_mode='mean'): if str(PYJHORA_COMPAT) not in sys.path: sys.path.insert(0, str(PYJHORA_COMPAT)) if str(PYJHORA_SITE) not in sys.path: @@ -95,8 +96,12 @@ def pyjhora_a10_sign(sample): from jhora import utils, const from jhora.panchanga import drik from jhora.horoscope.chart import charts, arudhas - const._DEFAULT_AYANAMSA_MODE = 'LAHIRI' - drik.set_ayanamsa_mode('LAHIRI') + # Same ayanamsa and node mode as the local baseline (pre_work_error_ledger 2026-11-04: + # this was hard-coded to Lahiri while the engine default had become Raman). + const._DEFAULT_AYANAMSA_MODE = ayanamsa.upper() + drik.set_ayanamsa_mode(ayanamsa.upper()) + const.set_node_mode(node_mode == 'true') + drik.set_planet_list(set_rahu_ketu_as_true_nodes=(node_mode == 'true')) b = sample['birth'] jd = utils.julian_day_number((b['year'], b['month'], b['day']), (b['hour'], b['minute'], 0)) place = drik.Place(sample['label'], b['lat'], b['lon'], b['tz']) @@ -165,15 +170,24 @@ def write_report(rows): return '\n'.join(lines) -def main(): +def main(argv=None): + parser = argparse.ArgumentParser(description='Compare the local A10 against an independent formula and PyJHora.') + parser.add_argument('--ayanamsa', choices=['lahiri', 'raman'], default='lahiri', help='Ayanamsa for PyJHora; the local baseline must have been built with the same one.') + parser.add_argument('--node-mode', choices=['mean', 'true'], default='mean', help='Node mode for PyJHora; must match the local baseline.') + args = parser.parse_args(argv) + sys.path.insert(0, str(Path(__file__).resolve().parent)) + from run_pyjhora_compare import baseline_settings_error samples = json.loads(DATA.read_text()) rows = [] for sample in samples: sid = sample['id'] canon = local_canonical(sid) + settings_error = baseline_settings_error(canon, ayanamsa=args.ayanamsa, node_mode=args.node_mode) + if settings_error: + parser.error(f'{sid}: {settings_error}; rebuild with run_skill_baseline.py') local_a10 = canon['advanced']['A10_Karma_Pada'] indep = independent_a10_from_canonical(canon) - pyj_sign = pyjhora_a10_sign(sample) + pyj_sign = pyjhora_a10_sign(sample, ayanamsa=args.ayanamsa, node_mode=args.node_mode) for field in ['sign', 'source_sign', 'source_lord_sign', 'distance_from_source', 'exception_applied']: compare(rows, sid, 'independent_formula', field, local_a10.get(field), indep.get(field)) compare(rows, sid, 'independent_formula', 'source_lord', SIGN_LORDS[local_a10['source_sign']], indep['source_lord']) diff --git a/benchmarks/jyotish/scripts/run_chara_dasha_compare.py b/benchmarks/jyotish/scripts/run_chara_dasha_compare.py index 088b5ec3..8fe8117c 100644 --- a/benchmarks/jyotish/scripts/run_chara_dasha_compare.py +++ b/benchmarks/jyotish/scripts/run_chara_dasha_compare.py @@ -10,6 +10,7 @@ PyJHora's KN Rao Chara Dasha over fictional/public smoke samples. This script is intended to identify whether the local module is production-grade or only a placeholder workflow component. """ +import argparse import csv import json import sys @@ -23,7 +24,7 @@ OUT = ROOT / 'outputs' CANON = OUT / 'canonical' REPORT = OUT / 'jyotish_benchmark_round7_chara_dasha_compare.md' MATRIX = OUT / 'chara_dasha_comparison_matrix.csv' -SKILL_SCRIPTS = Path(__file__).resolve().parents[2] / 'scripts' +SKILL_SCRIPTS = Path(__file__).resolve().parents[3] / 'scripts' # repo scripts/ (parents[2] was benchmarks/) PYJHORA_SITE = Path(__import__('os').environ.get('PYJHORA_SITE', '')) PYJHORA_COMPAT = ROOT / 'scripts/pyjhora_compat' @@ -71,7 +72,7 @@ def local_chara(sample, c): return [{'order': x['order'], 'sign': x['sign'], 'duration_years': float(x['duration_years'])} for x in d['dasha_sequence']] -def pyjhora_chara(sample): +def pyjhora_chara(sample, *, ayanamsa='lahiri', node_mode='mean'): if str(PYJHORA_COMPAT) not in sys.path: sys.path.insert(0, str(PYJHORA_COMPAT)) if str(PYJHORA_SITE) not in sys.path: @@ -80,8 +81,11 @@ def pyjhora_chara(sample): from jhora import const from jhora.panchanga import drik from jhora.horoscope.dhasa.raasi import chara - const._DEFAULT_AYANAMSA_MODE = 'LAHIRI' - drik.set_ayanamsa_mode('LAHIRI') + # Same ayanamsa and node mode as the local baseline (pre_work_error_ledger 2026-11-04). + const._DEFAULT_AYANAMSA_MODE = ayanamsa.upper() + drik.set_ayanamsa_mode(ayanamsa.upper()) + const.set_node_mode(node_mode == 'true') + drik.set_planet_list(set_rahu_ketu_as_true_nodes=(node_mode == 'true')) b = sample['birth'] dob = (b['year'], b['month'], b['day']) tob = (b['hour'], b['minute'], 0) @@ -161,15 +165,24 @@ def write_report(rows, per_sample): return '\n'.join(lines) -def main(): +def main(argv=None): + parser = argparse.ArgumentParser(description='Compare the local Chara Dasha against PyJHora KN Rao.') + parser.add_argument('--ayanamsa', choices=['lahiri', 'raman'], default='lahiri', help='Ayanamsa for PyJHora; the local baseline must have been built with the same one.') + parser.add_argument('--node-mode', choices=['mean', 'true'], default='mean', help='Node mode for PyJHora; must match the local baseline.') + args = parser.parse_args(argv) + sys.path.insert(0, str(Path(__file__).resolve().parent)) + from run_pyjhora_compare import baseline_settings_error samples = json.loads(DATA.read_text()) rows = [] per_sample = [] for sample in samples: sid = sample['id'] c = canon(sid) + settings_error = baseline_settings_error(c, ayanamsa=args.ayanamsa, node_mode=args.node_mode) + if settings_error: + parser.error(f'{sid}: {settings_error}; rebuild with run_skill_baseline.py') local = local_chara(sample, c) - pyj = pyjhora_chara(sample) + pyj = pyjhora_chara(sample, ayanamsa=args.ayanamsa, node_mode=args.node_mode) sign_match = 0 duration_match = 0 for i in range(12): diff --git a/tests/test_pyjhora_compare_cli.py b/tests/test_pyjhora_compare_cli.py index b80163b3..908676c2 100644 --- a/tests/test_pyjhora_compare_cli.py +++ b/tests/test_pyjhora_compare_cli.py @@ -107,3 +107,30 @@ def test_fixed_samples_include_the_public_rodden_aa_backtest_figures(): for figure in ["steve_jobs", "barack_obama", "elizabeth_taylor", "marilyn_monroe", "judy_garland", "edith_piaf", "frida_kahlo", "george_w_bush", "zinedine_zidane"]: assert figure in ids assert all(sample.get("privacy") in {"fictional_or_public_test", "public_rodden_aa"} for sample in samples) + + +def test_arudha_and_chara_compares_take_the_ayanamsa_and_refuse_a_mismatched_baseline(monkeypatch, tmp_path): + # Data audit wave 2 (2026-11-04): both scripts still hard-coded Lahiri on the PyJHora side. + import json + + from benchmarks.jyotish.scripts import run_arudha_compare as arudha + from benchmarks.jyotish.scripts import run_chara_dasha_compare as chara + + for module in (arudha, chara): + source = Path(module.__file__).read_text() + assert "set_ayanamsa_mode('LAHIRI')" not in source + assert "drik.set_ayanamsa_mode(ayanamsa.upper())" in source + canon = tmp_path / module.__name__ + canon.mkdir() + sample = json.loads(module.DATA.read_text())[0] + (canon / f"{sample['id']}.canonical.json").write_text(json.dumps({"parameters": {"ayanamsa": "raman", "node_mode": "mean"}})) + monkeypatch.setattr(module, "CANON", canon) + monkeypatch.setattr(module, "DATA", canon / "samples.json") + (canon / "samples.json").write_text(json.dumps([sample])) + try: + module.main(["--ayanamsa", "lahiri"]) + except SystemExit as exc: + assert exc.code == 2 + else: + raise AssertionError(f"{module.__name__} compared a raman baseline under lahiri") + assert chara.SKILL_SCRIPTS == ROOT / "scripts"