From 74169e1bfc22b8cd637bfd62b1269810deb21e5e Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 4 Nov 2026 11:55:22 +0800 Subject: [PATCH] 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 Claude-Session: https://claude.ai/code/session_01DbBXoyeZrYn6ryNcSe5Y2Y --- .../jyotish/data/benchmark_samples.json | 308 +++++++++++++++++- .../jyotish/scripts/run_pyjhora_compare.py | 44 ++- .../jyotish/scripts/run_skill_baseline.py | 15 +- tests/test_pyjhora_compare_cli.py | 52 +++ 4 files changed, 393 insertions(+), 26 deletions(-) diff --git a/benchmarks/jyotish/data/benchmark_samples.json b/benchmarks/jyotish/data/benchmark_samples.json index f1555897..bed98518 100644 --- a/benchmarks/jyotish/data/benchmark_samples.json +++ b/benchmarks/jyotish/data/benchmark_samples.json @@ -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" + } } ] diff --git a/benchmarks/jyotish/scripts/run_pyjhora_compare.py b/benchmarks/jyotish/scripts/run_pyjhora_compare.py index 041a7b7f..b2e347c5 100644 --- a/benchmarks/jyotish/scripts/run_pyjhora_compare.py +++ b/benchmarks/jyotish/scripts/run_pyjhora_compare.py @@ -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)) diff --git a/benchmarks/jyotish/scripts/run_skill_baseline.py b/benchmarks/jyotish/scripts/run_skill_baseline.py index a8a22375..a2731af5 100644 --- a/benchmarks/jyotish/scripts/run_skill_baseline.py +++ b/benchmarks/jyotish/scripts/run_skill_baseline.py @@ -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'), }, diff --git a/tests/test_pyjhora_compare_cli.py b/tests/test_pyjhora_compare_cli.py index fa6e81bd..b80163b3 100644 --- a/tests/test_pyjhora_compare_cli.py +++ b/tests/test_pyjhora_compare_cli.py @@ -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)