feat(narayana-rath): Rahu/Ketu dignity and debilitation at level 3 (BUG-1224)
Round-6 ruling Q2: node dignity by Rath Table 9 p.41-42 (own Aq/Sc, MT Vi/Pi, Phalita exaltation Ge/Sg), debilitated ranks below none (p.67: debilitated Ketu weaker than Mars by Rule 3). Rath module only; natal table unchanged. Book acceptance unchanged (15/17 general, 18 explicit 12/12, 28/30, 24/36). Goldens recaptured (only rath.source text changes). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N4f2nya58RoRu4yEmJgRGE
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
7dda1b45c3
commit
61bd4425cd
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+43
-10
@@ -14,8 +14,10 @@ p.63]:
|
||||
role counted once when the planet is in the sign or casts Rasi Drishti on
|
||||
it [p.48; no graha drishti]; roles are not de-duplicated by planet
|
||||
(Jupiter as Jupiter and as lord counts twice) [p.36];
|
||||
3. occupant dignity: exalted > moolatrikona > own sign (whole sign) — best
|
||||
rank, then the number of dignified occupants;
|
||||
3. occupant dignity: exalted > moolatrikona > own sign (whole sign) > none >
|
||||
debilitated — best rank, then the number of dignified occupants; Rahu /
|
||||
Ketu by Rath Table 9 (p.41-42, Phalita set; round-6 ruling Q2,
|
||||
BUG-1224), debilitation below none per p.67;
|
||||
4. dual > fixed > movable;
|
||||
5. the lord's degree inside its sign, Chara Karaka sense (higher is
|
||||
stronger; Rahu / Ketu as 30 - degree);
|
||||
@@ -98,7 +100,8 @@ SOURCE = {
|
||||
"md_order": "p.51 Table 10; Saturn p.51-53 Table 11; Ketu p.51, p.53 Table 12",
|
||||
"ad": "p.48 Arambha Rasi, p.50 note 2; Tables 13 / 11 / 14 (p.54)",
|
||||
"indira_example": "p.65-66 (AK), Table 18 p.67",
|
||||
"ruling": "astrologer round 5, 2026-10-03, Q1-Q4; product owner: Ketu first",
|
||||
"ruling": "astrologer round 5, 2026-10-03, Q1-Q4; product owner: Ketu first; round 6, 2026-10-04, Q2 (node dignity)",
|
||||
"node_dignity": "Table 9 p.41-42 (Phalita exaltation Rahu Gemini / Ketu Sagittarius); debilitated weaker p.67",
|
||||
"port": "upstream yinduzhanxing c7117f92 scripts/narayana_dasha.py",
|
||||
}
|
||||
SIGNS = nd.SIGNS
|
||||
@@ -163,16 +166,44 @@ def _role_candidate(sign_idx: int, planet_lons: Dict[str, float], lord: str) ->
|
||||
}
|
||||
|
||||
|
||||
# Rahu / Ketu dignity inside Rath Narayana only (round-6 ruling Q2, Rath
|
||||
# Table 9 p.41-42): own sign Rahu Aquarius / Ketu Scorpio; moolatrikona Rahu
|
||||
# Virgo / Ketu Pisces; exaltation for Phalita dasas such as Narayana Rahu
|
||||
# Gemini / Ketu Sagittarius (the Taurus / Scorpio pair is for Ayur dasas
|
||||
# only), debilitation in the opposite signs. Not the natal dignity table
|
||||
# (BUG-1211 "nodes have no own sign" still holds on the D1 main path).
|
||||
RATH_NODE_DIGNITY = {
|
||||
"Rahu": {"exalted": {2}, "moolatrikona": 5, "own": {10}, "debilitated": {8}},
|
||||
"Ketu": {"exalted": {8}, "moolatrikona": 11, "own": {7}, "debilitated": {2}},
|
||||
}
|
||||
DEBILITATED_RANK = -1
|
||||
|
||||
|
||||
def _occupant_dignity(planet: str, sign_idx: int) -> int:
|
||||
table = nd._NARAYANA_DIGNITY.get(planet)
|
||||
if table is None: # Rahu / Ketu: no ruling on node dignity; not counted.
|
||||
return 0
|
||||
if sign_idx in table["exalted"]:
|
||||
"""Level 3 rank: exalted 3 > moolatrikona 2 > own 1 > none 0 > debilitated -1.
|
||||
|
||||
Debilitated below none: p.67 (Indira, Table 18) "Ketu is debilitated and
|
||||
as per Rule (3) of first source of strength is weaker than Mars" (Mars in
|
||||
Leo, no dignity). Applied to all nine bodies (Rule 3 is general).
|
||||
"""
|
||||
node = RATH_NODE_DIGNITY.get(planet)
|
||||
if node is not None:
|
||||
exalted, moolatrikona, own, debilitated = node["exalted"], node["moolatrikona"], node["own"], node["debilitated"]
|
||||
else:
|
||||
table = nd._NARAYANA_DIGNITY.get(planet)
|
||||
if table is None:
|
||||
return 0
|
||||
exalted, debilitated = table["exalted"], table["debilitated"]
|
||||
moolatrikona = _RATH_CLASSICAL_MOOLATRIKONA_SIGNS[planet]
|
||||
own = {idx for idx, sign in enumerate(SIGNS) if SIGN_LORDS[sign] == planet}
|
||||
if sign_idx in exalted:
|
||||
return 3
|
||||
if sign_idx == _RATH_CLASSICAL_MOOLATRIKONA_SIGNS[planet]:
|
||||
if sign_idx == moolatrikona:
|
||||
return 2
|
||||
if SIGN_LORDS[SIGNS[sign_idx]] == planet:
|
||||
if sign_idx in own:
|
||||
return 1
|
||||
if sign_idx in debilitated:
|
||||
return DEBILITATED_RANK
|
||||
return 0
|
||||
|
||||
|
||||
@@ -189,12 +220,14 @@ def sign_strength(sign_idx: int, planet_lons: Dict[str, float]) -> Dict[str, Any
|
||||
ranks = {planet: _occupant_dignity(planet, sign_idx) for planet in row["occupants"]}
|
||||
best = max(ranks.values(), default=0)
|
||||
dignified = sum(1 for value in ranks.values() if value > 0)
|
||||
debilitated = sum(1 for value in ranks.values() if value == DEBILITATED_RANK)
|
||||
lord_sign_idx = nd._get_planet_sign(lord, lons)
|
||||
if lord_sign_idx is None:
|
||||
lord_sign_idx = sign_idx
|
||||
years, _ = nd.calculate_narayana_period_years(sign_idx, lord, lord_sign_idx, profile=DURATION_PROFILE)
|
||||
row.update({
|
||||
"occupant_dignity": {"ranks": ranks, "best_rank": best, "dignified_count": dignified},
|
||||
"occupant_dignity": {"ranks": ranks, "best_rank": best, "dignified_count": dignified,
|
||||
"debilitated_count": debilitated},
|
||||
"modality": "dual" if sign_idx in (2, 5, 8, 11) else "fixed" if sign_idx in nd._FIXED_SIGNS else "movable",
|
||||
"lord_sign": SIGNS[lord_sign_idx],
|
||||
"lord_degree_in_sign": round(_ck_degree(lord, lons[lord]), 6) if lord in lons else None,
|
||||
|
||||
@@ -48,7 +48,11 @@ LEVEL_CASES = {
|
||||
"Venus": 203.56, "Saturn": 341.08, "Rahu": 227.03, "Ketu": 47.03}, "Capricorn"),
|
||||
6: (3, 9, {"Sun": 136.2, "Moon": 178.62, "Mars": 80.38, "Mercury": 202.16, "Jupiter": 260.66,
|
||||
"Venus": 355.0, "Saturn": 268.62, "Rahu": 0.8, "Ketu": 180.8}, "Capricorn"),
|
||||
7: (6, 0, {"Sun": 195.37, "Moon": 223.57, "Mars": 268.44, "Mercury": 0.02, "Jupiter": 349.15,
|
||||
# 原值:Sun 195.37(天秤)、Moon 223.57(天蝎)
|
||||
# 新值:Sun 223.57、Moon 195.37(日月互换,天秤里换成不落陷的月亮)
|
||||
# 原因:BUG-1224(第六轮问 2,p.67)第 3 级把落陷算作弱于无尊贵;太阳落陷天秤会让本例在第 3 级分出,
|
||||
# 不再演示第 7 级。互换后前六级仍全平,第 7 级照旧判白羊。
|
||||
7: (6, 0, {"Sun": 223.57, "Moon": 195.37, "Mars": 268.44, "Mercury": 0.02, "Jupiter": 349.15,
|
||||
"Venus": 328.44, "Saturn": 266.23, "Rahu": 223.1, "Ketu": 43.1}, "Aries"),
|
||||
}
|
||||
|
||||
@@ -195,3 +199,52 @@ def test_output_fields() -> None:
|
||||
ad = antardasha(result["mahadasha"][0], lons)
|
||||
assert ad["status"] == "selected" and len(ad["rows"]) == 12
|
||||
assert ad["antardasha_start_rule"]["strength_level_decided"] in range(1, 8)
|
||||
|
||||
|
||||
# --- BUG-1224: Rahu / Ketu dignity inside Rath Narayana (round-6 ruling Q2,
|
||||
# Rath Table 9 p.41-42; Phalita exaltation set) and debilitation at level 3 (p.67).
|
||||
|
||||
@pytest.mark.parametrize("planet,sign,rank", [
|
||||
("Rahu", "Gemini", 3), ("Rahu", "Virgo", 2), ("Rahu", "Aquarius", 1), ("Rahu", "Sagittarius", -1),
|
||||
("Rahu", "Taurus", 0), # Taurus / Scorpio exaltation is the Ayur-dasa set, not Phalita
|
||||
("Ketu", "Sagittarius", 3), ("Ketu", "Pisces", 2), ("Ketu", "Scorpio", 1), ("Ketu", "Gemini", -1),
|
||||
("Ketu", "Scorpio", 1), ("Ketu", "Taurus", 0),
|
||||
("Mars", "Capricorn", 3), ("Mars", "Aries", 2), ("Mars", "Scorpio", 1), ("Mars", "Cancer", -1), ("Mars", "Leo", 0),
|
||||
])
|
||||
def test_node_and_debilitation_ranks(planet: str, sign: str, rank: int) -> None:
|
||||
from scripts.narayana_rath import _occupant_dignity
|
||||
|
||||
assert _occupant_dignity(planet, SIGNS.index(sign)) == rank
|
||||
|
||||
|
||||
def test_p67_debilitated_ketu_is_weaker_than_mars_by_rule_3() -> None:
|
||||
"""Rath p.67 (Indira Gandhi, Table 18): "Scorpio: Ketu is debilitated and as
|
||||
per Rule (3) of first source of strength is weaker than Mars" — Mars alone
|
||||
in Leo, Ketu alone in Gemini. Before BUG-1224 Gemini won at level 4 (dual > fixed)."""
|
||||
from tests.test_narayana_rath_book_tables import _chart
|
||||
|
||||
_asc, lons = _chart("Table 18")
|
||||
result = compare_signs(SIGNS.index("Leo"), SIGNS.index("Gemini"), lons)
|
||||
assert (result["selected_sign"], result["strength_level_decided"]) == ("Leo", 3)
|
||||
assert result["candidates"]["Gemini"]["occupant_dignity"]["ranks"] == {"Ketu": -1}
|
||||
assert result["candidates"]["Leo"]["occupant_dignity"]["ranks"] == {"Mars": 0}
|
||||
|
||||
|
||||
def test_constructed_debilitated_ketu_loses_to_plain_mars_at_level_3() -> None:
|
||||
lons = {"Sun": 95.0, "Moon": 185.0, "Mars": 135.0, "Mercury": 105.0, "Jupiter": 100.0,
|
||||
"Venus": 155.0, "Saturn": 275.0, "Rahu": 245.0, "Ketu": 65.0} # Mars Leo, Ketu Gemini
|
||||
result = compare_signs(SIGNS.index("Leo"), SIGNS.index("Gemini"), lons)
|
||||
a, b = (result["candidates"][name]["level_values"] for name in ("Leo", "Gemini"))
|
||||
assert a[:2] == b[:2]
|
||||
assert (result["selected_sign"], result["strength_level_decided"]) == ("Leo", 3)
|
||||
|
||||
|
||||
def test_node_dignity_stays_inside_rath() -> None:
|
||||
"""The natal dignity table is untouched (BUG-1211: nodes have no own sign on the D1 path)."""
|
||||
import inspect
|
||||
import scripts.narayana_rath as rath
|
||||
|
||||
users = sorted(path.name for path in (ROOT / "scripts").glob("*.py")
|
||||
if path.name != "narayana_rath.py" and "RATH_NODE_DIGNITY" in path.read_text(encoding="utf-8"))
|
||||
assert users == []
|
||||
assert "RATH_NODE_DIGNITY" in inspect.getsource(rath._occupant_dignity)
|
||||
|
||||
Reference in New Issue
Block a user