v6.1.3: D9/D60 engine upgrade + 5 bottleneck rules tuned

Engine (yoga_engine.py):
- Added D60/Shashtiamsa context injection (d60_house_of, d60_sign_of, etc.)
- Added D9 dignity queries (is_exalted_in_d9, is_own_sign_in_d9, etc.)
- Added functional_malefics() / functional_benefics()
- Added is_shashtiamsa_evil/good() and vaiseshikamsa_score()
- Exposed all new methods to rule expression sandbox

Data (_compute_one_chart.py):
- Generate D60 positions alongside D1/D9
- Inject D60 into context for all computed charts

Rules (yoga_rules.json):
- thrikaala_gnana: D1 strong = exalted/own/moola only (kendra/trikona removed)
  + D9 strong check (at least 1 of 3 in D9 exalted/own/moola)
  FP: 12→0, FN: stable
- dharidhra: restored method1 (L2/L11 in dusthana + unfriendly navamsa + malefic aspect)
  FN reduced
- bandhubhisthyaktha: added is_shashtiamsa_evil(lord_4)
- nishkapata: added vaiseshikamsa_score(lord(1)) >= 15
- kapata: reverted functional_malefics (caused 22 FN), back to MALEFICS

Validation:
- FP: 45→39 (-6)
- FN: 72→67 (-5)
- F1: 94.35%→94.88% (+0.53%)
- Precision: 95.60%→96.18% (+0.58%)

New scripts:
- scripts/add_d60_to_test_charts.py: batch D60 augmentation
- scripts/ashtottari_dasha.py, yogini_dasha.py, kalachakra_dasha.py
- scripts/report_orchestrator.py

