feat(narayana): Sanjay Rath profile replayed against the book tables; not wired (BUG-1215)
Round-4 ruling Q4. scripts/narayana_rath.py builds the Rath sequence from the rule ledger and book rows ported verbatim from upstream yinduzhanxing c7117f92 (sanjay_rath_narayana_book_ledger JSON + md, chara_narayana_book_golden_cases): Table 10 order (Saturn -> Table 11, Ketu -> reversed), years by jaimini_odd_footed_dignity_v2 with jaimini_dual_lord_source_v1, ADs twelve equal parts from the MD sign (Table 13 / 11 / 14), start sign by the existing select_narayana_seed_sign, ties reported as parameter_sensitive with both sequences. Given the book's start sign, Tables 15 / 17 / 18 match 36/36 MD signs and years. The start sign does not: Table 17 picks Virgo (book Pisces), Table 18 ties (book Cancer). Task red line 2: the profile stays out of every user-visible surface and out of scoring; T5 trial not run. A test pins that no script imports it. 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
0afcc2d9a3
commit
7cb332bce6
@@ -0,0 +1,152 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Narayana Dasha, Sanjay Rath profile (`sanjay_rath`) — research only (BUG-1215).
|
||||
|
||||
Astrologer round-4 ruling 2026-10-03, Q4: the second time axis should be Sanjay
|
||||
Rath's Narayana Dasha (start from the stronger of Lagna and 7th, order by the
|
||||
start sign, dual lords by the traditional profile). The current product axis
|
||||
(`narayana_dasha.calc_narayana_mahadasha`) always starts from the Lagna and runs
|
||||
zodiacally; it is labelled `legacy_reference_only` on user-visible surfaces.
|
||||
|
||||
This module builds the Rath sequence from the rule ledger ported from upstream
|
||||
yinduzhanxing c7117f92 (`references/oracle/sanjay_rath_narayana_book_ledger_2026_08_28.json`,
|
||||
`docs/research/sanjay_rath_narayana_book_ledger_2026_08_28.md`):
|
||||
|
||||
- start sign: stronger of Lagna / 7th (`rath_md_start_stronger_lagna_seventh`),
|
||||
decided by the existing `narayana_dasha.select_narayana_seed_sign`; a tie
|
||||
(`later_narayana_sign_strength_rules_required`) gives
|
||||
`status: parameter_sensitive` and both sequences;
|
||||
- MD order: Table 10 by the start sign (`rath_md_general_order_table10`); Saturn
|
||||
in the start sign -> Table 11, zodiacal (`rath_saturn_start_exception`); Ketu
|
||||
in the start sign (no Saturn) -> Table 10 direction reversed (the reversal
|
||||
rule, `rath_ketu_reversal_exception`; the ledger tables only the AD form);
|
||||
- years: `jaimini_odd_footed_dignity_v2` (Upadesa 1.1.25-27 odd/even-footed
|
||||
count with the fixed-sign exception, exalted +1 / debilitated -1, ledger
|
||||
`upadesa_1125_1126_odd_even_count`, `upadesa_1127_fixed_sign_exception`,
|
||||
`upadesa_bphs_exalt_debil_adjustment`); Scorpio / Aquarius lords by
|
||||
`jaimini_dual_lord_source_v1` (`upadesa_dual_lord_strength_chain`, partial);
|
||||
- AD: twelve equal parts from the MD sign, Table 13 order; Saturn in the MD sign
|
||||
-> Table 11, Ketu -> Table 14 (`rath_ad_equal_twelfths`, `rath_ad_order_table13`).
|
||||
|
||||
Not wired into scoring, consultation or reports: the three book tables
|
||||
(Table 15 / 17 / 18) are not all reproduced (see
|
||||
`docs/tasks/PROGRESS-astrologer-rulings-batch2-20261003.md`, task red line 2).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
try: # scripts/ on sys.path
|
||||
import narayana_dasha as nd
|
||||
except ImportError: # pragma: no cover - package import
|
||||
from scripts import narayana_dasha as nd
|
||||
|
||||
PROFILE = "sanjay_rath"
|
||||
DURATION_PROFILE = "jaimini_odd_footed_dignity_v2"
|
||||
DUAL_LORD_PROFILE = "jaimini_dual_lord_source_v1"
|
||||
SOURCE = ("Sanjay Rath, Narayana Dasa — rule ledger and Tables 10/11/13/14 "
|
||||
"(ported from upstream yinduzhanxing c7117f92)")
|
||||
SIGNS = nd.SIGNS
|
||||
|
||||
|
||||
def md_order(seed_sign_idx: int, planet_lons: Dict[str, float]) -> Dict[str, Any]:
|
||||
"""MD sign order for one start sign, with the rule that decided it."""
|
||||
saturn = nd._get_planet_sign("Saturn", planet_lons) == seed_sign_idx
|
||||
ketu = nd._get_planet_sign("Ketu", planet_lons) == seed_sign_idx
|
||||
if saturn:
|
||||
order = nd.build_narayana_sign_sequence(seed_sign_idx, profile="rath_saturn_table11_v1")
|
||||
direction = "table11_saturn_zodiacal"
|
||||
elif ketu:
|
||||
table10 = nd.build_narayana_sign_sequence(seed_sign_idx, profile="rath_general_table10_v1")
|
||||
order = [(2 * seed_sign_idx - sign) % 12 for sign in table10]
|
||||
direction = "table10_reversed_ketu"
|
||||
else:
|
||||
order = nd.build_narayana_sign_sequence(seed_sign_idx, profile="rath_general_table10_v1")
|
||||
direction = "table10_general"
|
||||
return {"order": order, "direction": direction, "saturn_in_seed": saturn, "ketu_in_seed": ketu}
|
||||
|
||||
|
||||
def _ad_order_profile(md_sign_idx: int, planet_lons: Dict[str, float]) -> str:
|
||||
if nd._get_planet_sign("Saturn", planet_lons) == md_sign_idx:
|
||||
return "rath_saturn_table11_v1"
|
||||
if nd._get_planet_sign("Ketu", planet_lons) == md_sign_idx:
|
||||
return "rath_ketu_table14_v1"
|
||||
return "rath_table13_v1"
|
||||
|
||||
|
||||
def mahadasha_sequence(seed_sign_idx: int, planet_lons: Dict[str, float],
|
||||
*, start_age: float = 0.0, cycle_count: int = 2) -> Dict[str, Any]:
|
||||
"""Rath MD rows from one start sign (cycle 2 = twelve minus cycle 1)."""
|
||||
order = md_order(seed_sign_idx, planet_lons)
|
||||
rows: List[Dict[str, Any]] = []
|
||||
cursor = float(start_age)
|
||||
for position, sign_idx in enumerate(order["order"], start=1):
|
||||
lord = nd._resolve_narayana_lord(sign_idx, planet_lons, dual_lord_profile=DUAL_LORD_PROFILE)
|
||||
lord_sign_idx = nd._get_planet_sign(lord, planet_lons)
|
||||
if lord_sign_idx is None:
|
||||
lord_sign_idx = sign_idx
|
||||
years, adjustment = nd.calculate_narayana_period_years(sign_idx, lord, lord_sign_idx, profile=DURATION_PROFILE)
|
||||
rows.append({
|
||||
"sign": SIGNS[sign_idx], "sign_idx": sign_idx, "lord": lord,
|
||||
"lord_in_sign": SIGNS[lord_sign_idx], "lord_sign_idx": lord_sign_idx,
|
||||
"years": years, "dignity_adjustment": adjustment, "cycle_index": 1,
|
||||
"period_key": f"narayana_rath:1:{position:02d}:{SIGNS[sign_idx]}",
|
||||
"start_age": round(cursor, 4), "end_age": round(cursor + years, 4),
|
||||
})
|
||||
cursor += years
|
||||
if cycle_count == 2:
|
||||
for position, first in enumerate(list(rows), start=1):
|
||||
years = 12 - first["years"]
|
||||
rows.append({**first, "years": years, "dignity_adjustment": None, "cycle_index": 2,
|
||||
"period_key": f"narayana_rath:2:{position:02d}:{first['sign']}",
|
||||
"start_age": round(cursor, 4), "end_age": round(cursor + years, 4)})
|
||||
cursor += years
|
||||
return {**order, "seed_sign": SIGNS[seed_sign_idx], "seed_sign_idx": seed_sign_idx, "mahadasha": rows}
|
||||
|
||||
|
||||
def antardasha(md_row: Dict[str, Any], planet_lons: Dict[str, float]) -> List[Dict[str, Any]]:
|
||||
"""Twelve equal ADs from the MD sign (Table 13 / 11 / 14)."""
|
||||
profile = _ad_order_profile(md_row["sign_idx"], planet_lons)
|
||||
rows = nd.subdivide_narayana_equal_order(
|
||||
md_row, start_sign_idx=md_row["sign_idx"], order_profile=profile, level="AD",
|
||||
parent_key="parent_md", parent_name=md_row["sign"],
|
||||
)
|
||||
return rows
|
||||
|
||||
|
||||
def calc_narayana_rath(lagna_sign_idx: int, planet_lons: Dict[str, float],
|
||||
*, seed_sign_idx: Optional[int] = None, cycle_count: int = 2) -> Dict[str, Any]:
|
||||
"""Rath Narayana Dasha for one chart.
|
||||
|
||||
`seed_sign_idx` overrides the start sign (book-table replay only).
|
||||
"""
|
||||
if seed_sign_idx is not None:
|
||||
selection = {"status": "explicit", "selected_sign_idx": seed_sign_idx}
|
||||
else:
|
||||
selection = nd.select_narayana_seed_sign(lagna_sign_idx, planet_lons)
|
||||
base = {
|
||||
"profile": PROFILE,
|
||||
"duration_profile": DURATION_PROFILE,
|
||||
"dual_lord_rule": DUAL_LORD_PROFILE,
|
||||
"source": SOURCE,
|
||||
"lagna_sign": SIGNS[lagna_sign_idx],
|
||||
"seed_selection": selection,
|
||||
}
|
||||
if selection["status"] in ("selected", "explicit"):
|
||||
seq = mahadasha_sequence(selection["selected_sign_idx"], planet_lons, cycle_count=cycle_count)
|
||||
return {**base, "status": "partial_verified" if selection["status"] == "selected" else "explicit_seed",
|
||||
"seed_sign": seq["seed_sign"],
|
||||
"seed_basis": ("explicit_seed" if selection["status"] == "explicit"
|
||||
else "stronger_of_lagna_seventh:" + "+".join(selection.get("selection_levels", []))),
|
||||
"direction": seq["direction"], "mahadasha": seq["mahadasha"]}
|
||||
alternatives = {
|
||||
SIGNS[idx]: mahadasha_sequence(idx, planet_lons, cycle_count=cycle_count)
|
||||
for idx in (lagna_sign_idx, (lagna_sign_idx + 6) % 12)
|
||||
}
|
||||
lagna_seq = alternatives[SIGNS[lagna_sign_idx]]
|
||||
return {**base, "status": "parameter_sensitive", "reason": selection.get("reason"),
|
||||
"seed_sign": None, "seed_basis": "tie_lagna_seventh_both_listed",
|
||||
"direction": None, "mahadasha": None,
|
||||
"alternatives": {sign: {"seed_sign": seq["seed_sign"], "direction": seq["direction"],
|
||||
"mahadasha": seq["mahadasha"]} for sign, seq in alternatives.items()},
|
||||
"lagna_start_mahadasha": lagna_seq["mahadasha"]}
|
||||
Reference in New Issue
Block a user