c4c0eff67a
PyJHora oracle benchmark 10案例×120字段:
- Sign Sequence: 120/120 = 100.00%
- Duration: 110/120 = 91.67%
- Overall: 230/240 = 95.83% >= 95% ✅ PASS
修复:
- <=0检查顺序对齐PyJHora(先于尊贵调整)
- _PLANET_DIGNITY_KNRAO表(含Rahu/Ketu+set检查+Mercury own sign排除)
- _resolve_chara_dasha_lord()动态宫主判定框架
- bench脚本含Rahu/Ketu行星映射
已知限制: ~4.2%差异来自Aquarius/Scorpio共主动态判定(未来优化)
173 lines
7.5 KiB
Python
173 lines
7.5 KiB
Python
#!/usr/bin/env python3
|
||
"""
|
||
Chara Dasha KN Rao Benchmark v1.1
|
||
比较 v6.1.11 yinduzhanxing Chara Dasha 与 PyJHora KN Rao method。
|
||
|
||
策略:用 PyJHora 计算行星位置作为共享输入,两个实现基于相同数据运行。
|
||
这样隔离算法差异,消除天文计算差异。
|
||
|
||
依赖: pip install jhora numpy
|
||
"""
|
||
import sys, json, os
|
||
|
||
# 添加scripts路径
|
||
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..', 'scripts')))
|
||
from jaimini import SIGNS, _chara_dasha_duration_knrao, _chara_progression_knrao, _EVEN_FOOTED_SIGNS
|
||
|
||
# PyJHora
|
||
from jhora.horoscope.dhasa.raasi import chara as pj_chara
|
||
from jhora.horoscope.chart import charts
|
||
from jhora.panchanga import drik
|
||
from jhora import const, utils
|
||
|
||
# ─────────────────────────────────────────────
|
||
# 10个测试案例(与Round 7相同)
|
||
# ─────────────────────────────────────────────
|
||
TEST_CASES = [
|
||
{"id": "smoke_beijing_1990_noon", "year":1990, "month":6, "day":15, "hour":12, "minute":0, "lat":39.9, "lon":116.4, "tz":8},
|
||
{"id": "smoke_newyork_1985_morning", "year":1985, "month":3, "day":22, "hour":9, "minute":30, "lat":40.7, "lon":-74.0, "tz":-5},
|
||
{"id": "smoke_london_1970_evening", "year":1970, "month":9, "day":12, "hour":19, "minute":0, "lat":51.5, "lon":-0.1, "tz":0},
|
||
{"id": "smoke_delhi_2000_midnight", "year":2000, "month":1, "day":1, "hour":0, "minute":0, "lat":28.6, "lon":77.2, "tz":5.5},
|
||
{"id": "smoke_sydney_1999_afternoon", "year":1999, "month":8, "day":7, "hour":15, "minute":45, "lat":-33.9, "lon":151.2, "tz":10},
|
||
{"id": "smoke_tokyo_1964_noon", "year":1964, "month":10, "day":10,"hour":12, "minute":0, "lat":35.7, "lon":139.7, "tz":9},
|
||
{"id": "smoke_cairo_1952_dawn", "year":1952, "month":7, "day":23, "hour":6, "minute":0, "lat":30.0, "lon":31.2, "tz":2},
|
||
{"id": "smoke_paris_1989_noon", "year":1989, "month":11,"day":9, "hour":12, "minute":30, "lat":48.9, "lon":2.3, "tz":1},
|
||
{"id": "smoke_losangeles_1995_night", "year":1995, "month":2, "day":14, "hour":22, "minute":15, "lat":34.1, "lon":-118.2,"tz":-8},
|
||
{"id": "smoke_sao_paulo_2004_morning","year":2004, "month":12,"day":25, "hour":7, "minute":0, "lat":-23.5, "lon":-46.6, "tz":-3},
|
||
]
|
||
|
||
# 行星名称到PyJHora ID
|
||
PLANET_NAMES = {
|
||
const.SUN_ID: "Sun", const.MOON_ID: "Moon", const.MARS_ID: "Mars",
|
||
const.MERCURY_ID: "Mercury", const.JUPITER_ID: "Jupiter", const.VENUS_ID: "Venus",
|
||
const.SATURN_ID: "Saturn", const.RAHU_ID: "Rahu", const.KETU_ID: "Ketu",
|
||
}
|
||
|
||
|
||
def run_benchmark():
|
||
total_sign = 0; sign_match = 0
|
||
total_dur = 0; dur_match = 0
|
||
detail_lines = []
|
||
sample_details = []
|
||
|
||
for case in TEST_CASES:
|
||
# 1. 用 PyJHora 获取行星位置
|
||
dob = drik.Date(case["year"], case["month"], case["day"])
|
||
tob = (case["hour"], case["minute"], 0)
|
||
place = drik.Place(f'test_{case["id"]}', case["lat"], case["lon"], case["tz"])
|
||
jd = utils.julian_day_number(dob, tob)
|
||
pp = charts.divisional_chart(jd, place, divisional_chart_factor=1)[:const._pp_count_upto_ketu]
|
||
|
||
asc_house = pp[0][1][0] # 0-indexed ascendant sign (pp[0] = Lagna)
|
||
|
||
# 2. 构建共享的行星经度字典(供我们的代码使用)
|
||
# 注意: pp[0]=Lagna, pp[1]=Sun, pp[2]=Moon, ... 即 pp[planet_id+1]=planet
|
||
planet_longitudes = {}
|
||
for planet_id, pname in PLANET_NAMES.items():
|
||
sign = pp[planet_id + 1][1][0]
|
||
deg = pp[planet_id + 1][1][1]
|
||
planet_longitudes[pname] = sign * 30 + deg
|
||
|
||
# 3. 我们的 KN Rao 实现
|
||
our_progression = _chara_progression_knrao(asc_house, planet_longitudes)
|
||
our_durations = [_chara_dasha_duration_knrao(planet_longitudes, s) for s in our_progression]
|
||
|
||
# 4. PyJHora KN Rao 实现(oracle)
|
||
pj_progression = pj_chara._dhasa_progression_knrao_method(pp)
|
||
pj_durations = [pj_chara._dhasa_duration_knrao_method(pp, s) for s in pj_progression]
|
||
|
||
# 5. 比较
|
||
case_sign_match = 0; case_dur_match = 0
|
||
mismatches = []
|
||
for i in range(12):
|
||
our_s = our_progression[i]
|
||
pj_s = pj_progression[i]
|
||
our_d = our_durations[i]
|
||
pj_d = pj_durations[i]
|
||
|
||
if our_s == pj_s:
|
||
case_sign_match += 1
|
||
else:
|
||
mismatches.append(f"sign[{i}]: us={SIGNS[our_s]}, pj={SIGNS[pj_s]}")
|
||
if our_d == pj_d:
|
||
case_dur_match += 1
|
||
else:
|
||
mismatches.append(f"dur[{i}]: us={our_d}, pj={pj_d}")
|
||
|
||
total_sign += 12; sign_match += case_sign_match
|
||
total_dur += 12; dur_match += case_dur_match
|
||
|
||
our_first3 = ",".join(SIGNS[s] for s in our_progression[:3])
|
||
pj_first3 = ",".join(SIGNS[s] for s in pj_progression[:3])
|
||
line = f" {case['id']:35s} | sign {case_sign_match:2d}/12 | dur {case_dur_match:2d}/12"
|
||
detail_lines.append(line)
|
||
|
||
sample_details.append({
|
||
"case": case['id'],
|
||
"asc": SIGNS[asc_house],
|
||
"our_first_3": our_first3,
|
||
"pj_first_3": pj_first3,
|
||
"sign_match": f"{case_sign_match}/12",
|
||
"dur_match": f"{case_dur_match}/12",
|
||
"mismatches": mismatches[:5] # 最多5个不匹配项
|
||
})
|
||
|
||
# ── 输出 ──
|
||
print("=" * 80)
|
||
print("Chara Dasha KN Rao Benchmark v1.1")
|
||
print(f"Skill: v6.1.11 (共享PyJHora行星位置)")
|
||
print("=" * 80)
|
||
|
||
print(f"\n{'Case':40s} | Sign Match | Dur Match | Us first3 vs PJ first3")
|
||
print("-" * 80)
|
||
for i, s in enumerate(sample_details):
|
||
line = f" {s['case']:35s} | {s['sign_match']:>10s} | {s['dur_match']:>9s} | {s['our_first_3']} | {s['pj_first_3']}"
|
||
print(line)
|
||
print("-" * 80)
|
||
|
||
total_all = total_sign + total_dur
|
||
match_all = sign_match + dur_match
|
||
print(f"\nSign Sequence Match: {sign_match}/{total_sign} = {sign_match/total_sign*100:.2f}%")
|
||
print(f"Duration Match: {dur_match}/{total_dur} = {dur_match/total_dur*100:.2f}%")
|
||
print(f"Overall Match: {match_all}/{total_all} = {match_all/total_all*100:.2f}%")
|
||
|
||
if match_all / total_all >= 0.95:
|
||
status = "✅ PASS ✓"
|
||
elif match_all / total_all >= 0.85:
|
||
status = "⚠️ BORDERLINE"
|
||
else:
|
||
status = "❌ FAIL"
|
||
print(f"\nBenchmark Result: {status}")
|
||
|
||
# 放生不匹配
|
||
if status != "✅ PASS ✓":
|
||
print("\n--- 不匹配详情 ---")
|
||
for s in sample_details:
|
||
if s["mismatches"]:
|
||
print(f"\n{s['case']}:")
|
||
for m in s["mismatches"]:
|
||
print(f" {m}")
|
||
|
||
# 保存 JSON
|
||
out = os.path.join(os.path.dirname(__file__), "..", "outputs", "chara_dasha_knrao_benchmark.json")
|
||
os.makedirs(os.path.dirname(out), exist_ok=True)
|
||
result = {
|
||
"version": "v6.1.11",
|
||
"benchmark": "chara_dasha_kn_rao",
|
||
"total_sign_match": sign_match,
|
||
"total_dur_match": dur_match,
|
||
"total_sign": total_sign,
|
||
"total_dur": total_dur,
|
||
"sign_match_rate": round(sign_match/total_sign, 4) if total_sign else 0,
|
||
"dur_match_rate": round(dur_match/total_dur, 4) if total_dur else 0,
|
||
"status": status,
|
||
"samples": sample_details,
|
||
}
|
||
with open(out, 'w') as f:
|
||
json.dump(result, f, indent=2, ensure_ascii=False)
|
||
print(f"\n结果已保存: {out}")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
run_benchmark()
|