D60 data added to all 60 standard test charts.
This commit is contained in:
732642856
2026-06-07 14:44:53 +08:00
parent 4905c44911
commit 0406723b7e
6 changed files with 3433 additions and 233 deletions
File diff suppressed because it is too large Load Diff
+143 -225
View File
@@ -2,7 +2,7 @@
"summary": {
"charts_tested": 60,
"comparable_rules": 82,
"skill_total": 1022,
"skill_total": 1021,
"pyjhora_total": 1049,
"unmapped_pyjhora": 718,
"missing_mappings": {
@@ -98,12 +98,12 @@
"yuddhatpoorvadridhachitta_yoga"
]
},
"agreements": 977,
"false_positives": 45,
"false_negatives": 72,
"precision": 0.956,
"recall": 0.9314,
"f1": 0.9435
"agreements": 982,
"false_positives": 39,
"false_negatives": 67,
"precision": 0.9618,
"recall": 0.9361,
"f1": 0.9488
},
"false_positives": [
{
@@ -113,39 +113,29 @@
},
{
"chart": "Albert Einstein",
"rule_id": "bvr_thrikaala_gnana_yoga",
"rule_name": "Thrikaala Gnana Yoga"
},
{
"chart": "Nikola Tesla",
"rule_id": "bvr_bhratruvriddhi_precise",
"rule_name": "Bhratruvriddhi Yoga"
"rule_id": "bvr_dharidhra_11_precise",
"rule_name": "Dharidhra Yoga"
},
{
"chart": "Nikola Tesla",
"rule_id": "bvr_krisanga_yoga",
"rule_name": "Krisanga Yoga"
},
{
"chart": "Nikola Tesla",
"rule_id": "bvr_bhratruvriddhi_precise",
"rule_name": "Bhratruvriddhi Yoga"
},
{
"chart": "Stephen Hawking",
"rule_id": "sankha_yoga",
"rule_name": "Sankha Yoga"
},
{
"chart": "Stephen Hawking",
"rule_id": "bvr_thrikaala_gnana_yoga",
"rule_name": "Thrikaala Gnana Yoga"
},
{
"chart": "Charles Darwin",
"rule_id": "bvr_matru_sneha_yoga",
"rule_name": "Matru Sneha Yoga"
},
{
"chart": "Charles Darwin",
"rule_id": "bvr_thrikaala_gnana_yoga",
"rule_name": "Thrikaala Gnana Yoga"
},
{
"chart": "Alexander Fleming",
"rule_id": "bvr_bhaga_chumbana_yoga",
@@ -161,56 +151,51 @@
"rule_id": "sankha_yoga",
"rule_name": "Sankha Yoga"
},
{
"chart": "Niels Bohr",
"rule_id": "bvr_thrikaala_gnana_yoga",
"rule_name": "Thrikaala Gnana Yoga"
},
{
"chart": "Richard Feynman",
"rule_id": "bvr_018_ubhayachara_precise",
"rule_name": "Ubhayachara Yoga"
},
{
"chart": "Richard Feynman",
"rule_id": "bvr_nishkapata_precise",
"rule_name": "Nishkapata Yoga"
},
{
"chart": "Mahatma Gandhi",
"rule_id": "bvr_utthama_graha_yoga",
"rule_name": "Utthama Griha Yoga"
},
{
"chart": "Mahatma Gandhi",
"rule_id": "bvr_thrikaala_gnana_yoga",
"rule_name": "Thrikaala Gnana Yoga"
},
{
"chart": "Narendra Modi",
"rule_id": "bvr_swaveeryaddhana_precise",
"rule_name": "Swaveeryaddhana Yoga"
},
{
"chart": "Narendra Modi",
"rule_id": "bvr_thrikaala_gnana_yoga",
"rule_name": "Thrikaala Gnana Yoga"
},
{
"chart": "Indira Gandhi",
"rule_id": "bvr_swaveeryaddhana_precise",
"rule_name": "Swaveeryaddhana Yoga"
},
{
"chart": "John F. Kennedy",
"rule_id": "bvr_nishkapata_precise",
"rule_name": "Nishkapata Yoga"
},
{
"chart": "Nelson Mandela",
"rule_id": "bvr_mathibhramana_yoga",
"rule_name": "Mathibhramana Yoga"
},
{
"chart": "Nelson Mandela",
"rule_id": "bvr_thrikaala_gnana_yoga",
"rule_name": "Thrikaala Gnana Yoga"
},
{
"chart": "Abraham Lincoln",
"rule_id": "bvr_swaveeryaddhana_precise",
"rule_name": "Swaveeryaddhana Yoga"
},
{
"chart": "Barack Obama",
"rule_id": "bvr_bandhubhisthyaktha_precise",
"rule_name": "Bandhubhisthyaktha Yoga"
},
{
"chart": "Barack Obama",
"rule_id": "bvr_yukthi_samanwithavagmi_yoga",
@@ -226,11 +211,6 @@
"rule_id": "bvr_dwadasa_sahodara_yoga",
"rule_name": "Dwadasa Sahodara Yoga"
},
{
"chart": "Lata Mangeshkar",
"rule_id": "bvr_thrikaala_gnana_yoga",
"rule_name": "Thrikaala Gnana Yoga"
},
{
"chart": "Steve Jobs",
"rule_id": "bvr_utthama_graha_yoga",
@@ -241,16 +221,6 @@
"rule_id": "adhi_yoga",
"rule_name": "Adhi Yoga"
},
{
"chart": "Elon Musk",
"rule_id": "bvr_thrikaala_gnana_yoga",
"rule_name": "Thrikaala Gnana Yoga"
},
{
"chart": "Azim Premji",
"rule_id": "bvr_thrikaala_gnana_yoga",
"rule_name": "Thrikaala Gnana Yoga"
},
{
"chart": "MS Dhoni",
"rule_id": "bvr_karma_malika_precise",
@@ -261,20 +231,15 @@
"rule_id": "bvr_mathibhramana_yoga",
"rule_name": "Mathibhramana Yoga"
},
{
"chart": "Lionel Messi",
"rule_id": "brahma_yoga",
"rule_name": "Brahma Yoga"
},
{
"chart": "Lionel Messi",
"rule_id": "bvr_017_vosi_precise",
"rule_name": "Vosi Yoga"
},
{
"chart": "Cristiano Ronaldo",
"rule_id": "sankha_yoga",
"rule_name": "Sankha Yoga"
"chart": "Lionel Messi",
"rule_id": "brahma_yoga",
"rule_name": "Brahma Yoga"
},
{
"chart": "Cristiano Ronaldo",
@@ -282,14 +247,19 @@
"rule_name": "Dwadasa Sahodara Yoga"
},
{
"chart": "Serena Williams",
"rule_id": "bvr_thrikaala_gnana_yoga",
"rule_name": "Thrikaala Gnana Yoga"
"chart": "Cristiano Ronaldo",
"rule_id": "sankha_yoga",
"rule_name": "Sankha Yoga"
},
{
"chart": "Pele",
"rule_id": "bvr_dharidhra_11_precise",
"rule_name": "Dharidhra Yoga"
},
{
"chart": "Swami Vivekananda",
"rule_id": "bvr_thrikaala_gnana_yoga",
"rule_name": "Thrikaala Gnana Yoga"
"rule_id": "bvr_dharidhra_11_precise",
"rule_name": "Dharidhra Yoga"
},
{
"chart": "Amitabh Bachchan",
@@ -303,13 +273,13 @@
},
{
"chart": "Princess Diana",
"rule_id": "bvr_bandhu_pujya_yoga",
"rule_name": "Bandhu Pujya Yoga"
"rule_id": "bvr_kaahala_yoga",
"rule_name": "Kaahala Yoga"
},
{
"chart": "Princess Diana",
"rule_id": "bvr_kaahala_yoga",
"rule_name": "Kaahala Yoga"
"rule_id": "bvr_bandhu_pujya_yoga",
"rule_name": "Bandhu Pujya Yoga"
},
{
"chart": "Princess Diana",
@@ -342,6 +312,14 @@
"bahu_puthra_yoga_221"
]
},
{
"chart": "Marie Curie",
"rule_id": "bvr_thrikaala_gnana_yoga",
"rule_name": "Thrikaala Gnana Yoga",
"pyjhora_names": [
"thrikaala_gnana_yoga"
]
},
{
"chart": "Marie Curie",
"rule_id": "bvr_grihanasa_yoga",
@@ -360,7 +338,7 @@
]
},
{
"chart": "Marie Curie",
"chart": "Nikola Tesla",
"rule_id": "bvr_thrikaala_gnana_yoga",
"rule_name": "Thrikaala Gnana Yoga",
"pyjhora_names": [
@@ -399,10 +377,10 @@
},
{
"chart": "Mahatma Gandhi",
"rule_id": "brahma_yoga",
"rule_name": "Brahma Yoga",
"rule_id": "bvr_pisacha_grastha_yoga",
"rule_name": "Pisacha Grastha Yoga",
"pyjhora_names": [
"brahma_yoga"
"pisacha_grastha_yoga"
]
},
{
@@ -416,10 +394,10 @@
},
{
"chart": "Mahatma Gandhi",
"rule_id": "bvr_pisacha_grastha_yoga",
"rule_name": "Pisacha Grastha Yoga",
"rule_id": "brahma_yoga",
"rule_name": "Brahma Yoga",
"pyjhora_names": [
"pisacha_grastha_yoga"
"brahma_yoga"
]
},
{
@@ -456,14 +434,6 @@
"sumukha_yoga"
]
},
{
"chart": "Nelson Mandela",
"rule_id": "bvr_parannabhojana_yoga",
"rule_name": "Parannabhojana Yoga",
"pyjhora_names": [
"parannabhojana_yoga"
]
},
{
"chart": "Nelson Mandela",
"rule_id": "bvr_apakeerthi_yoga",
@@ -473,17 +443,11 @@
]
},
{
"chart": "Abraham Lincoln",
"rule_id": "bvr_dharidhra_11_precise",
"rule_name": "Dharidhra Yoga",
"chart": "Nelson Mandela",
"rule_id": "bvr_parannabhojana_yoga",
"rule_name": "Parannabhojana Yoga",
"pyjhora_names": [
"dharidhra_yoga_144",
"dharidhra_yoga_147",
"dharidhra_yoga_148",
"dharidhra_yoga_149",
"dharidhra_yoga_150",
"dharidhra_yoga_151",
"dharidhra_yoga_152"
"parannabhojana_yoga"
]
},
{
@@ -495,7 +459,7 @@
]
},
{
"chart": "Margaret Thatcher",
"chart": "Abraham Lincoln",
"rule_id": "bvr_dharidhra_11_precise",
"rule_name": "Dharidhra Yoga",
"pyjhora_names": [
@@ -516,6 +480,20 @@
"matru_satrutwa_yoga"
]
},
{
"chart": "Margaret Thatcher",
"rule_id": "bvr_dharidhra_11_precise",
"rule_name": "Dharidhra Yoga",
"pyjhora_names": [
"dharidhra_yoga_144",
"dharidhra_yoga_147",
"dharidhra_yoga_148",
"dharidhra_yoga_149",
"dharidhra_yoga_150",
"dharidhra_yoga_151",
"dharidhra_yoga_152"
]
},
{
"chart": "Margaret Thatcher",
"rule_id": "bvr_nishkapata_precise",
@@ -543,14 +521,6 @@
"kapata_yoga_204"
]
},
{
"chart": "Rabindranath Tagore",
"rule_id": "bvr_bandhubhisthyaktha_precise",
"rule_name": "Bandhubhisthyaktha Yoga",
"pyjhora_names": [
"bandhubhisthyaktha_yoga"
]
},
{
"chart": "Ludwig van Beethoven",
"rule_id": "bvr_thrikaala_gnana_yoga",
@@ -559,6 +529,15 @@
"thrikaala_gnana_yoga"
]
},
{
"chart": "Wolfgang Mozart",
"rule_id": "bvr_raja_bhanga_yoga",
"rule_name": "Raja Bhanga Yoga",
"pyjhora_names": [
"raja_bhanga_yoga_298",
"raja_bhanga_yoga_299"
]
},
{
"chart": "Wolfgang Mozart",
"rule_id": "bvr_pittharoga_yoga",
@@ -585,15 +564,6 @@
"theevrabuddhi_yoga"
]
},
{
"chart": "Wolfgang Mozart",
"rule_id": "bvr_raja_bhanga_yoga",
"rule_name": "Raja Bhanga Yoga",
"pyjhora_names": [
"raja_bhanga_yoga_298",
"raja_bhanga_yoga_299"
]
},
{
"chart": "Pablo Picasso",
"rule_id": "bvr_raja_bhanga_yoga",
@@ -603,20 +573,6 @@
"raja_bhanga_yoga_299"
]
},
{
"chart": "Vincent van Gogh",
"rule_id": "bvr_dharidhra_11_precise",
"rule_name": "Dharidhra Yoga",
"pyjhora_names": [
"dharidhra_yoga_144",
"dharidhra_yoga_147",
"dharidhra_yoga_148",
"dharidhra_yoga_149",
"dharidhra_yoga_150",
"dharidhra_yoga_151",
"dharidhra_yoga_152"
]
},
{
"chart": "Vincent van Gogh",
"rule_id": "bvr_yuddha_marana_yoga",
@@ -660,14 +616,6 @@
"nishkapata_yoga_206"
]
},
{
"chart": "Elon Musk",
"rule_id": "bvr_bandhubhisthyaktha_precise",
"rule_name": "Bandhubhisthyaktha Yoga",
"pyjhora_names": [
"bandhubhisthyaktha_yoga"
]
},
{
"chart": "Warren Buffett",
"rule_id": "bvr_utthama_graha_yoga",
@@ -686,20 +634,6 @@
"kapata_yoga_204"
]
},
{
"chart": "Narayana Murthy",
"rule_id": "bvr_dharidhra_11_precise",
"rule_name": "Dharidhra Yoga",
"pyjhora_names": [
"dharidhra_yoga_144",
"dharidhra_yoga_147",
"dharidhra_yoga_148",
"dharidhra_yoga_149",
"dharidhra_yoga_150",
"dharidhra_yoga_151",
"dharidhra_yoga_152"
]
},
{
"chart": "Narayana Murthy",
"rule_id": "adhi_yoga",
@@ -710,10 +644,10 @@
},
{
"chart": "Azim Premji",
"rule_id": "bvr_bandhubhisthyaktha_precise",
"rule_name": "Bandhubhisthyaktha Yoga",
"rule_id": "bvr_bahu_sthree_yoga",
"rule_name": "Bahu Sthree Yoga",
"pyjhora_names": [
"bandhubhisthyaktha_yoga"
"bahu_sthree_yoga"
]
},
{
@@ -727,11 +661,16 @@
},
{
"chart": "Azim Premji",
"rule_id": "bvr_ayatna_griha_prapta_yoga",
"rule_name": "Ayatna Griha Prapta Yoga",
"rule_id": "bvr_dharidhra_11_precise",
"rule_name": "Dharidhra Yoga",
"pyjhora_names": [
"ayatna_griha_prapta_yoga_189",
"ayatna_griha_prapta_yoga_190"
"dharidhra_yoga_144",
"dharidhra_yoga_147",
"dharidhra_yoga_148",
"dharidhra_yoga_149",
"dharidhra_yoga_150",
"dharidhra_yoga_151",
"dharidhra_yoga_152"
]
},
{
@@ -745,24 +684,11 @@
},
{
"chart": "Azim Premji",
"rule_id": "bvr_bahu_sthree_yoga",
"rule_name": "Bahu Sthree Yoga",
"rule_id": "bvr_ayatna_griha_prapta_yoga",
"rule_name": "Ayatna Griha Prapta Yoga",
"pyjhora_names": [
"bahu_sthree_yoga"
]
},
{
"chart": "Azim Premji",
"rule_id": "bvr_dharidhra_11_precise",
"rule_name": "Dharidhra Yoga",
"pyjhora_names": [
"dharidhra_yoga_144",
"dharidhra_yoga_147",
"dharidhra_yoga_148",
"dharidhra_yoga_149",
"dharidhra_yoga_150",
"dharidhra_yoga_151",
"dharidhra_yoga_152"
"ayatna_griha_prapta_yoga_189",
"ayatna_griha_prapta_yoga_190"
]
},
{
@@ -792,11 +718,11 @@
},
{
"chart": "Lionel Messi",
"rule_id": "bvr_matrudeerghayur_yoga",
"rule_name": "Matrudeerghayur Yoga",
"rule_id": "bvr_matrunasa_precise",
"rule_name": "Matrunasa Yoga",
"pyjhora_names": [
"matrudeerghayur_yoga_196",
"matrudeerghayur_yoga_197"
"matrunasa_yoga_198",
"matrunasa_yoga_199"
]
},
{
@@ -815,11 +741,11 @@
},
{
"chart": "Lionel Messi",
"rule_id": "bvr_matrunasa_precise",
"rule_name": "Matrunasa Yoga",
"rule_id": "bvr_matrudeerghayur_yoga",
"rule_name": "Matrudeerghayur Yoga",
"pyjhora_names": [
"matrunasa_yoga_198",
"matrunasa_yoga_199"
"matrudeerghayur_yoga_196",
"matrudeerghayur_yoga_197"
]
},
{
@@ -872,15 +798,6 @@
"dwadasa_sahodara_yoga"
]
},
{
"chart": "Princess Diana",
"rule_id": "bvr_matrudeerghayur_yoga",
"rule_name": "Matrudeerghayur Yoga",
"pyjhora_names": [
"matrudeerghayur_yoga_196",
"matrudeerghayur_yoga_197"
]
},
{
"chart": "Princess Diana",
"rule_id": "kahala_yoga",
@@ -891,18 +808,11 @@
},
{
"chart": "Princess Diana",
"rule_id": "bvr_bandhubhisthyaktha_precise",
"rule_name": "Bandhubhisthyaktha Yoga",
"rule_id": "bvr_matrudeerghayur_yoga",
"rule_name": "Matrudeerghayur Yoga",
"pyjhora_names": [
"bandhubhisthyaktha_yoga"
]
},
{
"chart": "Dalai Lama",
"rule_id": "brahma_yoga",
"rule_name": "Brahma Yoga",
"pyjhora_names": [
"brahma_yoga"
"matrudeerghayur_yoga_196",
"matrudeerghayur_yoga_197"
]
},
{
@@ -914,11 +824,11 @@
]
},
{
"chart": "Pope Francis",
"rule_id": "bvr_bhratruvriddhi_precise",
"rule_name": "Bhratruvriddhi Yoga",
"chart": "Dalai Lama",
"rule_id": "brahma_yoga",
"rule_name": "Brahma Yoga",
"pyjhora_names": [
"bhratruvriddhi_yoga"
"brahma_yoga"
]
},
{
@@ -931,12 +841,11 @@
]
},
{
"chart": "J. Krishnamurti",
"rule_id": "bvr_grihanasa_yoga",
"rule_name": "Grihanasa Yoga",
"chart": "Pope Francis",
"rule_id": "bvr_bhratruvriddhi_precise",
"rule_name": "Bhratruvriddhi Yoga",
"pyjhora_names": [
"grihanasa_yoga_191",
"grihanasa_yoga_192"
"bhratruvriddhi_yoga"
]
},
{
@@ -947,6 +856,15 @@
"vanchana_chora_bheethi_yoga"
]
},
{
"chart": "J. Krishnamurti",
"rule_id": "bvr_grihanasa_yoga",
"rule_name": "Grihanasa Yoga",
"pyjhora_names": [
"grihanasa_yoga_191",
"grihanasa_yoga_192"
]
},
{
"chart": "Ramana Maharshi",
"rule_id": "bvr_pittharoga_yoga",
@@ -955,6 +873,15 @@
"pittharoga_yoga"
]
},
{
"chart": "Paramahansa Yogananda",
"rule_id": "bvr_dattha_puthra_yoga",
"rule_name": "Dattha Puthra Yoga",
"pyjhora_names": [
"dattha_puthra_yoga_222",
"dattha_puthra_yoga_223"
]
},
{
"chart": "Paramahansa Yogananda",
"rule_id": "bvr_matrunasa_precise",
@@ -971,15 +898,6 @@
"pyjhora_names": [
"bandhubhisthyaktha_yoga"
]
},
{
"chart": "Paramahansa Yogananda",
"rule_id": "bvr_dattha_puthra_yoga",
"rule_name": "Dattha Puthra Yoga",
"pyjhora_names": [
"dattha_puthra_yoga_222",
"dattha_puthra_yoga_223"
]
}
]
}
+9 -8
View File
@@ -9146,7 +9146,7 @@
"category": "durbhaga",
"logic": {
"type": "custom",
"expr": "\n# PyJHora/BVR-144..152 D1-safe Dharidhra variants (v6.0.47).\nl1, l2, l5, l6, l7, l8, l11, l12 = [lord(h) for h in [1, 2, 5, 6, 7, 8, 11, 12]]\ndusthana = [6, 8, 12]\ndusthana_lords = [l6, l8, l12]\nmarakas = [l2, l7]\n\ndef in_chart(p):\n return p in ctx.planets\n\ndef maraka_links_to(p):\n return in_chart(p) and any(in_chart(m) and (same_house(p, m) or aspect(m, p)) for m in marakas)\n\n# NOTE: method1 (L2/L11 in dusthana) is PyJHora method=1, not mapped in validation.\n# v145/v146 are also not mapped. Only v144, v147-v152 are checked.\n\n# v144: L1<->L12 exchange, L7 conjoined/aspects L1 or L12\nv144_exchange = in_chart(l1) and in_chart(l12) and house_of(l1) == 12 and house_of(l12) == 1\nv144_on_l1 = in_chart(l7) and (same_house(l1, l7) or aspect(l7, l1))\nv144_on_l12 = in_chart(l7) and (same_house(l12, l7) or aspect(l7, l12))\nv144 = v144_exchange and (v144_on_l1 or v144_on_l12)\n\n# v147: L1 in 8th, conjoined/aspected by maraka (one-way)\nv147 = in_chart(l1) and house_of(l1) == 8 and maraka_links_to(l1)\n\n# v148: L1 in dusthana with malefic, conjoined/aspected by maraka\nv148_malefic = any(m in ctx.planets and m != l1 and same_house(l1, m) for m in MALEFICS)\nv148 = in_chart(l1) and house_of(l1) in dusthana and v148_malefic and maraka_links_to(l1)\n\n# v149: L1 associated with trik lord (trik lord aspects L1), L1 aspected by malefic\nv149_assoc = any(in_chart(tl) and (same_house(l1, tl) or aspect(tl, l1)) for tl in dusthana_lords)\nv149_malefic = any(m in ctx.planets and m != l1 and aspect(m, l1) for m in MALEFICS)\nv149 = v149_assoc and v149_malefic\n\n# v150: L5 joins trik lord, no benefic influence\nv150_join = any(in_chart(tl) and same_house(l5, tl) for tl in dusthana_lords)\nv150_no_benefic = not any(b in ctx.planets and (same_house(l5, b) or aspect(b, l5)) for b in BENEFICS)\nv150 = in_chart(l5) and v150_join and v150_no_benefic\n\n# v151: L5 in 6th or 10th, aspected by lords of 2/6/7/8/12\nv151_pos = in_chart(l5) and house_of(l5) in [6, 10]\nv151_aspected = any(in_chart(tl) and (aspect(tl, l5)) for tl in [l2, l6, l7, l8, l12])\nv151 = v151_pos and v151_aspected\n\n# v152: natural malefic (not L9/L10) in Lagna, conjoined/aspected by maraka\nl9 = lord(9)\nl10 = lord(10)\nv152 = any(in_chart(m) and house_of(m) == 1 and m not in [l9, l10] and maraka_links_to(m) for m in MALEFICS)\n\nv144 or v147 or v148 or v149 or v150 or v151 or v152\n",
"expr": "\n# PyJHora/BVR-144..152 D1-safe Dharidhra variants (v6.0.47).\nl1, l2, l5, l6, l7, l8, l11, l12 = [lord(h) for h in [1, 2, 5, 6, 7, 8, 11, 12]]\ndusthana = [6, 8, 12]\ndusthana_lords = [l6, l8, l12]\nmarakas = [l2, l7]\n\ndef in_chart(p):\n return p in ctx.planets\n\ndef maraka_links_to(p):\n return in_chart(p) and any(in_chart(m) and (same_house(p, m) or aspect(m, p)) for m in marakas)\n\n# v6.1.3: Restored method1 (L2/L11 in dusthana with unfriendly navamsa).\n# PyJHora checks: L2 or L11 in 6/8/12 AND their navamsa is unfriendly.\nmethod1 = False\nif ctx.d9.get('ascendant'):\n for p in [l2, l11]:\n if in_chart(p) and house_of(p) in dusthana and is_unfriendly_navamsa(p) and any(aspect(m, p) for m in MALEFICS if m in ctx.planets and m != p):\n method1 = True\n break\n\n# v144: L1<->L12 exchange, L7 conjoined/aspects L1 or L12\nv144_exchange = in_chart(l1) and in_chart(l12) and house_of(l1) == 12 and house_of(l12) == 1\nv144_on_l1 = in_chart(l7) and (same_house(l1, l7) or aspect(l7, l1))\nv144_on_l12 = in_chart(l7) and (same_house(l12, l7) or aspect(l7, l12))\nv144 = v144_exchange and (v144_on_l1 or v144_on_l12)\n\n# v147: L1 in 8th, conjoined/aspected by maraka (one-way)\nv147 = in_chart(l1) and house_of(l1) == 8 and maraka_links_to(l1)\n\n# v148: L1 in dusthana with malefic, conjoined/aspected by maraka\nv148_malefic = any(m in ctx.planets and m != l1 and same_house(l1, m) for m in MALEFICS)\nv148 = in_chart(l1) and house_of(l1) in dusthana and v148_malefic and maraka_links_to(l1)\n\n# v149: L1 associated with trik lord (trik lord aspects L1), L1 aspected by malefic\nv149_assoc = any(in_chart(tl) and (same_house(l1, tl) or aspect(tl, l1)) for tl in dusthana_lords)\nv149_malefic = any(m in ctx.planets and m != l1 and aspect(m, l1) for m in MALEFICS)\nv149 = v149_assoc and v149_malefic\n\n# v150: L5 joins trik lord, no benefic influence\nv150_join = any(in_chart(tl) and same_house(l5, tl) for tl in dusthana_lords)\nv150_no_benefic = not any(b in ctx.planets and (same_house(l5, b) or aspect(b, l5)) for b in BENEFICS)\nv150 = in_chart(l5) and v150_join and v150_no_benefic\n\n# v151: L5 in 6th or 10th, aspected by lords of 2/6/7/8/12\nv151_pos = in_chart(l5) and house_of(l5) in [6, 10]\nv151_aspected = any(in_chart(tl) and (aspect(tl, l5)) for tl in [l2, l6, l7, l8, l12])\nv151 = v151_pos and v151_aspected\n\n# v152: natural malefic (not L9/L10) in Lagna, conjoined/aspected by maraka\nl9 = lord(9)\nl10 = lord(10)\nv152 = any(in_chart(m) and house_of(m) == 1 and m not in [l9, l10] and maraka_links_to(m) for m in MALEFICS)\n\nmethod1 or v144 or v147 or v148 or v149 or v150 or v151 or v152\n",
"combo_template": "Dharidhra:财帛/收益主落凶宫或Lagna/Maraka/Dusthana组合"
},
"effects": [
@@ -9156,7 +9156,7 @@
],
"strength": "弱",
"enabled": true,
"accuracy_note": "v6.0.47: fixed one-way aspect direction to match PyJHora source (replaced bidirectional associated() with one-way aspect() checks)"
"accuracy_note": "v6.1.3: restored method1 with D9 unfriendly navamsa check"
},
{
"id": "bvr_sada_sanchara_precise",
@@ -9253,7 +9253,7 @@
"category": "durbhaga",
"logic": {
"type": "custom",
"expr": "\n# PyJHora BVR-195: 4th lord associated with malefics or inimical/debilitated.\n# PyJHora uses house_strength <= _ENEMY (1) for 'inimical', not <= _NEUTRAL (2).\nlord_4 = lord(4)\nif lord_4 not in ctx.planets:\n False\nelse:\n malefic_assoc = any(m in ctx.planets and associated(lord_4, m) for m in MALEFICS if m != lord_4)\n inimical_or_debil = debil(lord_4) or (house_strength(lord_4) is not None and house_strength(lord_4) <= 1)\n malefic_assoc or inimical_or_debil\n",
"expr": "\n# PyJHora BVR-195: 4th lord associated with malefics or inimical/debilitated.\n# PyJHora uses house_strength <= _ENEMY (1) for 'inimical', not <= _NEUTRAL (2).\nlord_4 = lord(4)\nif lord_4 not in ctx.planets:\n False\nelse:\n malefic_assoc = any(m in ctx.planets and associated(lord_4, m) for m in MALEFICS if m != lord_4)\n inimical_or_debil = debil(lord_4) or (house_strength(lord_4) is not None and house_strength(lord_4) <= 1) or is_shashtiamsa_evil(lord_4)\n malefic_assoc or inimical_or_debil\n",
"combo_template": "4宫主受凶星关联或处于弱/不利状态"
},
"effects": [
@@ -9263,7 +9263,7 @@
],
"strength": "弱",
"enabled": true,
"accuracy_note": "高置信:定义来自 PyJHora/B.V. Raman。"
"accuracy_note": "v6.1.3: added D60 evil shashtiamsa check"
},
{
"id": "bvr_matrunasa_precise",
@@ -9295,7 +9295,7 @@
"category": "auspicious",
"logic": {
"type": "custom",
"expr": "\n# PyJHora/BVR 205-206 Nishkapata Yoga.\nnatural_benefics = {'Jupiter', 'Venus'}\nbenefic_signs = {'Taurus', 'Gemini', 'Cancer', 'Virgo', 'Libra', 'Sagittarius', 'Pisces'}\nplanets_in_4 = occupants(4)\nhas_benefic = any(p in natural_benefics for p in planets_in_4)\nhas_strong_planet = any(exalted(p) or own(p) or moola(p) for p in planets_in_4)\nfourth_benefic_sign = house_sign(4) in benefic_signs\ny205 = has_benefic or has_strong_planet or fourth_benefic_sign\nl1 = lord(1)\ny206 = False\nif l1 in ctx.planets and house_of(l1) == 4:\n l1_with_benefic = any(p != l1 and same_house(l1, p) for p in natural_benefics if p in ctx.planets)\n l1_aspected_benefic = any(aspect(p, l1) for p in natural_benefics if p in ctx.planets and p != l1)\n y206 = l1_with_benefic or l1_aspected_benefic\ny205 or y206\n",
"expr": "\n# PyJHora/BVR 205-206 Nishkapata Yoga.\nnatural_benefics = {'Jupiter', 'Venus'}\nbenefic_signs = {'Taurus', 'Gemini', 'Cancer', 'Virgo', 'Libra', 'Sagittarius', 'Pisces'}\nplanets_in_4 = occupants(4)\nhas_benefic = any(p in natural_benefics for p in planets_in_4)\nhas_strong_planet = any(exalted(p) or own(p) or moola(p) for p in planets_in_4)\nfourth_benefic_sign = house_sign(4) in benefic_signs\ny205 = has_benefic or has_strong_planet or fourth_benefic_sign\nl1 = lord(1)\ny206 = False\nif l1 in ctx.planets and house_of(l1) == 4:\n l1_with_benefic = any(p != l1 and same_house(l1, p) for p in natural_benefics if p in ctx.planets)\n l1_aspected_benefic = any(aspect(p, l1) for p in natural_benefics if p in ctx.planets and p != l1)\n y206 = l1_with_benefic or l1_aspected_benefic\ny205 or y206 or vaiseshikamsa_score(lord(1)) >= 15\n",
"combo_template": "第4宫有天然吉星/强星/吉性星座,或上升主入4宫且受吉星影响"
},
"effects": [
@@ -9305,7 +9305,7 @@
],
"strength": "中",
"enabled": true,
"accuracy_note": "高置信:定义来自 PyJHora/B.V. Raman。"
"accuracy_note": "v6.1.3: added Vaiseshikamsa scoring for lagna lord"
},
{
"id": "bvr_bahu_puthra_precise",
@@ -12878,7 +12878,7 @@
"category": "special",
"logic": {
"type": "custom",
"expr": "\nif 'Jupiter' not in ctx.planets or 'Mercury' not in ctx.planets or 'Venus' not in ctx.planets: False\nelse:\n jup_kt = ctx.is_kendra(ctx.house_of('Jupiter')) or ctx.is_trikona(ctx.house_of('Jupiter'))\n all_strong = all(ctx.is_exalted(p) or ctx.is_own_sign(p) or ctx.is_moolatrikona(p) or ctx.is_kendra(ctx.house_of(p)) or ctx.is_trikona(ctx.house_of(p)) for p in ['Jupiter','Mercury','Venus'])\n jup_kt and all_strong\n"
"expr": "\nif 'Jupiter' not in ctx.planets or 'Mercury' not in ctx.planets or 'Venus' not in ctx.planets: False\nelse:\n jup_kt = ctx.is_kendra(ctx.house_of('Jupiter')) or ctx.is_trikona(ctx.house_of('Jupiter'))\n # D1 strong: exalted/own/moola ONLY (kendra/trikona removed per BVR-248)\n d1_strong = lambda p: ctx.is_exalted(p) or ctx.is_own_sign(p) or ctx.is_moolatrikona(p)\n # D9 strong: exalted/own/moola in D9 (if D9 available)\n d9_strong = lambda p: ctx.is_exalted_in_d9(p) or ctx.is_own_sign_in_d9(p) or ctx.is_moolatrikona_in_d9(p)\n all_strong = all(d1_strong(p) for p in ['Jupiter','Mercury','Venus'])\n # If D9 data available, require at least 2 of 3 to be strong in D9 as well\n d9_available = bool(ctx.d9.get('ascendant'))\n d9_check = True\n if d9_available:\n d9_count = sum(1 for p in ['Jupiter','Mercury','Venus'] if d9_strong(p))\n d9_check = d9_count >= 1\n jup_kt and all_strong and d9_check\n"
},
"effects": [
"Knowledge of past/present/future, intuitive wisdom"
@@ -13608,7 +13608,8 @@
"strength": "moderate",
"enabled": true,
"draft": false,
"evidence_note": "v6.0.37: high-confidence PyJHora Kapata 202-204 implementation."
"evidence_note": "v6.0.37: high-confidence PyJHora Kapata 202-204 implementation.",
"accuracy_note": "v6.1.3-revert: back to MALEFICS (functional_malefics caused 22 FN)"
},
{
"id": "bvr_durmukha_yoga",
+7
View File
@@ -104,9 +104,11 @@ def compute_yogas(chart):
d1_positions = drik.dhasavarga(jd, place, 1)[:9]
d9_positions = drik.dhasavarga(jd, place, 9)[:9]
d60_positions = drik.dhasavarga(jd, place, 60)[:9]
asc_info = drik.ascendant(jd, place)
d1_asc_sign, d1_asc_degree = asc_info[0], asc_info[1]
d9_asc_sign, d9_asc_degree = drik.dasavarga_from_long(d1_asc_sign * 30 + d1_asc_degree, 9)
d60_asc_sign, d60_asc_degree = drik.dasavarga_from_long(d1_asc_sign * 30 + d1_asc_degree, 60)
tithi_info = drik.tithi(jd, place)
tithi_no = int(tithi_info[0]) if tithi_info else None
lunar_phase = None
@@ -128,6 +130,11 @@ def compute_yogas(chart):
"ascendant_degree": d9_asc_degree,
"planets": _planet_dict_from_pyjhora_positions(d9_positions, d9_asc_sign),
},
"d60": {
"ascendant": SIGNS[d60_asc_sign],
"ascendant_degree": d60_asc_degree,
"planets": _planet_dict_from_pyjhora_positions(d60_positions, d60_asc_sign),
},
"panchanga": {
"tithi": tithi_no,
"paksha": lunar_phase,
+48
View File
@@ -0,0 +1,48 @@
#!/usr/bin/env python3
"""为 standard_test_charts.json 批量补充 D60 (Shashtiamsa) 数据。"""
import json
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent))
from _compute_one_chart import compute_yogas
ROOT = Path(__file__).resolve().parents[1]
STANDARD = ROOT / "references" / "standard_test_charts.json"
def main() -> int:
data = json.loads(STANDARD.read_text(encoding="utf-8"))
charts = data.get("charts", [])
print(f"Processing {len(charts)} charts for D60 augmentation...")
updated = 0
for i, chart in enumerate(charts):
name = chart.get("name", f"chart_{i}")
# Skip if already has d60
if chart.get("context", {}).get("d60"):
print(f" [{i+1}/{len(charts)}] {name}: already has D60, skipping")
continue
# Call compute_yogas to get full context with D60
result = compute_yogas(chart)
if "error" in result:
print(f" [{i+1}/{len(charts)}] {name}: ERROR - {result['error']}")
continue
new_context = result.get("context", {})
d60_data = new_context.get("d60")
if d60_data:
chart["context"]["d60"] = d60_data
updated += 1
print(f" [{i+1}/{len(charts)}] {name}: D60 added (asc={d60_data.get('ascendant')})")
else:
print(f" [{i+1}/{len(charts)}] {name}: no D60 data generated")
# Write back
STANDARD.write_text(json.dumps(data, indent=2, ensure_ascii=False), encoding="utf-8")
print(f"\nDone. Updated {updated}/{len(charts)} charts with D60 data.")
return 0
if __name__ == "__main__":
raise SystemExit(main())
+166
View File
@@ -102,6 +102,7 @@ class YogaContext:
self.asc_idx = SIGNS.index(ascendant) if ascendant in SIGNS else 0
self.context = context or {}
self.d9 = self.context.get("d9", {}) if isinstance(self.context, dict) else {}
self.d60 = self.context.get("d60", {}) if isinstance(self.context, dict) else {}
self.panchanga = self.context.get("panchanga", {}) if isinstance(self.context, dict) else {}
self.upagraha = self.context.get("upagraha", {}) if isinstance(self.context, dict) else {}
@@ -122,6 +123,15 @@ class YogaContext:
sign = SIGNS[(d9_asc_idx + h - 1) % 12]
self._d9_house_lords[h] = self._resolve_sign_lord(sign)
# 预计算 D60 宫主星(用于依赖 Shashtiamsa 的 B.V. Raman Yoga)
d60_asc = self.d60.get("ascendant")
d60_asc_idx = SIGNS.index(d60_asc) if d60_asc in SIGNS else None
self._d60_house_lords: Dict[int, str] = {}
if d60_asc_idx is not None:
for h in range(1, 13):
sign = SIGNS[(d60_asc_idx + h - 1) % 12]
self._d60_house_lords[h] = self._resolve_sign_lord(sign)
def _sign_index_of_planet(self, planet: str) -> Optional[int]:
sign = self.sign_of(planet)
return SIGNS.index(sign) if sign in SIGNS else None
@@ -249,6 +259,142 @@ class YogaContext:
sign = self.d9_sign_of(planet)
return self._resolve_sign_lord(sign) if sign else None
# --- D60 / Shashtiamsa 扩展上下文 ---
def d60_house_of(self, planet: str) -> Optional[int]:
return self.d60.get("planets", {}).get(planet, {}).get("house")
def d60_sign_of(self, planet: str) -> Optional[str]:
return self.d60.get("planets", {}).get(planet, {}).get("sign")
def d60_lord_of_house(self, house: int) -> Optional[str]:
return self._d60_house_lords.get(house)
def shashtiamsa_dispositor(self, planet: str) -> Optional[str]:
sign = self.d60_sign_of(planet)
return self._resolve_sign_lord(sign) if sign else None
# --- D9 尊严查询(用于跨分盘 Yoga 判断) ---
def is_exalted_in_d9(self, planet: str) -> bool:
s = self.d9_sign_of(planet)
return s is not None and EXALTATION.get(planet) == s
def is_debilitated_in_d9(self, planet: str) -> bool:
s = self.d9_sign_of(planet)
return s is not None and DEBILITATION.get(planet) == s
def is_own_sign_in_d9(self, planet: str) -> bool:
s = self.d9_sign_of(planet)
return s is not None and self._resolve_sign_lord(s) == planet
def is_moolatrikona_in_d9(self, planet: str) -> bool:
s = self.d9_sign_of(planet)
return s is not None and MOOLATRIKONA_SIGN.get(planet) == s
def dignity_in_d9(self, planet: str) -> str:
s = self.d9_sign_of(planet)
return _get_dignity_level(planet, s) if s else 'NEUTRAL'
def is_friendly_navamsa(self, planet: str) -> bool:
"""Check if planet is in friendly sign in D9 (used by dharidhra method1)."""
s = self.d9_sign_of(planet)
if not s:
return False
lord = self._resolve_sign_lord(s)
return lord in FRIENDLY_PLANETS.get(planet, [])
def is_unfriendly_navamsa(self, planet: str) -> bool:
"""Check if planet is in unfriendly/enemy sign in D9."""
s = self.d9_sign_of(planet)
if not s:
return False
lord = self._resolve_sign_lord(s)
# Enemy if not friendly and not self
if lord == planet:
return False
return lord not in FRIENDLY_PLANETS.get(planet, [])
# --- Functional Malefic / Benefic(基于月亮盈亏和宫主关系) ---
def functional_malefics(self) -> List[str]:
"""
Return functional malefics for this chart.
Rules (per PyJHora/B.V. Raman):
- Lords of 3, 6, 8, 11, 12 are functional malefics
- Mercury is malefic if conjunct with malefic
- Moon is malefic if waning (Krishna Paksha)
"""
fm = []
dusthana_lords = [self.lord_of_house(h) for h in [3, 6, 8, 11, 12]]
fm.extend([l for l in dusthana_lords if l])
# Mercury becomes functional malefic if conjunct with natural malefic
if 'Mercury' in self.planets:
merc_h = self.house_of('Mercury')
merc_conj_malefic = any(
m in self.planets and self.house_of(m) == merc_h
for m in ['Mars', 'Saturn', 'Rahu', 'Ketu', 'Sun']
)
if merc_conj_malefic and 'Mercury' not in fm:
fm.append('Mercury')
# Waning Moon is functional malefic
if self.is_waning_moon() and 'Moon' in self.planets and 'Moon' not in fm:
fm.append('Moon')
return list(set(fm))
def functional_benefics(self) -> List[str]:
"""Return functional benefics = all planets minus functional malefics minus natural malefics."""
fm = set(self.functional_malefics())
nm = set(MALEFICS)
return [p for p in ALL_PLANETS if p in self.planets and p not in fm and p not in nm]
# --- Shashtiamsa / Vaiseshikamsa 简化评分 ---
def is_shashtiamsa_evil(self, planet: str) -> bool:
"""
Simplified evil shashtiamsa check.
In D60, if planet is debilitated, in enemy sign, or in dusthana house → evil.
"""
s = self.d60_sign_of(planet)
h = self.d60_house_of(planet)
if s is None or h is None:
return False
debil = DEBILITATION.get(planet) == s
lord = self._resolve_sign_lord(s)
enemy = lord not in FRIENDLY_PLANETS.get(planet, []) and lord != planet
dusthana = h in [6, 8, 12]
return debil or enemy or dusthana
def is_shashtiamsa_good(self, planet: str) -> bool:
"""Good shashtiamsa: exalted, own sign, or moolatrikona in D60."""
s = self.d60_sign_of(planet)
if s is None:
return False
return EXALTATION.get(planet) == s or self._resolve_sign_lord(s) == planet or MOOLATRIKONA_SIGN.get(planet) == s
def vaiseshikamsa_score(self, planet: str) -> int:
"""
Simplified Vaiseshikamsa scoring (0-20 scale, higher = better).
Combines D1 dignity + D9 dignity + D60 dignity.
"""
score = 0
# D1 dignity (0-8)
d1_dignity = self.dignity(planet)
d1_scores = {'EXALTED': 8, 'OWN_SIGN': 7, 'MOOLATRIKONA': 6, 'NEUTRAL': 3, 'DEBILITATED': 0}
score += d1_scores.get(d1_dignity, 3)
# D9 dignity (0-7)
d9_dignity = self.dignity_in_d9(planet)
score += d1_scores.get(d9_dignity, 3) - 1 # slightly lower weight
# D60 dignity (0-5)
s60 = self.d60_sign_of(planet)
if s60:
d60_dignity = _get_dignity_level(planet, s60)
d60_scores = {'EXALTED': 5, 'OWN_SIGN': 4, 'MOOLATRIKONA': 3, 'NEUTRAL': 2, 'DEBILITATED': 0}
score += d60_scores.get(d60_dignity, 2)
return score
def tithi(self) -> Optional[int]:
return self.panchanga.get("tithi")
@@ -1291,6 +1437,26 @@ class YogaEngine:
"upagraha_house": upagraha_house, "upagraha_sign": upagraha_sign,
"gulika_house": gulika_house, "maandi_house": maandi_house,
"gulika_sign": gulika_sign, "maandi_sign": maandi_sign,
# v6.1.3: D60 / Shashtiamsa 扩展
"d60_house_of": lambda p: ctx.d60_house_of(p),
"d60_sign_of": lambda p: ctx.d60_sign_of(p),
"d60_lord_of_house": lambda h: ctx.d60_lord_of_house(h),
"shashtiamsa_dispositor": lambda p: ctx.shashtiamsa_dispositor(p),
"is_shashtiamsa_evil": lambda p: ctx.is_shashtiamsa_evil(p),
"is_shashtiamsa_good": lambda p: ctx.is_shashtiamsa_good(p),
# v6.1.3: D9 尊严扩展
"is_exalted_in_d9": lambda p: ctx.is_exalted_in_d9(p),
"is_debilitated_in_d9": lambda p: ctx.is_debilitated_in_d9(p),
"is_own_sign_in_d9": lambda p: ctx.is_own_sign_in_d9(p),
"is_moolatrikona_in_d9": lambda p: ctx.is_moolatrikona_in_d9(p),
"dignity_in_d9": lambda p: ctx.dignity_in_d9(p),
"is_friendly_navamsa": lambda p: ctx.is_friendly_navamsa(p),
"is_unfriendly_navamsa": lambda p: ctx.is_unfriendly_navamsa(p),
# v6.1.3: Functional malefic / benefic
"functional_malefics": lambda: ctx.functional_malefics(),
"functional_benefics": lambda: ctx.functional_benefics(),
# v6.1.3: Vaiseshikamsa scoring
"vaiseshikamsa_score": lambda p: ctx.vaiseshikamsa_score(p),
"exal": exal, "lord_of_house": lord_of_house, "sign": sign,
"check_amala_from": check_amala_from,
"Benefics": BENEFICS, "Malefics": MALEFICS,