Release v6.0.39 Yoga source parity
This commit is contained in:
@@ -1,5 +1,52 @@
|
||||
# 印度占星 Skill 更新日志
|
||||
|
||||
## v6.0.39(2026-06-05)—— Yoga F1 90% 源码口径冲刺
|
||||
|
||||
> **目标**:继续用户要求的“准确率优先”路线,不为指标盲目调参;只在确认 PyJHora 源码口径后修改规则或 helper,使 D1/Rasi Yoga 逻辑验证首次突破 F1 90%。
|
||||
|
||||
### 引擎与 helper 修复
|
||||
|
||||
- `scripts/yoga_engine.py` 新增 PyJHora-style Scorpio/Aquarius 动态双主星 resolver:
|
||||
- Scorpio 由 `Mars` / `Ketu` 动态择强;
|
||||
- Aquarius 由 `Saturn` / `Rahu` 动态择强;
|
||||
- `_house_lords`、`_d9_house_lords`、`is_own_sign()`、`navamsa_dispositor()` 与 custom `house_strength()` 均接入动态主星口径。
|
||||
- 新增 `graha_aspects_house(p, h)`,用于显式判断 Graha Drishti 对宫位的影响。
|
||||
- 新增 `pyjhora_planets_aspecting_raasi(p, h)`,复刻 PyJHora `house.planets_aspecting_the_raasi()` 在 Bandhu Pujya Yoga 194 中的实际源码行为,避免用常规 Rasi Drishti 语义替代源码 oracle。
|
||||
|
||||
### 高影响规则对齐
|
||||
|
||||
- `bheri_yoga`:对齐 PyJHora/BVR-45,改为“9宫主有力且 1/2/7/12 宫均有行星,或 Jupiter/Venus/Lagna lord 三者互为 Kendra”。
|
||||
- `bvr_sada_sanchara_precise`:对齐 PyJHora/BVR-117,改为“Lagna lord 在 movable sign,或 Lagna lord 的 dispositor 在 movable sign”,不再错误纳入 Moon movable 条件。
|
||||
- `bvr_bandhu_pujya_yoga`:对齐 PyJHora/BVR 193-194,修复 Jupiter 与 4宫/4宫主关联判断:
|
||||
- 保留 193:benefic L4 被另一吉星相位且 Mercury 在 Lagna;
|
||||
- 修复 194:Jupiter 在4宫、PyJHora-style 影响4宫 rasi、Jupiter 本身为动态4宫主、与 L4 同宫或相位 L4。
|
||||
|
||||
### 验证结果
|
||||
|
||||
- `scripts/validate_logic_v2.py`:
|
||||
- Precision **93.82%**(v6.0.38 为 88.37%)
|
||||
- Recall **86.70%**(v6.0.38 为 81.09%)
|
||||
- F1 **90.12%**(v6.0.38 为 84.57%)
|
||||
- FP:61;FN:142
|
||||
- `scripts/run_quality_gate.py --skip-yoga-logic`:通过。
|
||||
- pytest **35 passed**
|
||||
- BPHS/Ashtakavarga 不变量 **18/18**
|
||||
- golden case 通过
|
||||
- capability audit valid,0 problem / 0 warning
|
||||
|
||||
### 规则库状态
|
||||
|
||||
- JSON 规则总数:**475**
|
||||
- 启用规则:**381**
|
||||
- 当前验证报告:`references/validation_logic_report.json`
|
||||
|
||||
### 后续优化点
|
||||
|
||||
- 下一轮 Top residual 仍建议只做源码确认后再改:`bvr_vanchana_chora_bheethi_yoga`、`bvr_kaalanirdesat_puthranaasa_yoga`、`bvr_kapata_yoga`、`bvr_bhaga_chumbana_yoga`、`bvr_mathibhramana_yoga`、`bvr_amala_precise`、`bvr_sumukha_precise`。
|
||||
- 建议继续把 PyJHora dynamic benefics/malefics、Upagraha/Gulika house-index、以及 `planets_aspecting_the_raasi()` 的边界行为抽象为可复用 helper,减少 custom 规则重复。
|
||||
|
||||
---
|
||||
|
||||
## v6.0.38(2026-06-05)—— Yoga 源码口径对齐与 F1 深水区提升
|
||||
|
||||
> **目标**:继续 v6.0.37 的 context-aware 路线,但不盲目扩规则;优先对齐 PyJHora 源码中已经明确的 house-strength、Navamsa dispositor、watery sign/planet、以及验证脚本路径口径,减少残余 Top FP/FN。
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: jyotish-vedic-astrology
|
||||
version: 6.0.38
|
||||
version: 6.0.39
|
||||
description: 印度占星(Jyotish)专业解盘与推运系统。核心能力:PDF星盘输入→严谨解盘→精确推运应期输出。触发词:印度占星、吠陀占星、Jyotish、解盘、推运、星盘分析、Dasha、Transit、Nakshatra、Yoga、出生时间矫正、PDF星盘、读取PDF、分析PDF星盘、现代解读、误判纠错、Varga分盘、综合分析、过境分析、合盘、婚姻匹配、年运盘、Prashna、Argala、Jaimini、Shadbala、Ashtakavarga、HTML报告、深度解盘。
|
||||
---
|
||||
|
||||
@@ -10,7 +10,7 @@ description: 印度占星(Jyotish)专业解盘与推运系统。核心能力
|
||||
> **严格路由**:`references/strict-workflow-router.md`(⭐涉及事业/婚恋/财务/应期/技法验证时必须优先读取)
|
||||
> **覆盖矩阵**:`references/technique-capability-matrix.md`(⭐判断技法 covered/partial/missing 时必须参考)
|
||||
> **机器注册表**:`references/technique_registry.json` + `scripts/audit_capabilities.py`(⭐用于自动审计与CI门禁)
|
||||
> **版本**:v6.0.38-yoga-source-alignment | **详细变更**:`CHANGELOG.md`
|
||||
> **版本**:v6.0.39-yoga-f1-90-source-parity | **详细变更**:`CHANGELOG.md`
|
||||
|
||||
---
|
||||
|
||||
@@ -301,9 +301,9 @@ $PYTHON $SCRIPT <子命令> [参数]
|
||||
|
||||
---
|
||||
|
||||
**版本**:v6.0.38-yoga-source-alignment
|
||||
**版本**:v6.0.39-yoga-f1-90-source-parity
|
||||
**创建日期**:2026-04-20
|
||||
**最后更新**:2026-06-05(v6.0.38 对齐 PyJHora house-strength 口径,修复 `sankha_yoga`、`kahala_yoga`、`bvr_kaahala_yoga`、`bvr_rogagrastha_precise`、`bvr_swaveeryaddhana_precise`、`bvr_bahu_puthra_precise`、`bvr_dehasthoulya_yoga` 等残余高影响规则,并固定 `validate_logic_v2.py` 从任意 cwd 运行都读写 skill 内部 references;当前 475 条 JSON 规则、381 条启用规则,D1/Rasi 逻辑验证 Precision=88.37%、Recall=81.09%、F1=84.57%,报告以 `references/validation_logic_report.json` 为准)
|
||||
**最后更新**:2026-06-05(v6.0.39 继续对齐 PyJHora Yoga 源码口径,实现 Scorpio/Aquarius 动态 co-lord resolver,修复 `bheri_yoga`、`bvr_sada_sanchara_precise`、`bvr_bandhu_pujya_yoga` 等高影响规则;当前 475 条 JSON 规则、381 条启用规则,D1/Rasi 逻辑验证 Precision=93.82%、Recall=86.70%、F1=90.12%,质量门禁通过,报告以 `references/validation_logic_report.json` 为准)
|
||||
|
||||
---
|
||||
|
||||
|
||||
+345
-1119
File diff suppressed because it is too large
Load Diff
+21
-100
@@ -1300,8 +1300,8 @@
|
||||
"category": "auspicious",
|
||||
"logic": {
|
||||
"type": "custom",
|
||||
"expr": "\nlord_9 = lord(9)\nif lord_9 not in ctx.planets or house_of(lord_9) != 2:\n False\nelse:\n jv_in_kendra = [p for p in ['Jupiter', 'Venus'] if p in ctx.planets and kendra(house_of(p))]\n bool(jv_in_kendra)\n",
|
||||
"combo_template": "9宫主在2宫+木星/金星在角宫"
|
||||
"expr": "\n# PyJHora/BVR-45 Bheri Yoga. 9th lord strong and either 1/2/7/12 occupied,\n# or Jupiter, Venus and Lagna lord are pairwise in mutual kendras.\nl1 = lord(1)\nl9 = lord(9)\nif l1 not in ctx.planets or l9 not in ctx.planets:\n False\nelse:\n ninth_strong = strong(l9, include_neutral=True)\n path_a = all(bool(occupants(h)) for h in [1, 2, 7, 12])\n def mutual_kendra_planets(a, b):\n return a in ctx.planets and b in ctx.planets and offset(house_of(a), house_of(b)) in [0, 3, 6, 9] and offset(house_of(b), house_of(a)) in [0, 3, 6, 9]\n path_b = mutual_kendra_planets('Jupiter', 'Venus') and mutual_kendra_planets('Jupiter', l1) and mutual_kendra_planets('Venus', l1)\n ninth_strong and (path_a or path_b)\n",
|
||||
"combo_template": "PyJHora aligned Bheri Yoga: 9宫主强且满足占宫/互为角宫条件"
|
||||
},
|
||||
"effects": [
|
||||
"品德高尚",
|
||||
@@ -1322,7 +1322,7 @@
|
||||
"category": "auspicious",
|
||||
"logic": {
|
||||
"type": "custom",
|
||||
"expr": "\nasc_lord = SIGN_LORDS.get(ctx.ascendant)\nif asc_lord not in ctx.planets:\n False\nelse:\n asc_strong = exalted(asc_lord) or own(asc_lord)\n other_strong = [p for p in ctx.planets if p != asc_lord and p not in ['Rahu', 'Ketu'] and (exalted(p) or own(p))]\n asc_strong and bool(other_strong)\n",
|
||||
"expr": "\n# PyJHora/BVR-46: planets with strength > FRIEND in both quadrants and trines,\n# and Lagna lord strength > FRIEND. Strength > FRIEND means own/exalted only.\nl1 = lord(1)\nif l1 not in ctx.planets:\n False\nelse:\n strong_kendra = any(p in ctx.planets and house_of(p) in [1, 4, 7, 10] and house_strength(p) is not None and house_strength(p) > 3 for p in PLANET_INDEX)\n strong_trine = any(p in ctx.planets and house_of(p) in [1, 5, 9] and house_strength(p) is not None and house_strength(p) > 3 for p in PLANET_INDEX)\n strong_kendra and strong_trine and house_strength(l1) is not None and house_strength(l1) > 3\n",
|
||||
"combo_template": "上升主星有力+其他行星有力"
|
||||
},
|
||||
"effects": [
|
||||
@@ -8217,15 +8217,8 @@
|
||||
"bvr_id": "BVR-2",
|
||||
"category": "chandra",
|
||||
"logic": {
|
||||
"type": "has_planet_in_house_from",
|
||||
"planets": "all",
|
||||
"from": "Moon",
|
||||
"houses": [
|
||||
2
|
||||
],
|
||||
"exclude": [
|
||||
"Sun"
|
||||
],
|
||||
"type": "custom",
|
||||
"expr": "\n# PyJHora/BVR-2 Sunapha: target 2nd from Moon has at least one planet and Sun is absent.\nmoon_h = house_of('Moon')\nif moon_h is None:\n False\nelse:\n target = ((moon_h - 1 + 1) % 12) + 1\n ps = occupants(target)\n bool(ps) and 'Sun' not in ps\n",
|
||||
"combo_template": "{planets}在月亮的第2宫"
|
||||
},
|
||||
"effects": [
|
||||
@@ -8248,15 +8241,8 @@
|
||||
"bvr_id": "BVR-3",
|
||||
"category": "chandra",
|
||||
"logic": {
|
||||
"type": "has_planet_in_house_from",
|
||||
"planets": "all",
|
||||
"from": "Moon",
|
||||
"houses": [
|
||||
12
|
||||
],
|
||||
"exclude": [
|
||||
"Sun"
|
||||
],
|
||||
"type": "custom",
|
||||
"expr": "\n# PyJHora/BVR-3 Anapha: target 12th from Moon has at least one planet and Sun is absent.\nmoon_h = house_of('Moon')\nif moon_h is None:\n False\nelse:\n target = ((moon_h - 1 + 11) % 12) + 1\n ps = occupants(target)\n bool(ps) and 'Sun' not in ps\n",
|
||||
"combo_template": "{planets}在月亮的第12宫"
|
||||
},
|
||||
"effects": [
|
||||
@@ -8279,33 +8265,9 @@
|
||||
"bvr_id": "BVR-4",
|
||||
"category": "chandra",
|
||||
"logic": {
|
||||
"type": "and",
|
||||
"conditions": [
|
||||
{
|
||||
"type": "has_planet_in_house_from",
|
||||
"planets": "all",
|
||||
"from": "Moon",
|
||||
"houses": [
|
||||
2
|
||||
],
|
||||
"exclude": [
|
||||
"Sun"
|
||||
],
|
||||
"combo_template": "{planets}在月亮的第2宫"
|
||||
},
|
||||
{
|
||||
"type": "has_planet_in_house_from",
|
||||
"planets": "all",
|
||||
"from": "Moon",
|
||||
"houses": [
|
||||
12
|
||||
],
|
||||
"exclude": [
|
||||
"Sun"
|
||||
],
|
||||
"combo_template": "{planets}在月亮的第12宫"
|
||||
}
|
||||
]
|
||||
"type": "custom",
|
||||
"expr": "\n# PyJHora/BVR-4 Duradhara: both Sunapha and Anapha conditions are true.\nmoon_h = house_of('Moon')\nif moon_h is None:\n False\nelse:\n h2 = ((moon_h - 1 + 1) % 12) + 1\n h12 = ((moon_h - 1 + 11) % 12) + 1\n ps2 = occupants(h2)\n ps12 = occupants(h12)\n bool(ps2) and 'Sun' not in ps2 and bool(ps12) and 'Sun' not in ps12\n",
|
||||
"combo_template": "PyJHora Chandra yoga logic"
|
||||
},
|
||||
"effects": [
|
||||
"月亮前后均有支撑",
|
||||
@@ -9194,7 +9156,7 @@
|
||||
"category": "travel",
|
||||
"logic": {
|
||||
"type": "custom",
|
||||
"expr": "\n# Lagna lord or Moon in movable sign (Chara Rashi)\nchara_signs = {'Aries', 'Cancer', 'Libra', 'Capricorn'}\nasc_lord = lord(1)\nin_chara = False\nif asc_lord in ctx.planets:\n if ctx.planets[asc_lord].get('sign') in chara_signs:\n in_chara = True\nif 'Moon' in ctx.planets and ctx.planets['Moon'].get('sign') in chara_signs:\n in_chara = True\nin_chara\n",
|
||||
"expr": "\n# PyJHora/BVR-117 Sada Sanchara: Lagna lord OR the dispositor of Lagna lord is in a movable sign.\nl1 = lord(1)\nif l1 not in ctx.planets:\n False\nelse:\n l1_h = house_of(l1)\n l1_dispositor = lord(l1_h) if l1_h else None\n sign_of(l1) in MOVABLE_SIGNS or (l1_dispositor in ctx.planets and sign_of(l1_dispositor) in MOVABLE_SIGNS)\n",
|
||||
"combo_template": "上升主或月亮在变动星座"
|
||||
},
|
||||
"effects": [
|
||||
@@ -9259,7 +9221,7 @@
|
||||
"category": "auspicious",
|
||||
"logic": {
|
||||
"type": "custom",
|
||||
"expr": "\n# PyJHora/BVR-177: 3rd lord, Mars, or 3rd house joined/aspected by benefics.\nlord_3 = lord(3)\nhouse_3 = 3\nbenefics = [p for p in BENEFICS if p in ctx.planets]\ndef benefic_link_to_planet(p):\n return p in ctx.planets and strong(p) and any(b != p and (same_house(b, p) or aspect(b, p)) for b in benefics)\nhouse_joined = any(house_of(b) == house_3 for b in benefics)\nhouse_aspected = any(aspects_house(b, house_3) for b in benefics)\nbenefic_link_to_planet(lord_3) or benefic_link_to_planet('Mars') or house_joined or house_aspected\n",
|
||||
"expr": "\n# PyJHora/BVR-177: 3rd lord, Mars, or 3rd house joined/aspected by benefics and strong.\n# Planet-to-house aspect uses rasi drishti; planet-to-planet uses graha drishti.\nlord_3 = lord(3)\nbenefics = [p for p in BENEFICS if p in ctx.planets]\ndef benefic_link_to_planet(p):\n return p in ctx.planets and strong(p) and any(b != p and (same_house(b, p) or aspect(b, p)) for b in benefics)\nhouse_joined = any(house_of(b) == 3 for b in benefics)\nhouse_aspected = any(rasi_aspects_house(b, 3) for b in benefics)\nbenefic_link_to_planet(lord_3) or benefic_link_to_planet('Mars') or house_joined or house_aspected\n",
|
||||
"combo_template": "3宫主、火星或3宫受吉星连接"
|
||||
},
|
||||
"effects": [
|
||||
@@ -9301,7 +9263,7 @@
|
||||
"category": "durbhaga",
|
||||
"logic": {
|
||||
"type": "custom",
|
||||
"expr": "\n# PyJHora/BVR 198-199.\nif 'Moon' not in ctx.planets:\n False\nelse:\n evil = set(MALEFICS)\n moon_h = house_of('Moon')\n prev_h = ((moon_h - 2) % 12) + 1 if moon_h else None\n next_h = (moon_h % 12) + 1 if moon_h else None\n hemmed = bool(prev_h and next_h and house_has_malefic(prev_h) and house_has_malefic(next_h))\n associated = any(same_house('Moon', p) for p in evil if p in ctx.planets)\n aspected = any(aspect(p, 'Moon') for p in evil if p in ctx.planets)\n y198 = hemmed or associated or aspected\n l4 = lord(4)\n nav_lord = navamsa_dispositor(l4) if l4 else None\n final_planet = navamsa_dispositor(nav_lord) if nav_lord else None\n y199 = final_planet in ctx.planets and house_of(final_planet) in [6, 8, 12]\n y198 or y199\n",
|
||||
"expr": "\n# PyJHora/BVR 198-199. Yoga 198 uses natural malefics and rasi drishti on Moon's rasi.\nif 'Moon' not in ctx.planets:\n False\nelse:\n moon_h = house_of('Moon')\n prev_h = ((moon_h - 2) % 12) + 1 if moon_h else None\n next_h = (moon_h % 12) + 1 if moon_h else None\n hemmed = bool(prev_h and next_h and house_has_malefic(prev_h) and house_has_malefic(next_h))\n associated = any(same_house('Moon', p) for p in MALEFICS if p in ctx.planets)\n aspected = any(rasi_aspects_house(p, moon_h) for p in MALEFICS if p in ctx.planets)\n y198 = hemmed or associated or aspected\n l4 = lord(4)\n nav_lord = navamsa_dispositor(l4) if l4 else None\n final_planet = navamsa_dispositor(nav_lord) if nav_lord else None\n y199 = final_planet in ctx.planets and house_of(final_planet) in [6, 8, 12]\n y198 or y199\n",
|
||||
"combo_template": "月亮受凶星夹制/合相/相位,或4宫主Navamsa链最终落6/8/12"
|
||||
},
|
||||
"effects": [
|
||||
@@ -9755,24 +9717,9 @@
|
||||
"bvr_id": null,
|
||||
"category": "kalatra",
|
||||
"logic": {
|
||||
"type": "compound_conditions",
|
||||
"conditions": [
|
||||
{
|
||||
"type": "compound",
|
||||
"operator": "AND",
|
||||
"conditions": [
|
||||
{
|
||||
"type": "lords_in_mutual_conjunction_or_aspect",
|
||||
"lords": [
|
||||
1,
|
||||
7
|
||||
],
|
||||
"note": "Lords of Lagna and 7th in conjunction or aspect with each other"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"note": ""
|
||||
"type": "custom",
|
||||
"expr": "\n# PyJHora/BVR-238: Lords of Lagna and 7th in conjunction or mutual graha aspect.\nl1 = lord(1)\nl7 = lord(7)\nif l1 not in ctx.planets or l7 not in ctx.planets:\n False\nelse:\n same_house(l1, l7) or (aspect(l1, l7) and aspect(l7, l1))\n",
|
||||
"combo_template": "PyJHora aligned source logic"
|
||||
},
|
||||
"effects": [
|
||||
"Association with many women"
|
||||
@@ -9835,35 +9782,9 @@
|
||||
"bvr_id": null,
|
||||
"category": "special",
|
||||
"logic": {
|
||||
"type": "compound_conditions",
|
||||
"conditions": [
|
||||
{
|
||||
"type": "compound",
|
||||
"operator": "OR",
|
||||
"conditions": [
|
||||
{
|
||||
"type": "compound",
|
||||
"operator": "AND",
|
||||
"conditions": [
|
||||
{
|
||||
"type": "4th_lord_is_benefic_aspected_by_benefic",
|
||||
"note": "Benefic 4th lord aspected by another benefic"
|
||||
},
|
||||
{
|
||||
"type": "planet_in_lagna",
|
||||
"planet": "Mercury",
|
||||
"note": "Mercury in Lagna"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "4th_house_or_lord_with_aspect_of_jupiter",
|
||||
"note": "4th house or 4th lord has association/aspect of Jupiter"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"note": ""
|
||||
"type": "custom",
|
||||
"expr": "\n# PyJHora/BVR 193-194. Yoga 194 treats Jupiter associated with the 4th house/lord.\n# Source parity note: PyJHora uses planets_aspecting_the_raasi() for the 4th-rasi clause,\n# and regards Jupiter as conjoining L4 when Jupiter itself is the dynamic 4th lord.\nl4 = lord(4)\nif l4 not in ctx.planets:\n False\nelse:\n benefics = [p for p in BENEFICS if p in ctx.planets]\n y193 = l4 in BENEFICS and house_of('Mercury') == 1 and any(b != l4 and aspect(b, l4) for b in benefics)\n y194 = 'Jupiter' in ctx.planets and (house_of('Jupiter') == 4 or pyjhora_planets_aspecting_raasi('Jupiter', 4) or l4 == 'Jupiter' or same_house('Jupiter', l4) or aspect('Jupiter', l4))\n y193 or y194\n",
|
||||
"combo_template": "PyJHora aligned source logic"
|
||||
},
|
||||
"effects": [
|
||||
"Respected and honored by relatives"
|
||||
@@ -11237,7 +11158,7 @@
|
||||
"category": "putra",
|
||||
"logic": {
|
||||
"type": "custom",
|
||||
"expr": "\n# PyJHora 229/230 D1-safe logic. Variant 229: Rahu in 5th, L5 joined\n# malefic, Jupiter debilitated. Variant 230: malefics occupy/aspect both\n# 5th from Lagna and 5th from Jupiter.\nl5 = lord(5)\nif l5 not in ctx.planets or 'Jupiter' not in ctx.planets:\n False\nelse:\n cond229 = house_of('Rahu') == 5 and debil('Jupiter') and any(m in ctx.planets and same_house(l5, m) for m in MALEFICS if m != l5)\n j5 = ((house_of('Jupiter') - 1 + 4) % 12) + 1\n mal_lagna5 = any(m in ctx.planets and (house_of(m) == 5 or aspects_house(m, 5)) for m in MALEFICS)\n mal_jupiter5 = any(m in ctx.planets and (house_of(m) == j5 or aspects_house(m, j5)) for m in MALEFICS)\n cond229 or (mal_lagna5 and mal_jupiter5)\n",
|
||||
"expr": "\n# PyJHora 229/230. Variant 229: Rahu in 5th, L5 joined malefic, Jupiter debilitated.\n# Variant 230: natural malefics occupy or rasi-aspect both 5th from Lagna and 5th from Jupiter.\nl5 = lord(5)\nif l5 not in ctx.planets or 'Jupiter' not in ctx.planets:\n False\nelse:\n cond229 = house_of('Rahu') == 5 and debil('Jupiter') and any(m in ctx.planets and same_house(l5, m) for m in MALEFICS)\n j5 = ((house_of('Jupiter') - 1 + 4) % 12) + 1\n mal_lagna5 = any(m in ctx.planets and (house_of(m) == 5 or rasi_aspects_house(m, 5)) for m in MALEFICS)\n mal_jupiter5 = any(m in ctx.planets and (house_of(m) == j5 or rasi_aspects_house(m, j5)) for m in MALEFICS)\n cond229 or (mal_lagna5 and mal_jupiter5)\n",
|
||||
"combo_template": "Rahu/L5/木星或凶星影响Lagna与木星起算第5宫"
|
||||
},
|
||||
"effects": [
|
||||
@@ -13795,7 +13716,7 @@
|
||||
"category": "durbhaga",
|
||||
"logic": {
|
||||
"type": "custom",
|
||||
"expr": "\n# PyJHora/BVR 202-204 Kapata Yoga.\nevil = set(MALEFICS)\nl4 = lord(4)\nl10 = lord(10)\nmaandi_h = maandi_house()\nif l4 not in ctx.planets or l10 not in ctx.planets:\n False\nelse:\n # 202: 4th joined by a malefic; 4th lord joined/aspected/hemmed by malefics.\n malefic_in_4 = any(house_of(m) == 4 for m in evil if m in ctx.planets)\n l4_h = house_of(l4)\n l4_joined_malefic = any(m != l4 and same_house(l4, m) for m in evil if m in ctx.planets)\n l4_aspected_malefic = any(aspect(m, l4) for m in evil if m in ctx.planets)\n prev_h = ((l4_h - 2) % 12) + 1 if l4_h else None\n next_h = (l4_h % 12) + 1 if l4_h else None\n l4_hemmed = bool(prev_h and next_h and house_has_malefic(prev_h) and house_has_malefic(next_h))\n yoga202 = malefic_in_4 and (l4_joined_malefic or l4_aspected_malefic or l4_hemmed)\n # 203: Saturn, Mars, Rahu in 4th; malefic 10th lord aspected by malefics.\n yoga203 = all(house_of(p) == 4 for p in ['Saturn', 'Mars', 'Rahu']) and l10 in evil and any(aspect(m, l10) for m in evil if m in ctx.planets and m != l10)\n # 204: 4th lord joins Saturn, Mandi, Rahu and is aspected by malefics.\n yoga204 = bool(maandi_h and house_of(l4) == house_of('Saturn') == house_of('Rahu') == maandi_h and any(aspect(m, l4) for m in evil if m in ctx.planets and m != l4))\n yoga202 or yoga203 or yoga204\n",
|
||||
"expr": "\n# PyJHora/BVR 202-204 Kapata Yoga; house aspects use rasi drishti, planet aspects use graha drishti.\nl4 = lord(4)\nl10 = lord(10)\nmaandi_h = maandi_house()\nif l4 not in ctx.planets or l10 not in ctx.planets:\n False\nelse:\n malefic_in_4 = any(house_of(m) == 4 for m in MALEFICS if m in ctx.planets)\n l4_h = house_of(l4)\n l4_joined_malefic = any(same_house(l4, m) for m in MALEFICS if m in ctx.planets)\n l4_aspected_malefic = any(aspect(m, l4) for m in MALEFICS if m in ctx.planets)\n prev_h = ((l4_h - 2) % 12) + 1 if l4_h else None\n next_h = (l4_h % 12) + 1 if l4_h else None\n l4_hemmed = bool(prev_h and next_h and house_has_malefic(prev_h) and house_has_malefic(next_h))\n yoga202 = malefic_in_4 and (l4_joined_malefic or l4_aspected_malefic or l4_hemmed)\n yoga203 = all(house_of(p) == 4 for p in ['Saturn', 'Mars', 'Rahu']) and l10 in MALEFICS and any(aspect(m, l10) for m in MALEFICS if m in ctx.planets)\n yoga204 = bool(maandi_h and house_of('Saturn') == maandi_h == house_of('Rahu') == l4_h and any(aspect(m, l4) for m in MALEFICS if m in ctx.planets))\n yoga202 or yoga203 or yoga204\n",
|
||||
"combo_template": "4宫/4宫主受凶星、Rahu、Maandi/Mandi 等严重影响"
|
||||
},
|
||||
"effects": [
|
||||
|
||||
+163
-7
@@ -45,8 +45,11 @@ PLANET_CN = {
|
||||
BENEFICS = ['Jupiter', 'Venus', 'Mercury', 'Moon']
|
||||
MALEFICS = ['Mars', 'Saturn', 'Sun', 'Rahu', 'Ketu']
|
||||
MOVABLE_SIGNS = ['Aries', 'Cancer', 'Libra', 'Capricorn']
|
||||
FIXED_SIGNS = ['Taurus', 'Leo', 'Scorpio', 'Aquarius']
|
||||
DUAL_SIGNS = ['Gemini', 'Virgo', 'Sagittarius', 'Pisces']
|
||||
ALL_PLANETS = ['Sun', 'Moon', 'Mars', 'Mercury', 'Jupiter', 'Venus', 'Saturn', 'Rahu', 'Ketu']
|
||||
PLANET_INDEX = {planet: idx for idx, planet in enumerate(ALL_PLANETS)}
|
||||
RAHU_KETU_OWNED_SIGN_INDEX = {'Rahu': 10, 'Ketu': 7}
|
||||
# PyJHora const.house_strengths_of_planets, copied to make Yoga rule alignment deterministic.
|
||||
# Strength scale: 5 own/lord, 4 exalted, 3 friend, 2 neutral, 1 enemy, 0 debilitated.
|
||||
HOUSE_STRENGTHS = {
|
||||
@@ -90,11 +93,13 @@ class YogaContext:
|
||||
self.panchanga = self.context.get("panchanga", {}) if isinstance(self.context, dict) else {}
|
||||
self.upagraha = self.context.get("upagraha", {}) if isinstance(self.context, dict) else {}
|
||||
|
||||
# 预计算 D1 宫主星
|
||||
# 预计算 D1 宫主星。v6.0.39: Scorpio/Aquarius follow PyJHora's dynamic co-lord resolver
|
||||
# (Scorpio: stronger of Mars/Ketu; Aquarius: stronger of Saturn/Rahu), while SIGN_LORDS stays
|
||||
# as the classical fixed fallback for dignity tables and non-Jaimini contexts.
|
||||
self._house_lords: Dict[int, str] = {}
|
||||
for h in range(1, 13):
|
||||
sign = SIGNS[(self.asc_idx + h - 1) % 12]
|
||||
self._house_lords[h] = SIGN_LORDS[sign]
|
||||
self._house_lords[h] = self._resolve_sign_lord(sign)
|
||||
|
||||
# 预计算 D9 宫主星(用于依赖 Navamsa 的 B.V. Raman Yoga)
|
||||
d9_asc = self.d9.get("ascendant")
|
||||
@@ -103,7 +108,104 @@ class YogaContext:
|
||||
if d9_asc_idx is not None:
|
||||
for h in range(1, 13):
|
||||
sign = SIGNS[(d9_asc_idx + h - 1) % 12]
|
||||
self._d9_house_lords[h] = SIGN_LORDS[sign]
|
||||
self._d9_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
|
||||
|
||||
def _rasi_drishti_sign_indices_from(self, sign_idx: int) -> List[int]:
|
||||
sign_name = SIGNS[sign_idx]
|
||||
if sign_name in MOVABLE_SIGNS:
|
||||
return [i for i in [1, 4, 7, 10] if i not in ((sign_idx + 1) % 12, (sign_idx - 1) % 12)]
|
||||
if sign_name in FIXED_SIGNS:
|
||||
return [i for i in [0, 3, 6, 9] if i not in ((sign_idx + 1) % 12, (sign_idx - 1) % 12)]
|
||||
return [SIGNS.index(s) for s in DUAL_SIGNS if SIGNS.index(s) != sign_idx]
|
||||
|
||||
def _stronger_co_lord(self, planet1: str, planet2: str) -> str:
|
||||
"""Approximate PyJHora stronger_planet_from_planet_positions() for Sc/Aq co-lords."""
|
||||
h1 = self._sign_index_of_planet(planet1)
|
||||
h2 = self._sign_index_of_planet(planet2)
|
||||
if h1 is None:
|
||||
return planet2
|
||||
if h2 is None:
|
||||
return planet1
|
||||
|
||||
node_owned_sign = RAHU_KETU_OWNED_SIGN_INDEX.get(planet1) or RAHU_KETU_OWNED_SIGN_INDEX.get(planet2)
|
||||
if node_owned_sign is not None:
|
||||
if h1 == node_owned_sign and h2 != node_owned_sign:
|
||||
return planet2
|
||||
if h2 == node_owned_sign and h1 != node_owned_sign:
|
||||
return planet1
|
||||
|
||||
planet1_co_count = sum(1 for p in self.planets if self._sign_index_of_planet(p) == h1) - 1
|
||||
planet2_co_count = sum(1 for p in self.planets if self._sign_index_of_planet(p) == h2) - 1
|
||||
if planet1_co_count > planet2_co_count:
|
||||
return planet1
|
||||
if planet2_co_count > planet1_co_count:
|
||||
return planet2
|
||||
|
||||
dispositor1 = self._resolve_sign_lord(SIGNS[h1], dynamic_co_lords=False)
|
||||
dispositor2 = self._resolve_sign_lord(SIGNS[h2], dynamic_co_lords=False)
|
||||
support1 = sum(
|
||||
self._sign_index_of_planet(p) == h1
|
||||
for p in ['Mercury', 'Jupiter', dispositor1]
|
||||
if p in self.planets
|
||||
)
|
||||
support1 += sum(
|
||||
p in self.planets and self._sign_index_of_planet(p) in self._rasi_drishti_sign_indices_from(h1)
|
||||
for p in ['Mercury', 'Jupiter', dispositor1]
|
||||
)
|
||||
support2 = sum(
|
||||
self._sign_index_of_planet(p) == h2
|
||||
for p in ['Mercury', 'Jupiter', dispositor2]
|
||||
if p in self.planets
|
||||
)
|
||||
support2 += sum(
|
||||
p in self.planets and self._sign_index_of_planet(p) in self._rasi_drishti_sign_indices_from(h2)
|
||||
for p in ['Mercury', 'Jupiter', dispositor2]
|
||||
)
|
||||
if support1 > support2:
|
||||
return planet1
|
||||
if support2 > support1:
|
||||
return planet2
|
||||
|
||||
strength1 = HOUSE_STRENGTHS.get(planet1, [None] * 12)[h1]
|
||||
strength2 = HOUSE_STRENGTHS.get(planet2, [None] * 12)[h2]
|
||||
if strength1 == 4 and strength2 is not None and strength1 > strength2:
|
||||
return planet1
|
||||
if strength2 == 4 and strength1 is not None and strength2 > strength1:
|
||||
return planet2
|
||||
|
||||
def modality_rank(sign_idx: int) -> int:
|
||||
sign_name = SIGNS[sign_idx]
|
||||
if sign_name in DUAL_SIGNS:
|
||||
return 3
|
||||
if sign_name in FIXED_SIGNS:
|
||||
return 2
|
||||
return 1
|
||||
|
||||
rank1 = modality_rank(h1)
|
||||
rank2 = modality_rank(h2)
|
||||
if rank1 > rank2:
|
||||
return planet1
|
||||
if rank2 > rank1:
|
||||
return planet2
|
||||
|
||||
degree1 = self.degree_of(planet1)
|
||||
degree2 = self.degree_of(planet2)
|
||||
if degree1 is not None and degree2 is not None:
|
||||
return planet1 if degree1 > degree2 else planet2
|
||||
return planet1
|
||||
|
||||
def _resolve_sign_lord(self, sign: str, dynamic_co_lords: bool = True) -> Optional[str]:
|
||||
if not dynamic_co_lords:
|
||||
return SIGN_LORDS.get(sign)
|
||||
if sign == 'Scorpio':
|
||||
return self._stronger_co_lord('Mars', 'Ketu')
|
||||
if sign == 'Aquarius':
|
||||
return self._stronger_co_lord('Saturn', 'Rahu')
|
||||
return SIGN_LORDS.get(sign)
|
||||
|
||||
# --- 基础查询 ---
|
||||
def house_of(self, planet: str) -> Optional[int]:
|
||||
@@ -133,7 +235,7 @@ class YogaContext:
|
||||
|
||||
def navamsa_dispositor(self, planet: str) -> Optional[str]:
|
||||
sign = self.d9_sign_of(planet)
|
||||
return SIGN_LORDS.get(sign) if sign else None
|
||||
return self._resolve_sign_lord(sign) if sign else None
|
||||
|
||||
def tithi(self) -> Optional[int]:
|
||||
return self.panchanga.get("tithi")
|
||||
@@ -156,7 +258,7 @@ class YogaContext:
|
||||
|
||||
def is_own_sign(self, planet: str) -> bool:
|
||||
s = self.sign_of(planet)
|
||||
return s is not None and SIGN_LORDS.get(s) == planet
|
||||
return s is not None and self._resolve_sign_lord(s) == planet
|
||||
|
||||
def is_moolatrikona(self, planet: str) -> bool:
|
||||
s = self.sign_of(planet)
|
||||
@@ -962,6 +1064,48 @@ class YogaEngine:
|
||||
return False
|
||||
return h == ctx.house_of(p) or offset(ctx.house_of(p), h) in (6, 3 if p == 'Mars' else -1, 7 if p == 'Mars' else -1, 4 if p == 'Jupiter' else -1, 8 if p == 'Jupiter' else -1, 2 if p == 'Saturn' else -1, 9 if p == 'Saturn' else -1)
|
||||
|
||||
def graha_aspects_house(p, h):
|
||||
if p not in ctx.planets or h is None:
|
||||
return False
|
||||
return offset(ctx.house_of(p), h) in (6, 3 if p == 'Mars' else -1, 7 if p == 'Mars' else -1, 4 if p == 'Jupiter' else -1, 8 if p == 'Jupiter' else -1, 2 if p == 'Saturn' else -1, 9 if p == 'Saturn' else -1)
|
||||
|
||||
def pyjhora_planets_aspecting_raasi(p, h):
|
||||
"""Replicate PyJHora house.planets_aspecting_the_raasi() behavior for source parity."""
|
||||
if p not in ctx.planets or h is None:
|
||||
return False
|
||||
sign_name = ctx.sign_of(p)
|
||||
if sign_name not in SIGNS:
|
||||
return False
|
||||
target_rasi_idx = (ctx.asc_idx + h - 1) % 12
|
||||
aspected_signs = rasi_drishti_signs_from(sign_name)
|
||||
planet_ids_in_aspected_signs = [
|
||||
PLANET_INDEX[q]
|
||||
for q in ctx.planets
|
||||
if q in PLANET_INDEX and ctx.sign_of(q) in aspected_signs
|
||||
]
|
||||
return target_rasi_idx in planet_ids_in_aspected_signs
|
||||
|
||||
def rasi_drishti_signs_from(sign_name):
|
||||
if sign_name not in SIGNS:
|
||||
return []
|
||||
sign_idx = SIGNS.index(sign_name)
|
||||
if sign_name in MOVABLE_SIGNS:
|
||||
return [SIGNS[i] for i in [1, 4, 7, 10] if i not in ((sign_idx + 1) % 12, (sign_idx - 1) % 12)]
|
||||
if sign_name in FIXED_SIGNS:
|
||||
return [SIGNS[i] for i in [0, 3, 6, 9] if i not in ((sign_idx + 1) % 12, (sign_idx - 1) % 12)]
|
||||
return [s for s in DUAL_SIGNS if s != sign_name]
|
||||
|
||||
def rasi_aspects_house(p, h):
|
||||
if p not in ctx.planets or h is None:
|
||||
return False
|
||||
return house_sign(h) in rasi_drishti_signs_from(ctx.sign_of(p))
|
||||
|
||||
def rasi_aspects(a, b):
|
||||
return a in ctx.planets and b in ctx.planets and rasi_aspects_house(a, ctx.house_of(b))
|
||||
|
||||
def rasi_aspected_by_planets(h):
|
||||
return [p for p in ctx.planets if rasi_aspects_house(p, h)]
|
||||
|
||||
def sign_index_of_planet(p):
|
||||
sign_name = ctx.sign_of(p)
|
||||
return SIGNS.index(sign_name) if sign_name in SIGNS else None
|
||||
@@ -970,7 +1114,13 @@ class YogaEngine:
|
||||
sign_idx = sign_index_of_planet(p)
|
||||
if sign_idx is None or p not in HOUSE_STRENGTHS:
|
||||
return None
|
||||
return HOUSE_STRENGTHS[p][sign_idx]
|
||||
strength = HOUSE_STRENGTHS[p][sign_idx]
|
||||
sign_name = SIGNS[sign_idx]
|
||||
# v6.0.39: PyJHora dynamic co-lord owner strength for Scorpio/Aquarius.
|
||||
# Without this, Ketu/Rahu can become house lords but still look non-owner in strength checks.
|
||||
if sign_name in ('Scorpio', 'Aquarius') and ctx._resolve_sign_lord(sign_name) == p:
|
||||
return max(strength, 5)
|
||||
return strength
|
||||
|
||||
def strong(p, include_neutral=False):
|
||||
strength = house_strength(p)
|
||||
@@ -1061,10 +1211,11 @@ class YogaEngine:
|
||||
"list": list, "bool": bool,
|
||||
"ctx": ctx, "bindings": bindings, "rule": rule,
|
||||
"SIGNS": SIGNS, "SIGN_LORDS": SIGN_LORDS,
|
||||
"RAHU_KETU_OWNED_SIGN_INDEX": RAHU_KETU_OWNED_SIGN_INDEX,
|
||||
"EXALTATION": EXALTATION, "DEBILITATION": DEBILITATION,
|
||||
"PLANET_CN": PLANET_CN, "BENEFICS": BENEFICS, "MALEFICS": MALEFICS,
|
||||
"MOVABLE_SIGNS": MOVABLE_SIGNS, "FIXED_SIGNS": FIXED_SIGNS, "DUAL_SIGNS": DUAL_SIGNS,
|
||||
"PLANET_INDEX": PLANET_INDEX, "HOUSE_STRENGTHS": HOUSE_STRENGTHS,
|
||||
"MOVABLE_SIGNS": MOVABLE_SIGNS,
|
||||
"ALL_PLANETS": ALL_PLANETS, "MOOLATRIKONA_SIGN": MOOLATRIKONA_SIGN,
|
||||
# 基础查询
|
||||
"house_of": house_of, "sign_of": sign_of, "deg_of": deg_of,
|
||||
@@ -1086,6 +1237,11 @@ class YogaEngine:
|
||||
"offset": offset,
|
||||
# v6.0.32: 同宫与相位检查(custom规则常用)
|
||||
"same_house": same_house, "aspect": aspect, "aspects_house": aspects_house,
|
||||
"graha_aspects_house": graha_aspects_house,
|
||||
"pyjhora_planets_aspecting_raasi": pyjhora_planets_aspecting_raasi,
|
||||
"rasi_drishti_signs_from": rasi_drishti_signs_from,
|
||||
"rasi_aspects_house": rasi_aspects_house, "rasi_aspects": rasi_aspects,
|
||||
"rasi_aspected_by_planets": rasi_aspected_by_planets,
|
||||
"house_strength": house_strength, "strong": strong, "weak": weak,
|
||||
"associated": associated, "temporal_friend": temporal_friend,
|
||||
"occupants": occupants, "only_benefics_in_house": only_benefics_in_house,
|
||||
|
||||
Reference in New Issue
Block a user