fix(benchmark): pass the ayanamsa to both engines and refuse a mismatched baseline
The local baseline still claimed "lahiri_assumed_by_skill" after the engine default moved to Raman, so the PyJHora comparison reported 59% instead of ~95% (every longitude 1.446 deg off). The baseline now passes --ayanamsa and --node-mode to the engine and records what the engine reports; the compare takes --ayanamsa and stops when the baseline was built differently. The nine public Rodden AA backtest figures join the fixed samples. Raman run (19 samples): ascendant, planets, nakshatras, D2/D4/D9/D10 and Ashtakavarga all 100%; Shadbala and three old-chart dashas differ (separate). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DbBXoyeZrYn6ryNcSe5Y2Y
This commit is contained in:
@@ -3,7 +3,16 @@
|
||||
"id": "smoke_beijing_1990_noon",
|
||||
"label": "Smoke Beijing 1990 noon",
|
||||
"category": "smoke",
|
||||
"birth": {"year": 1990, "month": 1, "day": 1, "hour": 12, "minute": 0, "lat": 39.9042, "lon": 116.4074, "tz": 8},
|
||||
"birth": {
|
||||
"year": 1990,
|
||||
"month": 1,
|
||||
"day": 1,
|
||||
"hour": 12,
|
||||
"minute": 0,
|
||||
"lat": 39.9042,
|
||||
"lon": 116.4074,
|
||||
"tz": 8
|
||||
},
|
||||
"today": "2026-06-03",
|
||||
"privacy": "fictional_or_public_test"
|
||||
},
|
||||
@@ -11,7 +20,16 @@
|
||||
"id": "smoke_newyork_1985_morning",
|
||||
"label": "Smoke New York 1985 morning",
|
||||
"category": "smoke",
|
||||
"birth": {"year": 1985, "month": 7, "day": 13, "hour": 8, "minute": 30, "lat": 40.7128, "lon": -74.0060, "tz": -4},
|
||||
"birth": {
|
||||
"year": 1985,
|
||||
"month": 7,
|
||||
"day": 13,
|
||||
"hour": 8,
|
||||
"minute": 30,
|
||||
"lat": 40.7128,
|
||||
"lon": -74.006,
|
||||
"tz": -4
|
||||
},
|
||||
"today": "2026-06-03",
|
||||
"privacy": "fictional_or_public_test"
|
||||
},
|
||||
@@ -19,7 +37,16 @@
|
||||
"id": "smoke_london_1970_evening",
|
||||
"label": "Smoke London 1970 evening",
|
||||
"category": "smoke",
|
||||
"birth": {"year": 1970, "month": 3, "day": 21, "hour": 18, "minute": 15, "lat": 51.5074, "lon": -0.1278, "tz": 0},
|
||||
"birth": {
|
||||
"year": 1970,
|
||||
"month": 3,
|
||||
"day": 21,
|
||||
"hour": 18,
|
||||
"minute": 15,
|
||||
"lat": 51.5074,
|
||||
"lon": -0.1278,
|
||||
"tz": 0
|
||||
},
|
||||
"today": "2026-06-03",
|
||||
"privacy": "fictional_or_public_test"
|
||||
},
|
||||
@@ -27,7 +54,16 @@
|
||||
"id": "smoke_delhi_2000_midnight",
|
||||
"label": "Smoke Delhi 2000 midnight",
|
||||
"category": "smoke",
|
||||
"birth": {"year": 2000, "month": 12, "day": 25, "hour": 0, "minute": 5, "lat": 28.6139, "lon": 77.2090, "tz": 5.5},
|
||||
"birth": {
|
||||
"year": 2000,
|
||||
"month": 12,
|
||||
"day": 25,
|
||||
"hour": 0,
|
||||
"minute": 5,
|
||||
"lat": 28.6139,
|
||||
"lon": 77.209,
|
||||
"tz": 5.5
|
||||
},
|
||||
"today": "2026-06-03",
|
||||
"privacy": "fictional_or_public_test"
|
||||
},
|
||||
@@ -35,7 +71,16 @@
|
||||
"id": "smoke_sydney_1999_afternoon",
|
||||
"label": "Smoke Sydney 1999 afternoon",
|
||||
"category": "smoke",
|
||||
"birth": {"year": 1999, "month": 9, "day": 9, "hour": 15, "minute": 45, "lat": -33.8688, "lon": 151.2093, "tz": 10},
|
||||
"birth": {
|
||||
"year": 1999,
|
||||
"month": 9,
|
||||
"day": 9,
|
||||
"hour": 15,
|
||||
"minute": 45,
|
||||
"lat": -33.8688,
|
||||
"lon": 151.2093,
|
||||
"tz": 10
|
||||
},
|
||||
"today": "2026-06-03",
|
||||
"privacy": "fictional_or_public_test"
|
||||
},
|
||||
@@ -43,7 +88,16 @@
|
||||
"id": "smoke_tokyo_1964_noon",
|
||||
"label": "Smoke Tokyo 1964 noon",
|
||||
"category": "smoke",
|
||||
"birth": {"year": 1964, "month": 10, "day": 10, "hour": 12, "minute": 0, "lat": 35.6762, "lon": 139.6503, "tz": 9},
|
||||
"birth": {
|
||||
"year": 1964,
|
||||
"month": 10,
|
||||
"day": 10,
|
||||
"hour": 12,
|
||||
"minute": 0,
|
||||
"lat": 35.6762,
|
||||
"lon": 139.6503,
|
||||
"tz": 9
|
||||
},
|
||||
"today": "2026-06-03",
|
||||
"privacy": "fictional_or_public_test"
|
||||
},
|
||||
@@ -51,7 +105,16 @@
|
||||
"id": "smoke_cairo_1952_dawn",
|
||||
"label": "Smoke Cairo 1952 dawn",
|
||||
"category": "smoke",
|
||||
"birth": {"year": 1952, "month": 7, "day": 23, "hour": 5, "minute": 20, "lat": 30.0444, "lon": 31.2357, "tz": 2},
|
||||
"birth": {
|
||||
"year": 1952,
|
||||
"month": 7,
|
||||
"day": 23,
|
||||
"hour": 5,
|
||||
"minute": 20,
|
||||
"lat": 30.0444,
|
||||
"lon": 31.2357,
|
||||
"tz": 2
|
||||
},
|
||||
"today": "2026-06-03",
|
||||
"privacy": "fictional_or_public_test"
|
||||
},
|
||||
@@ -59,7 +122,16 @@
|
||||
"id": "smoke_paris_1989_noon",
|
||||
"label": "Smoke Paris 1989 noon",
|
||||
"category": "smoke",
|
||||
"birth": {"year": 1989, "month": 11, "day": 9, "hour": 12, "minute": 0, "lat": 48.8566, "lon": 2.3522, "tz": 1},
|
||||
"birth": {
|
||||
"year": 1989,
|
||||
"month": 11,
|
||||
"day": 9,
|
||||
"hour": 12,
|
||||
"minute": 0,
|
||||
"lat": 48.8566,
|
||||
"lon": 2.3522,
|
||||
"tz": 1
|
||||
},
|
||||
"today": "2026-06-03",
|
||||
"privacy": "fictional_or_public_test"
|
||||
},
|
||||
@@ -67,7 +139,16 @@
|
||||
"id": "smoke_losangeles_1995_night",
|
||||
"label": "Smoke Los Angeles 1995 night",
|
||||
"category": "smoke",
|
||||
"birth": {"year": 1995, "month": 5, "day": 5, "hour": 23, "minute": 40, "lat": 34.0522, "lon": -118.2437, "tz": -7},
|
||||
"birth": {
|
||||
"year": 1995,
|
||||
"month": 5,
|
||||
"day": 5,
|
||||
"hour": 23,
|
||||
"minute": 40,
|
||||
"lat": 34.0522,
|
||||
"lon": -118.2437,
|
||||
"tz": -7
|
||||
},
|
||||
"today": "2026-06-03",
|
||||
"privacy": "fictional_or_public_test"
|
||||
},
|
||||
@@ -75,8 +156,215 @@
|
||||
"id": "smoke_sao_paulo_2004_morning",
|
||||
"label": "Smoke Sao Paulo 2004 morning",
|
||||
"category": "smoke",
|
||||
"birth": {"year": 2004, "month": 2, "day": 29, "hour": 9, "minute": 10, "lat": -23.5558, "lon": -46.6396, "tz": -3},
|
||||
"birth": {
|
||||
"year": 2004,
|
||||
"month": 2,
|
||||
"day": 29,
|
||||
"hour": 9,
|
||||
"minute": 10,
|
||||
"lat": -23.5558,
|
||||
"lon": -46.6396,
|
||||
"tz": -3
|
||||
},
|
||||
"today": "2026-06-03",
|
||||
"privacy": "fictional_or_public_test"
|
||||
},
|
||||
{
|
||||
"id": "steve_jobs",
|
||||
"label": "Steve Jobs",
|
||||
"category": "public_rodden_aa",
|
||||
"birth": {
|
||||
"year": 1955,
|
||||
"month": 2,
|
||||
"day": 24,
|
||||
"hour": 19,
|
||||
"minute": 15,
|
||||
"lat": 37.7749,
|
||||
"lon": -122.4194,
|
||||
"tz": -8.0
|
||||
},
|
||||
"today": "2026-09-27",
|
||||
"privacy": "public_rodden_aa",
|
||||
"source": {
|
||||
"url": "https://www.astro.com/adbvip/adbvip_02_24.htm?lang=r&nho2=250&nhor=1",
|
||||
"rodden_rating": "AA",
|
||||
"case_file": "references/real_case_calibration/minute_rectification_development_v1.json"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "barack_obama",
|
||||
"label": "Barack Obama",
|
||||
"category": "public_rodden_aa",
|
||||
"birth": {
|
||||
"year": 1961,
|
||||
"month": 8,
|
||||
"day": 4,
|
||||
"hour": 19,
|
||||
"minute": 24,
|
||||
"lat": 21.3069,
|
||||
"lon": -157.8583,
|
||||
"tz": -10.0
|
||||
},
|
||||
"today": "2026-09-27",
|
||||
"privacy": "public_rodden_aa",
|
||||
"source": {
|
||||
"url": "https://www.astro.com/adbvip/adbvip_08_04.htm?lang=r&nho2=161&nhor=1",
|
||||
"rodden_rating": "AA",
|
||||
"case_file": "references/real_case_calibration/minute_rectification_holdout_v4.json"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "elizabeth_taylor",
|
||||
"label": "Elizabeth Taylor",
|
||||
"category": "public_rodden_aa",
|
||||
"birth": {
|
||||
"year": 1932,
|
||||
"month": 2,
|
||||
"day": 27,
|
||||
"hour": 2,
|
||||
"minute": 30,
|
||||
"lat": 51.5,
|
||||
"lon": -0.1667,
|
||||
"tz": 0.0
|
||||
},
|
||||
"today": "2026-09-27",
|
||||
"privacy": "public_rodden_aa",
|
||||
"source": {
|
||||
"url": "https://www.astro.com/astro-databank/Taylor%2C_Elizabeth",
|
||||
"rodden_rating": "AA",
|
||||
"case_file": "references/real_case_calibration/minute_rectification_holdout_v4.json"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "marilyn_monroe",
|
||||
"label": "Marilyn Monroe",
|
||||
"category": "public_rodden_aa",
|
||||
"birth": {
|
||||
"year": 1926,
|
||||
"month": 6,
|
||||
"day": 1,
|
||||
"hour": 9,
|
||||
"minute": 30,
|
||||
"lat": 34.05,
|
||||
"lon": -118.25,
|
||||
"tz": -8.0
|
||||
},
|
||||
"today": "2026-09-27",
|
||||
"privacy": "public_rodden_aa",
|
||||
"source": {
|
||||
"url": "https://www.astro.com/astro-databank/Monroe,_Marilyn",
|
||||
"rodden_rating": "AA",
|
||||
"case_file": "references/real_case_calibration/minute_rectification_holdout_v5.json"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "judy_garland",
|
||||
"label": "Judy Garland",
|
||||
"category": "public_rodden_aa",
|
||||
"birth": {
|
||||
"year": 1922,
|
||||
"month": 6,
|
||||
"day": 10,
|
||||
"hour": 6,
|
||||
"minute": 0,
|
||||
"lat": 47.2333,
|
||||
"lon": -93.5333,
|
||||
"tz": -6.0
|
||||
},
|
||||
"today": "2026-09-27",
|
||||
"privacy": "public_rodden_aa",
|
||||
"source": {
|
||||
"url": "https://www.astro.com/astro-databank/Garland,_Judy",
|
||||
"rodden_rating": "AA",
|
||||
"case_file": "references/real_case_calibration/minute_rectification_holdout_v5.json"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "edith_piaf",
|
||||
"label": "Édith Piaf",
|
||||
"category": "public_rodden_aa",
|
||||
"birth": {
|
||||
"year": 1915,
|
||||
"month": 12,
|
||||
"day": 19,
|
||||
"hour": 5,
|
||||
"minute": 0,
|
||||
"lat": 48.8667,
|
||||
"lon": 2.3333,
|
||||
"tz": -0.0
|
||||
},
|
||||
"today": "2026-09-27",
|
||||
"privacy": "public_rodden_aa",
|
||||
"source": {
|
||||
"url": "https://www.astro.com/astro-databank/Piaf,_Édith",
|
||||
"rodden_rating": "AA",
|
||||
"case_file": "references/real_case_calibration/minute_rectification_holdout_v5.json"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "frida_kahlo",
|
||||
"label": "Frida Kahlo",
|
||||
"category": "public_rodden_aa",
|
||||
"birth": {
|
||||
"year": 1907,
|
||||
"month": 7,
|
||||
"day": 6,
|
||||
"hour": 8,
|
||||
"minute": 30,
|
||||
"lat": 19.3333,
|
||||
"lon": -99.1667,
|
||||
"tz": -6.6167
|
||||
},
|
||||
"today": "2026-09-27",
|
||||
"privacy": "public_rodden_aa",
|
||||
"source": {
|
||||
"url": "https://www.astro.com/astro-databank/Kahlo,_Frida",
|
||||
"rodden_rating": "AA",
|
||||
"case_file": "references/real_case_calibration/minute_rectification_holdout_v5.json"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "george_w_bush",
|
||||
"label": "George W. Bush",
|
||||
"category": "public_rodden_aa",
|
||||
"birth": {
|
||||
"year": 1946,
|
||||
"month": 7,
|
||||
"day": 6,
|
||||
"hour": 7,
|
||||
"minute": 26,
|
||||
"lat": 41.3,
|
||||
"lon": -72.9333,
|
||||
"tz": -4.0
|
||||
},
|
||||
"today": "2026-09-27",
|
||||
"privacy": "public_rodden_aa",
|
||||
"source": {
|
||||
"url": "https://www.astro.com/astro-databank/Bush,_George_W.",
|
||||
"rodden_rating": "AA",
|
||||
"case_file": "references/real_case_calibration/minute_rectification_holdout_v5.json"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "zinedine_zidane",
|
||||
"label": "Zinedine Zidane",
|
||||
"category": "public_rodden_aa",
|
||||
"birth": {
|
||||
"year": 1972,
|
||||
"month": 6,
|
||||
"day": 23,
|
||||
"hour": 3,
|
||||
"minute": 0,
|
||||
"lat": 43.3,
|
||||
"lon": 5.4,
|
||||
"tz": 1.0
|
||||
},
|
||||
"today": "2026-09-27",
|
||||
"privacy": "public_rodden_aa",
|
||||
"source": {
|
||||
"url": "https://www.astro.com/astro-databank/Zidane,_Zinedine",
|
||||
"rodden_rating": "AA",
|
||||
"case_file": "references/real_case_calibration/minute_rectification_holdout_v5.json"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -101,16 +101,16 @@ def tuple_to_date(t):
|
||||
return f'{int(y):04d}-{int(m):02d}-{int(d):02d}'
|
||||
|
||||
|
||||
def build_pyjhora_sample(sample, *, node_mode='mean'):
|
||||
def build_pyjhora_sample(sample, *, node_mode='mean', ayanamsa='lahiri'):
|
||||
swe = patch_swisseph()
|
||||
from jhora import utils, const
|
||||
from jhora.panchanga import drik
|
||||
from jhora.horoscope.chart import ashtakavarga, charts, strength
|
||||
from jhora.horoscope.dhasa.graha import vimsottari
|
||||
|
||||
# Align benchmark口径: Lahiri + mean sidereal year. PyJHora default is TRUE_PUSHYA.
|
||||
const._DEFAULT_AYANAMSA_MODE = 'LAHIRI'
|
||||
drik.set_ayanamsa_mode('LAHIRI')
|
||||
# Align benchmark口径: the requested ayanamsa + mean sidereal year. PyJHora default is TRUE_PUSHYA.
|
||||
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'))
|
||||
try:
|
||||
@@ -172,12 +172,12 @@ def build_pyjhora_sample(sample, *, node_mode='mean'):
|
||||
dasha['error'] = f'{type(exc).__name__}: {exc}'
|
||||
|
||||
return {
|
||||
'settings': {'ayanamsa': 'lahiri', 'node_mode': node_mode},
|
||||
'settings': {'ayanamsa': ayanamsa, 'node_mode': node_mode},
|
||||
'sample_id': sample['id'],
|
||||
'engine': 'PyJHora_4_8_6_lahiri_patched',
|
||||
'engine': f'PyJHora_4_8_6_{ayanamsa}_patched',
|
||||
'parameters': {
|
||||
'zodiac': 'sidereal',
|
||||
'ayanamsa': 'LAHIRI',
|
||||
'ayanamsa': ayanamsa.upper(),
|
||||
'd9_method': 'PyJHora divisional_chart chart_method=1',
|
||||
'd10_method': 'PyJHora divisional_chart chart_method=1',
|
||||
'd2_method': 'PyJHora hora_chart chart_method=2 traditional_parasara',
|
||||
@@ -301,7 +301,7 @@ def compare_one(sample_id, local, pyjhora):
|
||||
return rows
|
||||
|
||||
|
||||
def write_report(samples, rows, *, generated_at=None):
|
||||
def write_report(samples, rows, *, generated_at=None, ayanamsa='lahiri'):
|
||||
total = len(rows)
|
||||
counts = {}
|
||||
by_section = {}
|
||||
@@ -327,8 +327,8 @@ def write_report(samples, rows, *, generated_at=None):
|
||||
lines.append('')
|
||||
lines.append('- 外部引擎:PyJHora 4.8.6。')
|
||||
lines.append('- 用途:第二个独立 Jyotish 开源项目对标,重点验证 D1、D9、D10,并初探 Vimshottari。')
|
||||
lines.append('- 样本:10个公开/虚构 smoke case,不包含用户个人资料。')
|
||||
lines.append('- 口径:强制 Lahiri;PyJHora 默认 TRUE_PUSHYA,因此本轮显式切换到 LAHIRI。')
|
||||
lines.append(f'- 样本:{len(samples)} 个公开 / 虚构样本,不包含用户个人资料。')
|
||||
lines.append(f'- 口径:两边都用 {ayanamsa.capitalize()};PyJHora 默认 TRUE_PUSHYA,因此本轮显式切换到 {ayanamsa.upper()}。')
|
||||
lines.append('- 兼容处理:PyJHora 4.8.6 与本机 pyswisseph API 存在关键字参数/常量兼容问题,本脚本只在 benchmark 进程内 monkeypatch,不改 PyJHora 源码,不把 AGPL 代码并入 skill。')
|
||||
lines.append('')
|
||||
lines.append('## 2. 总体结果')
|
||||
@@ -373,6 +373,18 @@ def write_report(samples, rows, *, generated_at=None):
|
||||
return '\n'.join(lines)
|
||||
|
||||
|
||||
def baseline_settings_error(local, *, ayanamsa, node_mode):
|
||||
"""Why a local baseline cannot be compared under these settings, or None."""
|
||||
params = local.get('parameters') or {}
|
||||
built = str(params.get('ayanamsa') or '').lower()
|
||||
if built != ayanamsa:
|
||||
return f'local baseline ayanamsa is {built or "unrecorded"}, comparison asked for {ayanamsa}'
|
||||
built_node = str(params.get('node_mode') or '').lower()
|
||||
if built_node and built_node != node_mode:
|
||||
return f'local baseline node mode is {built_node}, comparison asked for {node_mode}'
|
||||
return None
|
||||
|
||||
|
||||
def main(argv=None):
|
||||
parser = argparse.ArgumentParser(description='Compare public benchmark samples against PyJHora.')
|
||||
parser.add_argument('--sample-id', action='append', default=[], help='Run only a named benchmark sample; repeatable.')
|
||||
@@ -380,6 +392,7 @@ def main(argv=None):
|
||||
parser.add_argument('--refresh-local', action='store_true', help='Explicitly rebuild selected local canonical baselines.')
|
||||
parser.add_argument('--node-mode', choices=['mean', 'true'], default='mean', help='Match the node convention before comparing.')
|
||||
parser.add_argument('--output-prefix', default='', help='Optional filename prefix for resumable batch artifacts.')
|
||||
parser.add_argument('--ayanamsa', choices=['lahiri', 'raman'], default='lahiri', help='Ayanamsa for both engines; the local baseline must have been built with the same one.')
|
||||
args = parser.parse_args(argv)
|
||||
PYJHORA_OUT.mkdir(parents=True, exist_ok=True)
|
||||
samples = json.loads(DATA.read_text())
|
||||
@@ -394,7 +407,7 @@ def main(argv=None):
|
||||
local_path = LOCAL_CANON / f"{sample['id']}.canonical.json"
|
||||
if (not local_path.exists() and args.build_local) or args.refresh_local:
|
||||
from run_skill_baseline import run_sample
|
||||
baseline = run_sample(sample)
|
||||
baseline = run_sample(sample, ayanamsa=args.ayanamsa, node_mode=args.node_mode)
|
||||
if not baseline.get('ok'):
|
||||
parser.error(f'failed to build local baseline for {sample["id"]}: {baseline.get("error", "unknown error")}')
|
||||
if not local_path.exists():
|
||||
@@ -402,9 +415,12 @@ def main(argv=None):
|
||||
f'missing local canonical baseline for {sample["id"]}; '
|
||||
'run with --build-local or run_skill_baseline.py first'
|
||||
)
|
||||
pyjhora = build_pyjhora_sample(sample, node_mode=args.node_mode)
|
||||
(PYJHORA_OUT / f"{sample['id']}.pyjhora.json").write_text(json.dumps(pyjhora, ensure_ascii=False, indent=2))
|
||||
local = json.loads(local_path.read_text())
|
||||
settings_error = baseline_settings_error(local, ayanamsa=args.ayanamsa, node_mode=args.node_mode)
|
||||
if settings_error:
|
||||
parser.error(f'{sample["id"]}: {settings_error}; rebuild with --refresh-local')
|
||||
pyjhora = build_pyjhora_sample(sample, node_mode=args.node_mode, ayanamsa=args.ayanamsa)
|
||||
(PYJHORA_OUT / f"{sample['id']}.pyjhora.json").write_text(json.dumps(pyjhora, ensure_ascii=False, indent=2))
|
||||
all_rows.extend(compare_one(sample['id'], local, pyjhora))
|
||||
|
||||
prefix = f"{args.output_prefix}_" if args.output_prefix else ''
|
||||
@@ -415,7 +431,7 @@ def main(argv=None):
|
||||
writer.writerows(all_rows)
|
||||
|
||||
report = OUT / f'{prefix}jyotish_benchmark_round3_pyjhora_compare.md'
|
||||
report.write_text(write_report(samples, all_rows))
|
||||
report.write_text(write_report(samples, all_rows, ayanamsa=args.ayanamsa))
|
||||
print(json.dumps({'report': str(report), 'matrix': str(matrix), 'samples': len(samples), 'fields': len(all_rows)}, ensure_ascii=False, indent=2))
|
||||
|
||||
|
||||
|
||||
@@ -30,7 +30,15 @@ def safe_get(obj, *keys, default=None):
|
||||
return cur
|
||||
|
||||
|
||||
def run_sample(sample):
|
||||
# The engine's default ayanamsa changed from Lahiri to Raman after the June
|
||||
# rounds; a baseline built on the default then compared against Lahiri PyJHora
|
||||
# and every longitude was off by 1.446 degrees (2026-11-04). The settings are
|
||||
# passed explicitly and recorded from the engine's own output.
|
||||
DEFAULT_AYANAMSA = 'lahiri'
|
||||
DEFAULT_NODE_MODE = 'mean'
|
||||
|
||||
|
||||
def run_sample(sample, *, ayanamsa=DEFAULT_AYANAMSA, node_mode=DEFAULT_NODE_MODE):
|
||||
RAW.mkdir(parents=True, exist_ok=True)
|
||||
CANON.mkdir(parents=True, exist_ok=True)
|
||||
birth = sample['birth']
|
||||
@@ -45,6 +53,8 @@ def run_sample(sample):
|
||||
'--lon', str(birth['lon']),
|
||||
'--tz', str(birth['tz']),
|
||||
'--today', sample.get('today', '2026-06-03'),
|
||||
'--ayanamsa', ayanamsa,
|
||||
'--node-mode', node_mode,
|
||||
]
|
||||
proc = subprocess.run(cmd, text=True, capture_output=True)
|
||||
raw_path = RAW / f"{sample['id']}.json"
|
||||
@@ -93,7 +103,8 @@ def canonicalize(sample, data):
|
||||
'engine': 'local_jyotish_skill_v6_0_4',
|
||||
'parameters': {
|
||||
'zodiac': 'sidereal',
|
||||
'ayanamsa': 'lahiri_assumed_by_skill',
|
||||
'ayanamsa': safe_get(data, 'birth_info', 'ayanamsa_name') or safe_get(chart, 'birth_info', 'ayanamsa_name'),
|
||||
'node_mode': safe_get(data, 'birth_info', 'node_mode') or safe_get(chart, 'birth_info', 'node_mode'),
|
||||
'house': 'whole_sign_for_planet_house',
|
||||
'today': sample.get('today'),
|
||||
},
|
||||
|
||||
@@ -55,3 +55,55 @@ def test_pyjhora_comparison_includes_d2_d4_bav_sav_and_shadbala_rows():
|
||||
sections = {row["section"] for row in compare_one("fixture", local, pyjhora)}
|
||||
|
||||
assert {"D2", "D4", "Ashtakavarga_BAV", "Ashtakavarga_SAV", "Shadbala"} <= sections
|
||||
|
||||
|
||||
def test_baseline_records_the_engine_ayanamsa_and_the_compare_refuses_a_mismatch():
|
||||
# 2026-11-04: the engine default moved from Lahiri to Raman while the
|
||||
# baseline still claimed "lahiri_assumed_by_skill"; the comparison then
|
||||
# reported 59% instead of ~95% because every longitude was 1.446 deg off.
|
||||
from benchmarks.jyotish.scripts.run_pyjhora_compare import baseline_settings_error
|
||||
from benchmarks.jyotish.scripts.run_skill_baseline import canonicalize
|
||||
|
||||
data = {"birth_info": {"ayanamsa_name": "raman", "node_mode": "mean"}, "modules": {"chart": {}}}
|
||||
canon = canonicalize({"id": "fixture", "label": "x", "category": "smoke", "birth": {}}, data)
|
||||
assert canon["parameters"]["ayanamsa"] == "raman"
|
||||
assert canon["parameters"]["node_mode"] == "mean"
|
||||
assert baseline_settings_error(canon, ayanamsa="raman", node_mode="mean") is None
|
||||
assert "lahiri" in baseline_settings_error(canon, ayanamsa="lahiri", node_mode="mean")
|
||||
assert "unrecorded" in baseline_settings_error({"parameters": {}}, ayanamsa="lahiri", node_mode="mean")
|
||||
|
||||
|
||||
def test_baseline_passes_ayanamsa_and_node_mode_to_the_engine(monkeypatch, tmp_path):
|
||||
from benchmarks.jyotish.scripts import run_skill_baseline as rsb
|
||||
|
||||
seen = {}
|
||||
|
||||
class Done:
|
||||
returncode = 1
|
||||
stdout = ""
|
||||
stderr = "stub"
|
||||
|
||||
def fake_run(cmd, **kwargs):
|
||||
seen["cmd"] = cmd
|
||||
return Done()
|
||||
|
||||
monkeypatch.setattr(rsb, "RAW", tmp_path / "raw")
|
||||
monkeypatch.setattr(rsb, "CANON", tmp_path / "canon")
|
||||
monkeypatch.setattr(rsb.subprocess, "run", fake_run)
|
||||
sample = {"id": "fixture", "birth": {"year": 2000, "month": 1, "day": 1, "hour": 0, "minute": 0, "lat": 0, "lon": 0, "tz": 0}}
|
||||
rsb.run_sample(sample, ayanamsa="raman", node_mode="true")
|
||||
cmd = seen["cmd"]
|
||||
assert cmd[cmd.index("--ayanamsa") + 1] == "raman"
|
||||
assert cmd[cmd.index("--node-mode") + 1] == "true"
|
||||
rsb.run_sample(sample)
|
||||
assert seen["cmd"][seen["cmd"].index("--ayanamsa") + 1] == "lahiri"
|
||||
|
||||
|
||||
def test_fixed_samples_include_the_public_rodden_aa_backtest_figures():
|
||||
import json
|
||||
|
||||
samples = json.loads((ROOT / "benchmarks" / "jyotish" / "data" / "benchmark_samples.json").read_text())
|
||||
ids = {sample["id"] for sample in samples}
|
||||
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)
|
||||
|
||||
Reference in New Issue
Block a user