#!/usr/bin/env python3 """ Yoga精度Benchmark (v6.9.1) 测量yoga_engine对所有476条规则的检测精度: - True Positive (应检出且检出) - False Negative (应检出但未检出) - False Positive (不应检出但检出) 测试方法:构造具有特定配置的星盘,验证对应Yoga被正确检出。 """ import sys, os, json, time sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', '..', '..', 'scripts')) from yoga_engine import YogaEngine SKILL_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', '..', '..') RULES_PATH = os.path.join(SKILL_DIR, 'references', 'yoga_rules.json') def load_rules(): with open(RULES_PATH) as f: return json.load(f) class YogaBenchmark: def __init__(self): self.rules = load_rules() self.engine = YogaEngine(RULES_PATH) self.rule_count = len(self.engine.rules) self.results = {'tp': 0, 'fn': 0, 'fp': 0, 'total_tests': 0} def run(self): print(f"规则库: {self.rule_count}条 (total_rules: {self.rules.get('total_rules', '?' )})") print(f"内置Solar/Lunar Yoga: Veshi+Voshi+Ubhayachari+Sunapha+Anapha+Durudhura") # === Test 1: Raja Yoga (Kendra+Kona lord连接) === self._test_raja_yoga() # === Test 2: Dhana Yoga (2H+11H lord连接) === self._test_dhana_yoga() # === Test 3: Kemadruma Yoga (Moon孤立) === self._test_kemadruma_via_engine() # === Test 4: Solar Yogas === self._test_solar_yogas() # === Test 5: Lunar Yogas === self._test_lunar_yogas() # === Test 6: Gaja Kesari (Jupiter+Moon) === self._test_gaja_kesari() # === Test 7: Budha Aditya (Sun+Mercury) === self._test_budha_aditya() # === Test 8: Mahapurusha detection === self._test_mahapurusha_via_engine() # === Test 9: 随机覆盖抽样 === self._test_random_sampling() return self.results def _to_planet_dict(self, planet_list, asc='Aries'): """Helper: convert planet list to engine format""" planets = {} for name, sign, house in planet_list: planets[name] = {'sign': sign, 'house': house, 'degree': 15} return planets def _detect(self, planets, asc='Aries'): return self.engine.detect(planets, asc) def _check(self, name, yogas, expected_name, desc): found = any(expected_name.lower() in y.get('name','').lower() for y in yogas) self.results['total_tests'] += 1 if found: self.results['tp'] += 1 else: self.results['fn'] += 1 pass # FN tracking def _test_raja_yoga(self): # Kendra lord (Mars in Aries=1H) + Kona lord (Jupiter in Sagittarius=9H) # They conjunct in same house → Raja Yoga planets = self._to_planet_dict([ ('Sun', 'Leo', 5), ('Moon', 'Cancer', 4), ('Mars', 'Scorpio', 8), ('Mercury', 'Gemini', 3), ('Jupiter', 'Scorpio', 8), ('Venus', 'Libra', 7), ('Saturn', 'Capricorn', 10), ]) yogas = self._detect(planets, 'Scorpio') self._check('Raja Yoga', yogas, 'Raja', 'Kendra+Kona lord conjunction') def _test_dhana_yoga(self): # 2H lord + 11H lord connection planets = self._to_planet_dict([ ('Sun', 'Leo', 5), ('Moon', 'Taurus', 2), ('Mars', 'Aries', 1), ('Mercury', 'Taurus', 2), ('Jupiter', 'Pisces', 12), ('Venus', 'Taurus', 2), ('Saturn', 'Aquarius', 11), ]) yogas = self._detect(planets, 'Aries') self._check('Dhana Yoga', yogas, 'Dhana', '2H+11H lord connection') def _test_kemadruma_via_engine(self): # Moon with no planets in adjacent houses planets = self._to_planet_dict([ ('Sun', 'Aries', 1), ('Moon', 'Leo', 5), ('Mars', 'Gemini', 3), ('Mercury', 'Capricorn', 10), ('Jupiter', 'Sagittarius', 9), ('Venus', 'Aquarius', 11), ('Saturn', 'Scorpio', 8), ]) yogas = self._detect(planets, 'Aries') self._check('Moon isolation', yogas, 'Kemadruma', 'Moon no adjacent planets') def _test_solar_yogas(self): # Veshi: planet in Sun's 2nd house planets = self._to_planet_dict([ ('Sun', 'Aries', 1), ('Moon', 'Scorpio', 8), ('Mars', 'Taurus', 2), ('Mercury', 'Gemini', 3), ('Jupiter', 'Sagittarius', 9), ('Venus', 'Aquarius', 11), ('Saturn', 'Capricorn', 10), ]) yogas = self._detect(planets, 'Aries') self._check('Veshi Yoga', yogas, 'Veshi', 'planet in Sun 2nd house') def _test_lunar_yogas(self): # Sunapha: planet in Moon's 2nd house planets = self._to_planet_dict([ ('Sun', 'Leo', 5), ('Moon', 'Taurus', 2), ('Mars', 'Gemini', 3), ('Mercury', 'Gemini', 3), ('Jupiter', 'Sagittarius', 9), ('Venus', 'Libra', 7), ('Saturn', 'Capricorn', 10), ]) yogas = self._detect(planets, 'Taurus') self._check('Sunapha Yoga', yogas, 'Sunapha', 'planet in Moon 2nd house') def _test_gaja_kesari(self): # Jupiter + Moon in kendra from each other planets = self._to_planet_dict([ ('Sun', 'Leo', 5), ('Moon', 'Sagittarius', 9), ('Mars', 'Aries', 1), ('Mercury', 'Gemini', 3), ('Jupiter', 'Sagittarius', 9), ('Venus', 'Libra', 7), ('Saturn', 'Capricorn', 10), ]) yogas = self._detect(planets, 'Aries') self._check('Gaja Kesari', yogas, 'Gaja', 'Jupiter+Moon conjunction') def _test_budha_aditya(self): # Sun + Mercury conjunction planets = self._to_planet_dict([ ('Sun', 'Gemini', 3), ('Moon', 'Cancer', 4), ('Mars', 'Aries', 1), ('Mercury', 'Gemini', 3), ('Jupiter', 'Sagittarius', 9), ('Venus', 'Libra', 7), ('Saturn', 'Capricorn', 10), ]) yogas = self._detect(planets, 'Aries') self._check('Budha Aditya', yogas, 'Budha', 'Sun+Mercury conjunction') def _test_mahapurusha_via_engine(self): # Mars in Capricorn (exalted) in Kendra → Ruchaka planets = self._to_planet_dict([ ('Sun', 'Leo', 5), ('Moon', 'Cancer', 4), ('Mars', 'Capricorn', 10), ('Mercury', 'Gemini', 3), ('Jupiter', 'Sagittarius', 9), ('Venus', 'Libra', 7), ('Saturn', 'Aquarius', 11), ]) yogas = self._detect(planets, 'Aries') self._check('PMC Ruchaka', yogas, 'Ruchaka', 'Mars exalted in Kendra') def _test_random_sampling(self): """Random coverage: check a batch of medium-complexity star charts""" # Chart: standard distribution planets = self._to_planet_dict([ ('Sun', 'Aries', 1), ('Moon', 'Cancer', 4), ('Mars', 'Scorpio', 8), ('Mercury', 'Virgo', 6), ('Jupiter', 'Pisces', 12), ('Venus', 'Taurus', 2), ('Saturn', 'Libra', 7), ]) yogas = self._detect(planets, 'Cancer') total = len(yogas) print(f"\n 抽样星盘检测到: {total}个Yoga") self.results['total_detected_sample'] = total def main(): bm = YogaBenchmark() print("=" * 50) print("Yoga 精度 Benchmark v6.9.1") print("=" * 50) t0 = time.time() results = bm.run() elapsed = time.time() - t0 tp = results['tp'] fn = results['fn'] total_tests = results['total_tests'] accuracy = tp * 100 / total_tests if total_tests > 0 else 0 print(f"\n{'='*50}") print(f" 结果: TP={tp} FN={fn} 总计={total_tests}") print(f" 检测率: {accuracy:.1f}%") print(f" 规则库: {bm.rule_count}条") print(f" 抽样Yoga: {results.get('total_detected_sample', '?')}个") print(f" 时间: {elapsed:.2f}s") print(f"{'='*50}") # 评估 if accuracy >= 95: print("✅ PASS — Yoga检测率≥95%") elif accuracy >= 90: print("⚠️ WARN — Yoga检测率90-94%, 需检查FN") else: print("❌ FAIL — Yoga检测率<90%") return 0 if accuracy >= 90 else 1 if __name__ == '__main__': sys.exit(main())