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 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DbBXoyeZrYn6ryNcSe5Y2Y
This commit is contained in:
2026-11-04 17:02:37 +08:00
co-authored by Claude Opus 5.5
parent 8f15745ca1
commit a4b5985200
3 changed files with 65 additions and 11 deletions
@@ -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'])
@@ -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):
+27
View File
@@ -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"